QgsSpatialIndexによって返される機能に効率的にアクセスするにはどうすればよいですか?


9

PyQGISクックブックは、空間インデックスを設定する方法について説明しますが、それは唯一のその用法の半分を説明します。

空間インデックスの作成—次のコードは空のインデックスを作成します

index = QgsSpatialIndex()

インデックスに機能を追加—インデックスはQgsFeatureオブジェクトを受け取り、それを内部データ構造に追加します。オブジェクトを手動で作成するか、プロバイダーのnextFeature()への以前の呼び出しからのオブジェクトを使用できます

index.insertFeature(feat)

空間インデックスにいくつかの値が入力されると、いくつかのクエリを実行できます

# returns array of feature IDs of five nearest features
nearest = index.nearestNeighbor(QgsPoint(25.4, 12.7), 5)

返された機能IDに属する実際の機能を取得する最も効率的な手順は何ですか?

回答:


12
    # assume a list of feature ids returned from index and a QgsVectorLayer 'lyr'
    fids = [1, 2, 4]
    request = QgsFeatureRequest()
    request.setFilterFids(fids)

    features = lyr.getFeatures(request)
    # can now iterate and do fun stuff:
    for feature in features:
        print feature.id(), feature

    1 <qgis._core.QgsFeature object at 0x000000000E987510>
    2 <qgis._core.QgsFeature object at 0x000000000E987400>
    4 <qgis._core.QgsFeature object at 0x000000000E987510>

ありがとう!Snorfalorpagusさんは、setFilterFidsは彼が投稿したソリューションよりもかなり遅いと述べました。これを確認しますか?
アンダーダーク

大きな結果セットでは使用していないため、確認できません。
gsherman、2015年

1
私の場合、rtreeはQgsSpatialIndex()よりもさらに高速です(非常に大きなポリラインレイヤーから平面グラフ作成する場合、PyQGISでShapelyを使用したPlanarGraph モジュールを転置します。しかし、Fiona、Shapely、rtreeを使用した解決策は、最速)
遺伝子

1
問題は、さまざまなインデックス作成方法の速度ではなく、返された機能ID から実際の機能を取得することに関するものだと思います。
gsherman 2015年

7

、件名にブログの記事、ネイサンウッドローは、次のコードを提供します。

layer = qgis.utils.iface.activeLayer()

# Select all features along with their attributes
allAttrs = layer.pendingAllAttributesList()
layer.select(allAttrs)
# Get all the features to start
allfeatures = {feature.id(): feature for (feature) in layer}

def noindex():
    for feature in allfeatures.values():
        for f in allfeatures.values():
            touches = f.geometry().touches(feature.geometry())
            # It doesn't matter if we don't return anything it's just an example

def withindex():
    # Build the spatial index for faster lookup.
    index = QgsSpatialIndex()
    map(index.insertFeature, allfeatures.values())

    # Loop each feature in the layer again and get only the features that are going to touch.
    for feature in allfeatures.values():
        ids = index.intersects(feature.geometry().boundingBox())
        for id in ids:
            f = allfeatures[id]
            touches = f.geometry().touches(feature.geometry())
            # It doesn't matter if we don't return anything it's just an example

import timeit
print "With Index: %s seconds " % timeit.timeit(withindex,number=1)
print "Without Index: %s seconds " % timeit.timeit(noindex,number=1)

これにより、FIDを使用してQgsFeatureをすばやく検索できる辞書が作成されます。

非常に大きなレイヤーの場合、大量のメモリを必要とするため、これは特に実用的ではないことがわかりました。ただし、代替(目的の機能へのランダムアクセス)を使用layer.getFeatures(QgsFeatureRequest().setFilterFid(fid))すると、比較的非常に遅くなります。SWIG OGRバインディングを使用した同等の呼び出しlayer.GetFeature(fid)はこれよりもはるかに高速に見えるため、これがなぜかはわかりません。


1
辞書を使用していた非常にはるかに速くよりlayer.getFeatures(QgsFeatureRequest().setFilterFid(fid))。私は140k個の機能を持つレイヤーで作業しており、140k個のルックアップの合計時間は数分から数秒になりました。
ハーバード・トバイト

