概要
ArcObjectsのサンプルプログラムで提供している空間検索はArcMapと比較するとパフォーマンスが悪い。特に大量のデータ(数十万件)に対する場合だと差が歴然となる。
http://www.esrij.com/support/arcobjects/samples/Geodatabase/Accessing_Data/PerformSpatialQuery.html
ここでは空間検索をより高速に行う方法について解説する。
処理方法
できるだけ早く検索を行う方法として
ISpatialIndexを利用
これにより速度は劇的に改善される。 検索用ジオメトリを用意し、SpatialFilterへ渡す前に以下のコードを実行する。
Dim pSpatialIndex As ISpatialIndex Set pSpatialIndex = pGeometry(検索用ジオメトリ) pSpatialIndex.AllowIndexing = True pSpatialIndex.Invalidate
ISpatialFilter::GeometryFieldを指定
ジオデータベースの場合はGeometryFieldを指定しなければ“非常に“遅くなる。シェープファイルの場合は指定した時としない時の違いは見られない。
'例: pStatialFilter.GeometryField = pFeatureClass.ShapeFieldName
GeometryBagの使用
検索用ジオメトリをひとつ用意して1回で検索する方法と複数のジオメトリをひとつひとつ使って検索をして選択結果を次々に足していく方法(SelectFeaturesの第2引数をesriSelectionResultAddにして選択結果を追加していく方法)とでは、1回で検索した方が速く処理できる。&br; 検索用ジオメトリの作成の方法では(選択セットのフィーチャで検索すると仮定します)
選択セット内でカーソルを取得してカーソルをまわしてGeometryBagにジオメトリを追加する方法
Dim pGeometryCollection As IGeometryCollection Set pGeometryCollection = New GeometryBag Dim pFeatureCursor As IFeatureCursor Dim pFeatureSelection As IFeatureSelection Set pFeatureSelectionl = pFeatureLayer pFeatureSelection.SelectionSet.Search Nothing, True, pFeatureCursor '選択セット内でカーソルを取得 Dim pFeature As IFeature Set pFeature = pFeatureCursor.NextFeature Do Until pf Is Nothing pGeometryCollection.AddGeometry pFeature.ShapeCopy Set pFeature = pFeatureCursor.NextFeature 'カーソルをまわしてGeometryBagにジオメトリを追加する Loop Dim pGeometry As IGeometry Set pGeometry = pGeometryCollection
IEnumGeometryBind::BindGeometrySourceを使用して選択セットのジオメトリをひとつにまとめて、まとめられた列挙型のジオメトリからジオメトリを作成(geomtryBagができる)する方法はどちらも大差なし。ただしどちらも最初の一回目の検索は遅く、2回目の検索から同程度の速度が出る。
この方がカーソルをぐるぐる回さずにエレガントな感じがするので、私はこちらを使っていましたがパフォーマンスに違いはなさそうです。
Dim pFeatureselection As IFeatureSelection Set pFeatureselection = pFeatureLayer Dim pSelectionSet As ISelectionSet Set pSelectionSet = pFeatureselection.SelectionSet '選択セットの取得 Dim pEnumGeometry As IEnumGeometry Set pEnumGeometry = New EnumFeatureGeometry Dim pEnumGeometryBind As IEnumGeometryBind Set pEnumGeometryBind = pEnumGeometry pEnumGeometryBind.BindGeometrySource Nothing, pSelectionSet '選択セットのジオメトリをまとめる Dim pGeometryFactoryy As IGeometryFactory Set pGeometryFactory = New GeometryEnvironment Dim pGeometry As IGeometry Set pGeometry = pGeometryFactory.CreateGeometryFromEnumerator(pEnumGeom) 'まとめられた列挙型のジオメトリからジオメトリを作成(GeomtryBagができる)
サンプルコード
Sub selectByLayer() Dim pMxDocument As IMxDocument Dim pTargetLayer As IFeatureLayer Dim pSourceLayer As IFeatureLayer Dim pFeatureCursor As IFeatureCursor Dim pFeature As IFeature Dim pGeometryCollection As IGeometryCollection Dim pSpatialIndex As ISpatialIndex Dim pspatialfilter As ispatialfilter Dim pFeatureselection As IFeatureSelection Set pMxDocument = ThisDocument Set pTargetLayer = pMxDocument.FocusMap.Layer(1) 'ラインレイヤ(選択されるレイヤ) Set pSourceLayer = pMxDocument.FocusMap.Layer(0) 'ポリゴンレイヤ(検索用ジオメトリを含んだレイヤ) Set pFeatureCursor = pSourceLayer.FeatureClass.Search(Nothing, False) Set pGeometryCollection = New GeometryBag Set pFeature = pFeatureCursor.NextFeature Do While Not pFeature Is Nothing pGeometryCollection.AddGeometry pFeature.Shape Set pFeature = pFeatureCursor.NextFeature Loop Set pSpatialIndex = pGeometryCollection pSpatialIndex.AllowIndexing = True pSpatialIndex.Invalidate Set pspatialfilter = New spatialfilter With pspatialfilter Set .Geometry = pGeometryCollection .GeometryField = pTargetLayer.FeatureClass.ShapeFieldName .SpatialRel = esriSpatialRelIntersects End With Set pFeatureselection = pTargetLayer pFeatureselection.SelectFeatures pspatialfilter, esriSelectionResultNew, False pMxDocument.ActiveView.Refresh End Sub
補足
過去にISpatialIndexに関する不具合(CQ00196129)が登録されているが、ESRIに問い合わせたところ、現在ISpatialIndexに不具合はない”はず”との見解だった。
「に完全に含まれる」 ポリゴン同士だとISpatialIndexを作成しても処理時間が非常にかかる。ポリゴンからITopologicalOperator:Boundaryでラインを取得し、これをインターセクトするとArcMap並みの速さが得られる。