Выполнение пространственного запроса в цикле в PyQGIS

9

Что я пытаюсь сделать: перебрать точечный шейп-файл и выбрать каждую точку, которая попадает в многоугольник.

Следующий код основан на примере пространственного запроса, который я нашел в книге:

mitte_path = r"D:\PythonTesting\SelectByLocation\mitte.shp"
punkte_path = r"D:\PythonTesting\SelectByLocation\punkte.shp"

polygon = QgsVectorLayer(mitte_path, 'Mitte', 'ogr')
points = QgsVectorLayer(punkte_path, 'Berlin Punkte', 'ogr')

QgsMapLayerRegistry.instance().addMapLayer(polygon)
QgsMapLayerRegistry.instance().addMapLayer(points)

polyFeatures = polygon.getFeatures()

pointsCount = 0

for poly_feat in polyFeatures:
    polyGeom = poly_feat.geometry()
    pointFeatures = points.getFeatures(QgsFeatureRequest().setFilterRect(polyGeom.boundingBox()))
    for point_feat in pointFeatures:
        points.select(point_feat.id())
        pointsCount += 1

print 'Total:',pointsCount

Это работает, и он выбирает наборы данных, но проблема в том, что он выбирает ограничивающий прямоугольник , поэтому, очевидно, возвращает точки, которые меня не интересуют:

введите описание изображения здесь

Как можно было бы возвращать только точки внутри многоугольника без использования qgis: selectbylocation ?

Я пытался использовать методы inside () и intersects () , но так как я не заставлял их работать, я прибегнул к приведенному выше коду. Но, возможно, они все-таки ключ.

BritishSteel
источник

Ответы:

10

Вам не нужна специальная функция (например, «Приведение лучей»), все в PyQGIS ( содержит () в PyQGIS Geometry Handling )

polygons = [feature for feature in polygons.getFeatures()]
points = [feature for feature in points.getFeatures()]
for pt in points: 
     point = pt.geometry() # only and not pt.geometry().asPolygon() 
     for pol in polygons:
        poly = pol.geometry()
        if poly.contains(point):
             print "ok" 

или в одну строку

 polygons = [feature for feature in polygons.getFeatures()]
 points = [feature for feature in points.getFeatures()]
 resulting = [pt for pt in points for poly in polygons if poly.geometry().contains(pt.geometry())]
 print len(resulting)
 ...

Вы также можете напрямую использовать

[pt.geometry().asPoint() for pt in points for poly in polygons if poly.geometry().contains(pt.geometry())]

Проблема в том, что вы должны пройти через все геометрии (полигоны и точки). Более интересно использовать ограничивающий пространственный индекс: вы перебираете только те геометрии, которые могут пересекаться с вашей текущей геометрией («фильтр», смотрите Как эффективно получить доступ к функциям, возвращаемым QgsSpatialIndex? )

ген
источник
1
Также см. Nathanw.net/2013/01/04/…
Натан Вт
5

Вы можете использовать алгоритм «Ray Casting» , который я немного адаптировал для использования с PyQGIS:

def point_in_poly(point,poly):
    x = point.x()
    y = point.y()

    n = len(poly)
    inside = False

    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside

## Test
mapcanvas = iface.mapCanvas()

layers = mapcanvas.layers()

#For polygon 
polygon = [feature.geometry().asPolygon() 
            for feature in layers[1].getFeatures()]

points = [feat.geometry().asPoint() 
           for feat in layers[0].getFeatures()]

## Call the function with the points and the polygon
count = [0]*(layers[1].featureCount())

for point in points:
    i = 0
    for feat in polygon:
        if point_in_poly(point, feat[0]) == True:
            count[i] += 1
        i += 1

print count

Применительно к этой ситуации:

введите описание изображения здесь

результат, на консоли Python, был:

[2, 2]

Это сработало.

Редактирование Примечание:

Код с более кратким предложением гена :

mapcanvas = iface.mapCanvas()

layers = mapcanvas.layers()

count = [0]*(layers[1].featureCount())

polygon = [feature
           for feature in layers[1].getFeatures()]

points = [feature
          for feature in layers[0].getFeatures()]

for point in points:

    i = 0

    geo_point = point.geometry()

    for pol in polygon:
        geo_pol = pol.geometry()

        if geo_pol.contains(geo_point):
            count[i] += 1
        i += 1

print count
xunilk
источник
Отличная ссылка и отличный ответ! Тем не менее, я отмечу тот, который я только что опубликовал, как решение, поскольку его немного легче реализовать. Вы должны быть вознаграждены большим количеством голосов, хотя. +1 от меня, точно.
BritishSteel
Вам не нужно указывать, if geo_pol.contains(geo_point) == True:потому что это неявно в if geo_pol.contains(geo_point)(всегда True)
гене
3

С некоторыми советами от напарника я наконец получил его для работы с помощью inside ().

Общая логика

  1. получить особенности полигона (ов)
  2. получить особенности очков
  3. цикл по каждой функции из файла многоугольника, и для каждого:
    • получить геометрию
    • пройти через все точки
      • получить геометрию одной точки
      • проверить, находится ли геометрия внутри геометрии многоугольника

Вот код:

mitte_path = r"D:\PythonTesting\SelectByLocation\mitte.shp"
punkte_path = r"D:\PythonTesting\SelectByLocation\punkte.shp"

poly = QgsVectorLayer(mitte_path, 'Mitte', 'ogr')
points = QgsVectorLayer(punkte_path, 'Berlin Punkte', 'ogr')

QgsMapLayerRegistry.instance().addMapLayer(poly)
QgsMapLayerRegistry.instance().addMapLayer(points)

polyFeatures = poly.getFeatures()
pointFeatures = points.getFeatures()

pointCounter = 0

for polyfeat in polyFeatures:
    polyGeom = polyfeat.geometry()
    for pointFeat in pointFeatures:
        pointGeom = pointFeat.geometry()
        if pointGeom.within(polyGeom):
            pointCounter += 1
            points.select(pointFeat.id())

print 'Total',pointCounter

Это также будет работать с intersects () вместо inside () . При использовании очков не имеет значения, какой из них вы бы использовали, поскольку они оба будут возвращать один и тот же результат. Однако при проверке линий / полигонов это может иметь важное значение: в пределах () возвращаются объекты, которые полностью находятся внутри, тогда как intersects () возвращает объекты, которые полностью находятся внутри и которые частично находятся внутри (то есть пересекаются с объектом, как имя указывает)

введите описание изображения здесь

BritishSteel
источник
Я попробовал ваше решение. Это работает, только если у вас есть один полигон, иначе будут выбраны только точки в первом полигоне
ilFonta