Як ефективно отримати доступ до функцій, повернених QgsSpatialIndex?


9

PyQGIS Cookbook пояснює , як налаштувати просторовий індекс , але це пояснює тільки половину його використання:

створити просторовий індекс - наступний код створює порожній індекс

index = QgsSpatialIndex()

додавання функцій до індексу - індекс бере об'єкт QgsFeature і додає його до внутрішньої структури даних. Ви можете створити об'єкт вручну або використовувати один з попереднього дзвінка до наступногоFeature постачальника ()

index.insertFeature(feat)

як тільки просторовий індекс заповниться деякими значеннями, ви можете виконати деякі запити

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

Що є найбільш ефективним кроком для отримання фактичних функцій, що належать поверненим ідентифікаторам функції?

Відповіді:


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 буде значно повільнішим за рішення, яке він розмістив. Ви підтверджуєте це?
underdark

Я не використовував його на великих наборах результатів, тому не можу підтвердити.
gsherman

1
Я підтверджую, і в моєму випадку rtree навіть швидше, ніж QgsSpatialIndex () (для побудови планарних графіків з дуже великих полілінійних шарів, переміщення модуля PlanarGraph з Shapely в PyQGIS. Але рішення з Fiona, Shapely і rtree все ще є найшвидший)
ген

1
Я вважаю, що питання полягає в отриманні фактичних особливостей з повернених ідентифікаторів функції, а не швидкості різних методів індексації.
gsherman

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)

Це створює словник, що дозволяє швидко шукати QgsFeature, використовуючи його FID.

Я виявив, що для дуже великих шарів це не особливо практично, оскільки вимагає багато пам'яті. Однак альтернатива (випадковий доступ до потрібної функції) за допомогою layer.getFeatures(QgsFeatureRequest().setFilterFid(fid))здається порівняно дуже повільною. Я не впевнений, чому це так, оскільки еквівалентний виклик за допомогою прив'язки SWIG OGR layer.GetFeature(fid)здається набагато швидшим, ніж це.


1
Використання словника було дуже багато швидше layer.getFeatures(QgsFeatureRequest().setFilterFid(fid)). Я працював над шаром із функціями 140k, і загальний час пошуку 140k шукав від багатьох хвилин до секунд.
Håvard Tveite

5

Для порівняння подивіться на більш ефективне просторове приєднання в Python без QGIS, ArcGIS, PostGIS тощо . Представлене рішення використовує модулі Python Fiona , Shapely та rtree (просторовий індекс).

З PyQGIS і тим же прикладом два шари, 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-Tree 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)]]

Ті самі висновки, що і в більш ефективному просторовому приєднанні до Python без QGIS, ArcGIS, PostGIS тощо :

  • Без і вказівки ви повинні повторити всі геометрії (полігони та точки).
  • За допомогою обмежувального просторового індексу (QgsSpatialIndex ()) ви повторюєте лише геометрії, які мають шанс перетинатися з вашою поточною геометрією ("фільтр", що може заощадити значну кількість обчислень та часу ...).
  • Ви також можете використовувати інші модулі просторового індексу Python ( rtree , Pyrtree або квадрантів ) з PyQGIS як в Використання QGIS просторового індексу швидкості до коду (з QgsSpatialIndex () і rtree )
  • але просторовий індекс - це не чарівна паличка. Коли потрібно отримати дуже велику частину набору даних, просторовий індекс не може дати переваги швидкості.

Інший приклад в GIS se: Як знайти найближчу лінію до точки в QGIS? [дублікат]


Дякую за все додаткове пояснення. В основному, ваше рішення використовує список, а не диктант, як це робив Snorfalorpagus. Тож насправді, здається, немає функції layer.getFeatures ([id]) ...
underdark

Мета цього пояснення суто геометрична, і додати функцію layer.getFeatures ([ids]) дуже просто, як у більш ефективному просторовому об'єднанні в Python без QGIS, ArcGIS, PostGIS тощо
ген

0

Мабуть, єдиний метод для отримання хорошої продуктивності - це уникнути або згрупувати виклики для layer.getFeatures (), навіть якщо фільтр такий же простий, як fid.

Тепер ось ось пастка: виклик getFeatures дорогий. Якщо ви називаєте це на векторному шарі, QGIS буде потрібно для встановлення нового з'єднання з сховищем даних (постачальника шарів), створення певного запиту для повернення даних та розбору кожного результату після повернення з боку провайдера. Це може бути повільним, особливо якщо ви працюєте з деяким типом віддаленого шару, наприклад, таблицею PostGIS через VPN-з'єднання.

джерело: http://nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.