Виконання просторового запиту в циклі в 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 ?

Я спробував використовувати методи within () та intersects () , але, оскільки я не змушував їх працювати, я вдався до коду вище. Але, можливо, вони є ключовими зрештою.

Відповіді:


10

Вам не потрібна спеціальна функція (як "Ray Casting"), все є в PyQGIS ( містить () в обробці геометрії PyQGIS )

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/…
Nathan W

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

Чудова довідка та чудова відповідь! Я відзначу те, що я щойно опублікував, як рішення, оскільки це трохи легше здійснити. Ти повинен бути нагороджений великою кількістю грошей. +1 від мене точно.
BritishSteel

Не потрібно вказувати, if geo_pol.contains(geo_point) == True:оскільки це неявно if geo_pol.contains(geo_point)(завжди вірно)
ген

3

За допомогою порад колеги я нарешті змусив його працювати, використовуючи в межах ().

Загальна логіка

  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

Це також буде працювати з перехрестями (), а не з () . При використанні очок не має значення, який би ви використовували, оскільки вони обидва повернуть один і той же результат. При перевірці на лінії / полігони, однак, це може зробити важливу різницю: у межах () повертаються об'єкти, які знаходяться повністю всередині, тоді як перетинає () повторює об'єкти, які знаходяться повністю всередині і частково знаходяться в межах (тобто перетинаються з ознакою, як назва вказує).

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


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