注:文章第四部分搬运自ArcEngine空间查询优化;版权归原作者所有,特此声明
1 查询接口函数解析
1.1 IFeatureClass.Search()
1.1.1 函数说明
[C#]public IFeatureCursor Search ( IQueryFilterfilter, boolRecycling);
Search将返回一个满足IQueryFilter 设定的属性查询或空间查询条件的IFeatureCursor 对象;如果该参数为null,则返回要素类中的所有要素。参数recycling 控制行对象的分配行为,循环光标在提取单个要素目标都会重新进行初始化,并采用只读优化;在多次调用游标的NextFeature时,保持对要素的引用是非法的;通过回收游标返回的要素是不可修改的,不可回收游标在每个对象提取时,返回的是单独的对象,可以被多种行为的修改和存储。
The recycling parameter controls row object allocation behavior. Recycling cursors rehydrate a single feature object on each fetch and can be used to optimize read-only access, for example, when drawing. It is illegal to maintain a reference on a feature object returned by a recycling cursor across multiple calls to NextFeature on the cursor. Features returned by a recycling cursor should not be modified. Non-recycling cursors return a separate feature object on each fetch. The features returned by a non-recycling cursor may be modified and stored with polymorphic behavior.
The Geodatabase guarantees "unique instance semantics" on non-recycling feature objects fetched during an edit session. In other words, if the feature retrieved by a search cursor has already been instantiated and is being referenced by the calling application, then a reference to the existing feature object is returned.
Non-recycling feature cursors returned from the Search method *MUST* be used when copying features from the cursor into an insert cursor of another class. This is because a recycling cursor reuses the same geometry and under some circumstances all of the features inserted into the insert cursor may have the same geometry. Using a non-recycling cursor ensures that each geometry is unique.
1.1.2 依据点坐标查找图层中的第一个要素
The code in this document requires the following References added to the Visual Studio project:
ESRI.ArcGIS.Carto
ESRI.ArcGIS.Geodatabase
ESRI.ArcGIS.Geometry
ESRI.ArcGIS.System
///<summary>Finds the first feature in a GeoFeature layer by supplying an point. The point could come from a mouse click in the map.</summary> /// ///<param name="searchTolerance">A System.Double that is the number of map units to search. Example: 25</param> ///<param name="point">An IPoint interface in map units where the user clicked on the map</param> ///<param name="geoFeatureLayer">An ILayer interface to search upon</param> ///<param name="activeView">An IActiveView interface</param> /// ///<returns>An IFeature interface that is the first feature found in the GeoFeatureLayer.</returns> /// ///<remarks></remarks> public ESRI.ArcGIS.Geodatabase.IFeature GetFirstFeatureFromPointSearchInGeoFeatureLayer( System.Double searchTolerance, ESRI.ArcGIS.Geometry.IPoint point, ESRI.ArcGIS.Carto.IGeoFeatureLayer geoFeatureLayer, ESRI.ArcGIS.Carto.IActiveView activeView) { if (searchTolerance < 0 || point == null || geoFeatureLayer == null || activeView == null) { return null; } ESRI.ArcGIS.Carto.IMap map = activeView.FocusMap; // Expand the points envelope to give better search results ESRI.ArcGIS.Geometry.IEnvelope envelope = point.Envelope; envelope.Expand(searchTolerance, searchTolerance, false); ESRI.ArcGIS.Geodatabase.IFeatureClass featureClass = geoFeatureLayer.FeatureClass; System.String shapeFieldName = featureClass.ShapeFieldName; // Create a new spatial filter and use the new envelope as the geometry ESRI.ArcGIS.Geodatabase.ISpatialFilter spatialFilter = new ESRI.ArcGIS.Geodatabase.SpatialFilterClass(); spatialFilter.Geometry = envelope; spatialFilter.SpatialRel = ESRI.ArcGIS.Geodatabase.esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects; spatialFilter.set_OutputSpatialReference(shapeFieldName, map.SpatialReference); spatialFilter.GeometryField = shapeFieldName; // Do the search ESRI.ArcGIS.Geodatabase.IFeatureCursor featureCursor = featureClass.Search(spatialFilter, false); // Get the first feature ESRI.ArcGIS.Geodatabase.IFeature feature = featureCursor.NextFeature(); if (!(feature == null)) { return feature; } else { return null; } }
1.2 IFeatureClass.Update()
1.2.1 函数说明
[C#]public IFeatureCursor Update ( IQueryFilterfilter, boolRecycling);
配合更新游标使用,通过过滤器filter进行属性或空间查询,如果选择一组特定的查询要素对其进行更新,则使用更新游标会比逐个要素更新更有效率,其更新是通过当前游标的位置执行。更新游标可作为带有多态行为(polymorphic behavior)要素子类的实例。更新游标即可在编辑会话一起使用,也可以单独使用;当在编辑会话内部使用更新游标时,这些变化知道编辑会话被保存才会提交到基础表中;网络要素类、拓扑要素类、参与组合关系或其他信息关系的要素类,只能通过编辑会话更新。如果你尝试在编辑会话之外使用更新游标,它将会失败;除此之外,编辑参与拓扑或几何网络的的要素必须要在编辑会话内。当在编辑会话中使用游标时,必须将其作用域设置为编辑操作;也就是说,游标可在编辑操作开始之后创建,且在编辑操作停止或中止之后不应再被使用。
Update opens an update cursor on the features specified by an attribute and/or spatial query as specified by the filter parameter. If a number of features selected by a particular query are to be updated and each feature is to be updated to a separate value then the update cursor is faster than doing an individual feature level update for each feature. The update is performed on the current 'cursor position'.Update cursors can be used on instances of Feature subclasses (such as network features), with guaranteed polymorphic behavior. Update cursors can be used either inside or outside of an edit session. If used inside an edit session, the changes are not committed to the base table until the edit session is saved. Network feature classes, Topology feature classes, feature classes that participate in composite relationships or other relationships with messaging may only be updated within an edit session. If you attempt to use an update cursor on one of these classes outside of an edit session, it will fail. In addition, edits to features that participate in a Topology or Geometric Network must be bracketed within an edit operation.When using cursors within an edit session, they should always be scoped to edit operations. In other words, a cursor should be created after an edit operation has begun and should not be used once that edit operation has been stopped or aborted.
1.2.2 游标要素更新
public void UseUpdateCursor(IFeatureClass featureClass) { // Restrict the number of features to be updated. IQueryFilter queryFilter = new QueryFilterClass(); queryFilter.WhereClause = "NAME = 'Highway 104'"; queryFilter.SubFields = "TYPE"; // Use IFeatureClass.Update to populate IFeatureCursor. IFeatureCursor updateCursor = featureClass.Update(queryFilter, false); int typeFieldIndex = featureClass.FindField("TYPE"); IFeature feature = null; try { while ((feature = updateCursor.NextFeature()) != null) { feature.set_Value(typeFieldIndex, "Toll Highway"); updateCursor.UpdateFeature(feature); } } catch (COMException comExc) { // Handle any errors that might occur on NextFeature(). } // If the cursor is no longer needed, release it. Marshal.ReleaseComObject(updateCursor); }
1.3 IFeatureClass.Insert()
1.3.1 函数说明
[C#]public IFeatureCursor Insert (bool useBuffering);
插入要素
1.3.2 示例
示例1:使用插入游标插入要素
public static void InsertFeaturesUsingCursor(IFeatureClass featureClass, List < IGeometry > geometryList) { using(ComReleaser comReleaser = new ComReleaser()) { // Create a feature buffer. IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer(); comReleaser.ManageLifetime(featureBuffer); // Create an insert cursor. IFeatureCursor insertCursor = featureClass.Insert(true); comReleaser.ManageLifetime(insertCursor); // All of the features to be created are classified as Primary Highways. int typeFieldIndex = featureClass.FindField("TYPE"); featureBuffer.set_Value(typeFieldIndex, "Primary Highway"); foreach (IGeometry geometry in geometryList) { // Set the feature buffer's shape and insert it. featureBuffer.Shape = geometry; insertCursor.InsertFeature(featureBuffer); } // Flush the buffer to the geodatabase. insertCursor.Flush(); } }
示例2:使用仅加载模式插入要素
public static void LoadOnlyModeInsert(IFeatureClass featureClass, List < IGeometry > geometryList) { // Cast the feature class to the IFeatureClassLoad interface. IFeatureClassLoad featureClassLoad = (IFeatureClassLoad)featureClass; // Acquire an exclusive schema lock for the class. ISchemaLock schemaLock = (ISchemaLock)featureClass; try { schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock); // Enable load-only mode on the feature class. featureClassLoad.LoadOnlyMode = true; using(ComReleaser comReleaser = new ComReleaser()) { // Create the feature buffer. IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer(); comReleaser.ManageLifetime(featureBuffer); // Create an insert cursor. IFeatureCursor insertCursor = featureClass.Insert(true); comReleaser.ManageLifetime(insertCursor); // All of the features to be created are classified as Primary Highways. int typeFieldIndex = featureClass.FindField("TYPE"); featureBuffer.set_Value(typeFieldIndex, "Primary Highway"); foreach (IGeometry geometry in geometryList) { // Set the feature buffer's shape and insert it. featureBuffer.Shape = geometry; insertCursor.InsertFeature(featureBuffer); } // Flush the buffer to the geodatabase. insertCursor.Flush(); } } catch (Exception) { // Handle the failure in a way appropriate to the application. } finally { // Disable load-only mode on the feature class. featureClassLoad.LoadOnlyMode = false; // Demote the exclusive schema lock to a shared lock. schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock); } }
2 资源的释放
在进行数据查询时,如果不对游标进行释放,查询效率会越来越慢
2.1 使用Marsh.ReleaseComObject进行查询游标的释放
private void find_allroads_lxbm(IFeature feature_ld, string lxbm) { try { ISpatialFilter spatialfilter = new SpatialFilterClass(); spatialfilter.WhereClause = "[LXBM] = " + lxbm; spatialfilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; //feature_ld该路段 feature_last下一路段 while (feature_ld != null) { IPolyline polyline = feature_ld.Shape as IPolyline; //缓冲 IPoint end_pt = polyline.ToPoint; ITopologicalOperator topo2 = end_pt as ITopologicalOperator; IGeometry geo_buffer_end = topo2.Buffer(1); spatialfilter.Geometry = geo_buffer_end; IFeatureCursor featcursor_lastld = m_featcls.Search(spatialfilter, true); IFeature feature_last = featcursor_lastld.NextFeature(); try { while (feature_last != null) { /****省略***/ } } catch (Exception exx) { MessageBox.Show(exx.ToString()); } //下一个 feature_ld = feature_last; System.Runtime.InteropServices.Marshal.ReleaseComObject(featcursor_lastld); } System.Runtime.InteropServices.Marshal.ReleaseComObject(spatialfilter); 垃圾回收 System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } catch (Exception exx) { MessageBox.Show(exx.ToString()); } }
2.2 分析
情况一:在使用完游标之后,将其赋值为null;这时候对象实际上是没有被释放掉的,因此 可能会报 '超出打开游标最大数’;
情况二:在使用完游标时,在循环末尾,用Marshal.ReleaseComObject方法来释放com对象,同时在使用Marshal.ReleaseComObject 方法时,并没有增加多余的时间,循环执行时间上还是跟不释放对象一样(小数据量比较)。
结论:
在使用ArcEngine中的游标对象时,一定要在使用完之后进行对象的释放,否则会不定时出现上面的错误;而且需要使用marshal.releasecomobject方法来进行对象的释放,赋值为null是达不到目的的
Search()版本
IFeatureCursor pFeatureCursor = featureClass.Search(null, true); IFeature pFeature = pFeatureCursor.NextFeature(); while(pFeature != null) { //自已的一通操作 pFeature.Store();//保存 pFeature = pFeatureCursor.NextFeature(); } Marshal.ReleaseComObject(pFeatureCursor); pFeatureCursor = null; GC.Collect();
Update()版本
IFeatureCursor pFeatureCursor = featureClass.Update(null, true); IFeature pFeature = pFeatureCursor.NextFeature(); while(pFeature != null) { //自已的一通操作 pFeature.UpdateFeature(pFeature );//保存 pFeature = pFeatureCursor.NextFeature(); } Marshal.ReleaseComObject(pFeatureCursor); pFeatureCursor = null; GC.Collect();
Insert()版本
IFeatureCursor pFeatureCursor = featureClass.Insert(true); IFeature pFeature = pFeatureCursor.NextFeature(); IFeatureBuffer pFeatureBuffer = featureClass.CreatFeatureBuffer(); while(pFeature != null) { //对pFeatureBuffer执行一波操作 pFeatureCursor.UpdateFeature(pFeatureBuffer);//保存 pFeature = pFeatureCursor.NextFeature(); } pFeatureCursor.Flush(); Marshal.ReleaseComObject(pFeatureCursor); pFeatureCursor = null; GC.Collect();
建议就是尽量避免对数据执行大量的循环查询操作,如果能采用内存优化的,就直接使用内存,这样可以快速提升程序的运行效率;但是对于,海量数据,迫不得已必须进行大量的循环操作时,请注意及时释放游标,释放内存资源,以确保程序性能。