Melakukan Kueri Spasial dalam satu lingkaran di PyQGIS

9

Apa yang saya coba lakukan: loop melalui shapefile titik dan pilih setiap titik yang jatuh ke dalam poligon.

Kode berikut ini terinspirasi oleh contoh kueri spasial yang saya temukan di sebuah buku:

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

Ini berfungsi, dan ia memang memilih dataset, tetapi masalahnya adalah ia memilih dengan kotak pembatas , karenanya jelas mengembalikan poin yang tidak saya minati:

masukkan deskripsi gambar di sini

Bagaimana saya bisa pergi hanya mengembalikan poin dalam poligon tanpa menggunakan qgis: selectbylocation ?

Saya telah mencoba menggunakan metode inside () dan intersect () , tetapi karena saya tidak membuatnya berfungsi, saya beralih ke kode di atas. Tapi mungkin mereka kuncinya.

BritishSteel
sumber

Jawaban:

10

Anda tidak memerlukan fungsi khusus (seperti "Ray Casting"), semuanya ada di PyQGIS ( berisi () di 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" 

atau dalam satu baris

 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)
 ...

Anda juga bisa langsung menggunakan

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

Masalahnya di sini adalah bahwa Anda harus mengulangi semua geometri (poligon dan titik). Lebih menarik menggunakan indeks spasial pembatas: Anda beralih hanya melalui geometri yang berpeluang bersinggungan dengan geometri Anda saat ini ('filter', lihat Bagaimana cara efisien mengakses fitur yang dikembalikan oleh QgsSpatialIndex? )

gen
sumber
1
Lihat juga nathanw.net/2013/01/04/...
Nathan W
5

Anda dapat menggunakan algoritme "Ray Casting" yang sedikit saya adaptasi untuk digunakan dengan 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

Diterapkan pada situasi ini:

masukkan deskripsi gambar di sini

hasilnya, di Python Console, adalah:

[2, 2]

Itu berhasil.

Catatan Pengeditan:

Kode dengan proposal gen yang lebih ringkas :

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
sumber
Referensi hebat dan jawaban bagus! Saya akan menandai yang baru saja saya posting, sebagai solusi, karena sedikit lebih mudah untuk diterapkan. Anda harus diberi banyak upvotes. +1 dari saya, pasti.
BritishSteel
Anda tidak perlu menentukan if geo_pol.contains(geo_point) == True:karena ini tersirat dalam if geo_pol.contains(geo_point)(selalu Benar)
gen
3

Dengan beberapa saran dari rekan kerja saya akhirnya berhasil menggunakan dalam ().

Logika umum

  1. dapatkan fitur poligon
  2. dapatkan fitur poin
  3. loop melalui setiap fitur dari file poligon, dan untuk masing-masing:
    • dapatkan geometri
    • loop melalui semua titik
      • dapatkan geometri titik tunggal
      • uji apakah geometri berada dalam geometri poligon

Ini kodenya:

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

Ini juga akan bekerja dengan intersect () bukan inside () . Saat menggunakan poin, tidak masalah yang mana yang akan Anda gunakan, karena keduanya akan menghasilkan hasil yang sama. Ketika memeriksa garis / poligon, bagaimanapun, itu dapat membuat perbedaan penting: di dalam () mengembalikan objek yang sepenuhnya di dalam, sedangkan memotong () mengembalikan objek yang sepenuhnya di dalam dan yang sebagian di dalam (yaitu yang bersinggungan dengan fitur, seperti namanya menunjukkan).

masukkan deskripsi gambar di sini

BritishSteel
sumber
Saya mencoba solusi Anda. Ini hanya berfungsi jika Anda memiliki satu poligon, jika tidak hanya titik dalam poligon pertama yang akan dipilih
ilFonta