Как эффективно получить доступ к функциям, возвращаемым QgsSpatialIndex?

9

PyQGIS Cookbook объясняет , как настроить пространственный индекс , но это объясняет только половину его использования:

создать пространственный индекс - следующий код создает пустой индекс

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)

Какой самый эффективный шаг для получения реальных функций, принадлежащих возвращенным идентификаторам функций?

Подземье
источник

Ответы:

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>
gsherman
источник
Спасибо! Snorfalorpagus отметил, что setFilterFids будет значительно медленнее, чем решение, которое он опубликовал. Вы подтверждаете это?
Подземье
Я не использовал его на больших наборах результатов, поэтому не могу подтвердить.
Гшерман
1
Я подтверждаю, и, в моем случае, rtree даже быстрее, чем QgsSpatialIndex () (для построения планарных графов из очень больших слоев полилиний, транспонирование модуля PlanarGraph с Shapely в PyQGIS. Но решение с Fiona, Shapely и rtree по-прежнему самый быстрый)
ген
1
Я полагаю, что вопрос заключается в получении фактических функций из возвращенных идентификаторов, а не в скорости различных методов индексации.
Гшерман
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)кажется намного быстрее, чем этот.

Snorfalorpagus
источник
1
Использование словаря было очень много быстрее layer.getFeatures(QgsFeatureRequest().setFilterFid(fid)). Я работал над слоем с 140k-объектами, и общее время поиска 140k-страниц ушло от нескольких минут до секунд.
Håvard Tveite
5

Для сравнения посмотрите на Более эффективное пространственное соединение в Python без QGIS, ArcGIS, PostGIS и т . Д. В представленном решении используются модули Python Fiona , Shapely и rtree (Spatial Index).

С 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 или Quadtree ) с PyQGIS, как в разделе Использование пространственного индекса QGIS для ускорения вашего кода (с QgsSpatialIndex () и rtree )
  • но Пространственный Индекс не волшебная палочка. Когда требуется извлечь очень большую часть набора данных, пространственный индекс не может дать никакого преимущества в скорости.

Другой пример в ГИС: Как найти ближайшую линию к точке в QGIS? [дубликат]

ген
источник
Спасибо за все дополнительные объяснения. По сути, ваше решение использует список вместо слова, как Snorfalorpagus. Так что, похоже, действительно нет функции layer.getFeatures ([ids]) ...
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/

evod
источник