5

比較については、QGIS、ArcGIS、PostGISなどを使用しないPythonでのより効率的な空間結合ご覧ください。提示されたソリューションは、PythonモジュールFionaShapely、および rtree(空間インデックス)を使用しています。

PyQGISと同じ例で2つのレイヤー、pointおよびpolygon

ここに画像の説明を入力してください

1)空間インデックスなし:

polygons = [feature for feature in polygon.getFeatures()]
points = [feature for feature in point.getFeatures()]
for pt in points: 
    point = pt.geometry()
    for pl  in polygons:
        poly = pl.geometry()
        if poly.contains(point):
            print point.asPoint(), poly.asPolygon()
(184127,122472) [[(183372,123361), (184078,123130), (184516,122631),   (184516,122265), (183676,122144), (183067,122570), (183128,123105), (183372,123361)]]
(183457,122850) [[(183372,123361), (184078,123130), (184516,122631), (184516,122265), (183676,122144), (183067,122570), (183128,123105), (183372,123361)]]
(184723,124043) [[(184200,124737), (185368,124372), (185466,124055), (185515,123714), (184955,123580), (184675,123471), (184139,123787), (184200,124737)]]
(182179,124067) [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]

2)Rツリー PyQGIS空間インデックス:

# build the spatial index with all the polygons and not only a bounding box
index = QgsSpatialIndex()
for poly in polygons:
     index.insertFeature(poly)

# intersections with the index 
# indices of the index for the intersections
for pt in points:
    point = pt.geometry()
    for id in index.intersects(point.boundingBox()):
    print id
0
0
1
2

これらのインデックスはどういう意味ですか?

for i, pt in enumerate(points):
     point = pt.geometry()
     for id in index.intersects(point.boundingBox()):
        print "Point ", i, points[i].geometry().asPoint(), "is in Polygon ", id, polygons[id].geometry().asPolygon()
Point  1 (184127,122472) is in Polygon  0 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]
Point  2 (183457,122850) is in Polygon  0 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]
Point  4 (184723,124043) is in Polygon  1 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]
Point  6 (182179,124067) is in Polygon  2 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]

QGIS、ArcGIS、PostGISなどを使用しないPythonでのより効率的な空間結合と同じ結論:

  • インデックスなしでは、すべてのジオメトリ(ポリゴンとポイント)を反復処理する必要があります。
  • 境界空間インデックス(QgsSpatialIndex())を使用すると、現在のジオメトリと交差する可能性のあるジオメトリ(かなりの計算と時間を節約できる「フィルター」)だけを反復処理します。
  • また、他の空間インデックスPythonモジュール(使用することができますRTREEPyrtreeまたは四分木を同様にPyQGISで)あなたのコードのスピードアップにQGIS空間インデックスを使用して(QgsSpatialIndex(と)とRTREE
  • しかし、空間インデックスは魔法の杖ではありません。データセットの非常に大きな部分を取得する必要がある場合、空間インデックスは速度の利点を提供できません。

GIS seの他の例:QGISでポイントに最も近いラインを見つける方法は?[重複]


追加の説明をすべてありがとう。基本的に、ソリューションはSnorfalorpagusが行ったようなdictではなくリストを使用します。だから、そこは本当に何layer.getFeatures([IDS])関数...もないように思われる
アンダーダーク

この説明の目的は純粋に幾何学的であり、layer.getFeatures([ids])関数を追加するのは非常に簡単です。たとえば、QGIS、ArcGIS、PostGISなどを使用しないPython
gene

0

フィルタがfidのように単純であっても、優れたパフォーマンスを得る唯一の方法は、layer.getFeatures()の呼び出しを回避またはバンドルすることです。

さて、ここに罠があります:getFeaturesの呼び出しは高価です。ベクターレイヤーで呼び出す場合、QGISはデータストア(レイヤープロバイダー)への新しい接続をセットアップし、データを返すクエリを作成し、プロバイダーから返される各結果を解析する必要があります。これは、VPN接続を介したPostGISテーブルなど、特定のタイプのリモートレイヤーで作業している場合は特に、遅くなる可能性があります。

ソース:http : //nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.