空間検索の高速化

空間検索の高速化

概要

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を指定しなければ“非常に“遅くなる。シェープファイルの場合は指定した時としない時の違いは見られない。&br; 例: 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


1-2 IEnumGeometryBind::BindGeometrySourceを使用して選択セットのジオメトリをひとつにまとめて、まとめられた列挙型のジオメトリからジオメトリを作成(geomtryBagができる)する方法はどちらも大差なし。ただしどちらも最初の一回目の検索は遅く、2回目の検索から同程度の速度が出る。

1-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並みの速さが得られる