Fellow Travellers

ArcEngine开发中一些提高效率技巧

胡飞玲
字数统计: 2.1k阅读时长: 8 min
2018/11/12 Share

ArcEngine开发中一些提高效率技巧

一 . 查询篇

1. AE的三种查询方式概述

在使用ArcEngine查询空间数据库的时候主要使用三种查询接口:

  • IQueryFilter
  • ISpatialFilter
  • IQueryDef

三种接口使用场景总结如下表:

要求 IQueryFilter ISpatialFilter IQueryDef
属性约束 True True True
空间约束 False True False
查询结果包括多个表的字段 False False True
查询结果返回一个游标 True True True
对象可以从结果中创建 True True True
返回的记录可以被编辑 True True False
Records include edits made in active edit session True True False

从上表可看出:

  • IQueryFilter 实现属性条件查询

  • ISpatialFilter 实现 属性+空间条件查询

  • IQueryDef 可实现属性查询,和多表查询,不能实现空间查询

2. IQueryFilter 接口参数设置技巧

  • SubFields

    只设置需要查询的字段,这样比不设置和设置*查询速度会快很多。如果不设置这个属性,则会返回全部字段

    注意:如果需要读取图形geometry,SubFields需要加 shape字段 ,否则报错;编辑数据时,一般不设置

  • WhereClause

    优先使用大于、小于,其次才是不等于

    索引的使用注意事项:

    索引失效:值如 Area/30< 1000 换成Area<1000/30

    索引失效:使用不等于运算符(<>、!=尽量不要计算字段,而去计算查询)

  • Recycling 参数 【 IFeatureClass.Search(filter, Recycling ) 】

    True :循环使用对象,查询速度快,只存储一份对象 ,一般读操作时设置

    False :不循环使用对象,查询速度慢,存储多份对象,一般写操作时设置

    注意:如果设置为True,跳出循环以后,游标获取Feature不可以使用,

  • 实例:

    1
    2
    3
    4
    5
    IQueryFilter pQueryFilter = new QueryFilterClass(); 
    string SubFields = "OID,Shape";
    pQueryFilter.SubFields = SubFields;
    ipFeatCursor = ipBufferFeaCls.Search(pQueryFilter, true);
    ipFeature = ipFeatCursor.NextFeature();

3. ISpatialFilter 接口

  • Geometry
    如果使用几何包对象作为过滤的几何图形,在为ISpatialFilter对象设置Geometry属性之前,先对几何包对象创建空间索引,这样能大大提高查询的效率,下面是创建空间索引的示例代码

    1
    2
    3
    4
    // 将几何包对象转换到ISpatialIndex对象上去
    ISpatialIndex spatialIndex = (ISpatialIndex)geometryBag;
    spatialIndex.AllowIndexing = true;
    spatialIndex.Invalidate();
  • SubFields (同上)

  • WhereClause (同上)

  • Recycling 参数 (同上)

4. IQueryDef2 接口

IQueryDef2对象可由IFeatureWorkspace.CreateQueryDef 方法创建,一般这个速度最快,类似构建sql语句查表。该接口可同时可实现 distinct,group by,order by 等功能

  • 注意事项:

    1) 只能在ArcSDE、PGDB、FGDB数据源上使用(注意:shp数据不支持),数据集的历史表示 不被QueryDef游标支持(翻译可能不太准确,详见官方文档)。

    2) 支持多表查询,但是查询的结果不能进行修改!

    3) 使用IQueryDef查询出来的游标,用此游标获取的字段的别名和字段名一致(如果是要获取字段的中文别名,请使用其他方法获取游标)

  • 参数说明

    • Tables

      查询的表名称,支持多表查询,用逗号隔开即可,如”TableUser,TableRole”**
      注意事项:如果使用的是SDE数据源,当当前的工作空间不是表所在的用户空间下,需要在表的前边加用户名前缀(如:Owner.MyTable)

    • SubFields

      查询的字段列表,默认不填为查询所有字段,即”*”,支持Distinct关键字,
      格式:

      1) “*” 返回所有字段

      2)”field1,field2,field3” 用逗号隔开要查询的字段

      3)”table1.*,table2.field1,table3.field2” 查询table1的所有字段,table2的字段1,和table3的字段2

    • WhereClause

      这块注意不同数据源,查询的SQL写法不同

    • PrefixClause

      前缀查询条件,在Select 和 Select Column List之间,如Distinct关键字和ALL关键字

    • PrefixClause

      后缀查询条件,在Select语句后,紧跟Where语句之后,如Order By

    • Evaluate

      返回ICursor对象,这块使用的过程中记得用ComReleaser管理,在使用完毕释放Com对象

  • 实例:
IQueryDef2 queryDef2 = (IQueryDef2)featureWorkspace.CreateQueryDef();
queryDef2.Tables = "Cities";
queryDef2.SubFields = "Name";
queryDef2.PostfixClause = "ORDER BY Name DESC";
(ComReleaser comReleaser = new ComReleaser())
{
    ICursor cursor = queryDef2.Evaluate2(true);
    comReleaser.ManageLifetime(cursor);
    int nameIndex = cursor.FindField("Name");
    IRow row = null;
    while ((row = cursor.NextRow()) != null)
    {
        String cityName = Convert.ToString(row.get_Value(nameIndex));
    }
}

二 . 统计篇

1. 统计两种方式

  • IQueryDef2接口

  • IDataStatistics接口

总结:

  • IQueryDef接口 无法进行空间查询统计,但是速度快

  • IDataStatistics 可进行空间查询统计,但是数据量大的时候比较慢

2. IDataStatistics接口

IDataStatistics的属性Statistics返回一个IStatisticsResult,可统计字段最大值 最小值 平均值 求和等:

获取唯一值:
IQueryFilter pQueryF = new QueryFilterClass();  
pQueryF.SubFields = "CC";  
IFeatureCursor pFeatureCursor = pCoverFC.Search(null, true);  
IDataStatistics pDStatistics = new DataStatistics();  
pDStatistics.Field = "CC";  
pDStatistics.Cursor = (ICursor)pFeatureCursor;  
IEnumerator pEnumerator = pDStatistics.UniqueValues;


统计其他:
ICursor pCursor = pFeaturelayer.Search(pQueryFilter, false) as ICursor;
IDataStatistics pData = new DataStatisticsClass();
pData.Cursor = pCursor;
pData.Field = pField.Name;
ESRI.ArcGIS.esriSystem.IStatisticsResults statisticsResults = pData.Statistics;
statisticsResults.Minimum;//最小值
statisticsResults.Maximum;//最大值
statisticsResults.Count;//数量

3. IQueryDef2接口

  • 通过IQueryDef 中 group by 达到统计的效果,但是无法空间查询。同时可用distinct 来实现统计唯一值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
IDataset pDataset = pFeatureClass as IDataset;
IQueryDef2 pDef = (pDataset.Workspace as IFeatureWorkspace).CreateQueryDef() as IQueryDef2;
pDef.Tables = pDataset.Name;
pDef.WhereClause = whereCause;
pDef.SubFields = "count(*),QDM";
pDef.PostfixClause = " group by QDM";
ICursor cursor = pDef.Evaluate2(true);
IRow row = null;
while ((row = cursor.NextRow()) != null)
{
int index = cursor.FindField("QDM");
string QDM = row.get_Value(index);
string Count = row.get_Value(0).;
}

三 . 编辑篇

1. 新增插入

  • 插入要素的两种方式

    单个创建:IFeatureClass.CreateFeature() + IFeature.Store()

    批量创建:IFeatureCursor.InsertFeature(IFeatureBuffer)

  • 空间索引

    大数据量进行数据插入的过程中,无需进行空间索引的创建,可以等批量插入完成之后,统一创建空间索引

    IFeatureClassLoad.LoadOnlyMode =true 高效插入

  • 注意事项

     插入游标是用来实现要素批量插入功能的,单个要素的编辑尽量不使用插入游标

     调用插入游标的Flush方法时要捕捉异常

     不要在循环中重复FindField查找字段,给字段赋值,在循环前获取字段索引(适用于所有查询,插入,更新操作)

