1. 程式人生 > >ArcEngine空間查詢優化

ArcEngine空間查詢優化

開發環境:VS2013 + ArcEngine 10.4
開發背景:有兩個圖層,一個點圖層,一個線圖層,現在需要查詢與線相交的點有哪些~大多數書上的方法如下:

        public void Method_A(IFeatureLayer pSourceFeatureLayer, IFeatureLayer pTargetFeatureLayer)
        {
            IQueryFilter pQueryFilter = new QueryFilter();
            pQueryFilter.AddField("Shape");

            // 源圖層
            IFeatureClass pSourceFeatureClass = pSourceFeatureLayer.FeatureClass;
            IFeatureCursor pSourceFeatureCursor = pSourceFeatureClass.Search(pQueryFilter, true);
            IFeature pSourceFeature = pSourceFeatureCursor.NextFeature();
            if (pSourceFeature == null)
            {
                return;
            }

            // 目標圖層
            IFeatureSelection pTargetFeatureSelection = pTargetFeatureLayer as IFeatureSelection;
            ISpatialFilter pSpatialFilter = new SpatialFilter();
            while (pSourceFeature != null)
            {
                pSpatialFilter.Geometry = pSourceFeature.ShapeCopy;
                pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
                pTargetFeatureSelection.SelectFeatures(pSpatialFilter, esriSelectionResultEnum.esriSelectionResultAdd, false);
                pSourceFeature = pSourceFeatureCursor.NextFeature();
            }
            Marshal.ReleaseComObject(pSourceFeatureCursor);

            // 重新整理檢視
            axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);
        }

20毫秒!!!該方法的缺點:一旦資料量較大時,遍歷次數較多,時間效率就會較低。我們可以回想一下,傳統關係型資料庫怎麼優化查詢?除了優化SQL,建立索引也是一個較好的方法,在ArcEngne中也同樣可以建立空間索引,程式碼如下:

        public void Method_B(IFeatureLayer pSourceFeatureLayer, IFeatureLayer pTargetFeatureLayer)
        {
            IFeatureClass pSourceFeatureClass = pSourceFeatureLayer.FeatureClass;
            IGeoDataset pGeoDataset = pSourceFeatureClass as IGeoDataset;
            ISpatialReference pSpatialReference = pGeoDataset.SpatialReference;

            // 實體幾何
            IGeometryBag pGeometryBag = new GeometryBag() as IGeometryBag;
            pGeometryBag.SpatialReference = pSpatialReference;
            IGeometryCollection pGeometryCollection = pGeometryBag as IGeometryCollection;

            // 要素遊標
            IFeatureCursor pSourceFeatureCursor = pSourceFeatureClass.Search(null, true);
            IFeature pSourceFeature = pSourceFeatureCursor.NextFeature();
            if (pSourceFeature == null)
            {
                return;
            }

            // 新增實體
            object missing = Type.Missing;
            while (pSourceFeature != null)
            {
                pGeometryCollection.AddGeometry(pSourceFeature.ShapeCopy, ref missing, ref missing);
                pSourceFeature = pSourceFeatureCursor.NextFeature();
            }
            Marshal.ReleaseComObject(pSourceFeatureCursor);

            // 建立空間索引
            ISpatialIndex pSpatialIndex = pGeometryBag as ISpatialIndex;
            pSpatialIndex.AllowIndexing = true;
            pSpatialIndex.Invalidate();

            // 建立空間過濾器
            ISpatialFilter pSpatialFilter = new SpatialFilter();
            pSpatialFilter.Geometry = pGeometryBag;
            pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;

            // 重新整理檢視
            IFeatureSelection pTargetFeatureSelection = pTargetFeatureLayer as IFeatureSelection;
            pTargetFeatureSelection.SelectFeatures(pSpatialFilter, esriSelectionResultEnum.esriSelectionResultAdd, false);
            axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);
        }

15毫秒!!!這種方法已經能夠滿足大部分需求,但還有沒有更快的方法?ArcEngine有一個IQueryByLayer介面,這個介面很有意思,首先貼上程式碼:

        public void Method_C(IFeatureLayer pSourceFeatureLayer, IFeatureLayer pTargetFeatureLayer)
        {
            IQueryByLayer pQueryByLayer = new QueryByLayer();
            pQueryByLayer.FromLayer = pTargetFeatureLayer;
            pQueryByLayer.ByLayer = pSourceFeatureLayer;
            pQueryByLayer.LayerSelectionMethod = esriLayerSelectionMethod.esriLayerSelectIntersect;
            pQueryByLayer.UseSelectedFeatures = false;

            // 重新整理檢視
            IFeatureSelection pFeatureSelection = pTargetFeatureLayer as IFeatureSelection;
            ISelectionSet pSelectionSet = pQueryByLayer.Select();
            pFeatureSelection.SelectionSet = pSelectionSet;
            axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);
        }

13毫秒!!!對於圖層與圖層之間的查詢,這個方法速度最快~而且大家可以看一下IQueryByLayer的成員:

        // 摘要: 
        //     The type of selection method to be performed.
        [DispId(1610678275)]
        double BufferDistance { set; }
        //
        // 摘要: 
        //     The buffer units.
        [DispId(1610678276)]
        esriUnits BufferUnits { set; }
        //
        // 摘要: 
        //     The layer features will be selected from.
        [DispId(1610678273)]
        IFeatureLayer ByLayer { set; }
        //
        // 摘要: 
        //     Provides access to the methods and properties of QueryByLayer.
        [DispId(1610678272)]
        IFeatureLayer FromLayer { set; }
        //
        // 摘要: 
        //     The input layer that contains features to base the selection on.
        [DispId(1610678274)]
        esriLayerSelectionMethod LayerSelectionMethod { set; }
        //
        // 摘要: 
        //     The result type of the selection where it can be specified that the selection
        //     adds to a current selection etc.
        [DispId(1610678278)]
        esriSelectionResultEnum ResultType { set; }
        //
        // 摘要: 
        //     Indicates whether selected features will be used.
        [DispId(1610678277)]
        bool UseSelectedFeatures { set; }

        // 摘要: 
        //     Selects the features based on the input parameters and returns a selection
        //     set.
        ISelectionSet Select();

我們再來看一下ArcMap中的空間查詢介面,如下圖:
在這裡插入圖片描述
我們發現,IQueryByLayer介面中的BuferDistance對應“應用搜索距離”,BufferUnits對應搜尋距離的單位,由於ArcGIS Desktop和ArcEngine都是基於ArcObjects,所以Desktop中的空間查詢也是基於IQueryByLayer介面。