2. 更新

  • 更新要素的两种方式

    单个更新:IFeature.Store()

    批量更新:IFeatureCursor.UpdateFeature(IFeature)

    将一批数据更新为某一相同的属性IRowBuffer

  • 注意事项

    –使用Update Cursor更新注意其位置有效性,只对游标当前位置的
    IFeature有效

3. 删除

  • 删除要素的两种方式

    IFeatureCursor 查询或IFeatureClass.GetFeature(OID) ,IFeature.Delete()(最慢,适合单个删除)

    更新游标删除IFeatureCursor.DeleteFeature()(速度第三)

    ITable.DeleteSearchedRows(IQueryFilter)删除 (速度第二)

     IDataset.Workspace.ExecuteSQL(速度第一)

     删除要素中所有要素删除(ITableWrite. Truncate)

  • 注意事项

     DeleteSearchedRows和ExecuteSQL属于批量删除,性能较优,但是整个过程不好 加进度条,界面不够友好

     IFeatureCursor.DeleteFeature()虽次于前两者,可加界面进度条,用户体验会比较好

    只针对非版本数据,且只有当前用户在用

四 . 内存释放篇

1. 需要释放的对象

  • ICursor对象务必及时释放。在每次获取查询统计时获取游标后必须释放
  • IEnumXX接口。
  • GetXX方法获取的对象,比如IFeatureClass的GetFeature方法
  • IPointCollection的GetPoint方法
  • IWorkspace
  • 打开单个文件数据库后的方法
  • Mxd文档打开后的关闭

2. 尽可能使用Esri提供的MangeLifetime来管理ArcGIS Engine对象的生命周期

using(ComReleaser comReleaser = new ComReleaser())
{
    //读取查询结果
    IFeatureCursor featureCursor = featureClass.Search(spatialFilter, true);
    comReleaser.ManageLifetime(featureCursor);
    IFeature feature = null;
    while ((feature = featureCursor.NextFeature()) != null)
    {
        String roadName = Convert.ToString(feature.get_Value(nameIndex));
        String roadType = Convert.ToString(feature.get_Value(typeIndex));
        Console.WriteLine("Name: {0}, Type: {1}", roadName, roadType);
        //这块强烈建议feature释放掉,如果做大数据操作的时候这块不释放 内存会撑爆的。
        System.Runtime.InteropServices.Marshal.ReleaseComObject(feature);
    }
}

五 . 多线程篇

1.多线程使用注意事项

ArcGIS Engine支持多线程,但是不支持多个线程之间的彼此调用

• AO对象不能在线程中间共享,比如必须在各自的线程中重新打开Workspace、                 FeatureClass。
•主线程向工作线程传递AO对象,可以将对象序列化成字符串,再将字符串传递    给目标线程,然后再反序列化还原到对象(待验证)
•多线程处理中涉及GP工具,保证操作数据未被使用或占用,一般不能成功
CATALOG
  1. 1. ArcEngine开发中一些提高效率技巧
    1. 1.1. 一 . 查询篇
      1. 1.1.1. 1. AE的三种查询方式概述
      2. 1.1.2. 2. IQueryFilter 接口参数设置技巧
      3. 1.1.3. 3. ISpatialFilter 接口
      4. 1.1.4. 4. IQueryDef2 接口
    2. 1.2. 二 . 统计篇
      1. 1.2.1. 1. 统计两种方式
      2. 1.2.2. 2. IDataStatistics接口
      3. 1.2.3. 3. IQueryDef2接口
    3. 1.3. 三 . 编辑篇
      1. 1.3.1. 1. 新增插入
      2. 1.3.2. 2. 更新
      3. 1.3.3. 3. 删除
    4. 1.4. 四 . 内存释放篇
      1. 1.4.1. 1. 需要释放的对象
      2. 1.4.2. 2. 尽可能使用Esri提供的MangeLifetime来管理ArcGIS Engine对象的生命周期
    5. 1.5. 五 . 多线程篇
      1. 1.5.1. 1.多线程使用注意事项