การทำ Spatial Query ในลูปใน 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 ได้อย่างไร

ฉันได้ลองใช้วิธีการภายใน ()และปริภูมิ ()แต่เนื่องจากฉันไม่ได้รับให้ทำงานฉันจึงใช้รหัสด้านบน แต่บางทีพวกเขาเป็นกุญแจสำคัญหลังจากทั้งหมด

คำตอบ:


10

คุณไม่ต้องการฟังก์ชั่นพิเศษ (เช่น "Ray Casting") ทุกอย่างอยู่ใน 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/ …
นาธาน 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 Console คือ:

[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

การอ้างอิงที่ดีและคำตอบที่ยอดเยี่ยม! ฉันจะทำเครื่องหมายสิ่งที่ฉันเพิ่งโพสต์ แต่เป็นวิธีแก้ปัญหาเพราะมันง่ายกว่าที่จะใช้ คุณควรได้รับรางวัลด้วย upvotes มากมาย +1 จากฉันแน่นอน
BritishSteel

คุณไม่จำเป็นต้องระบุif geo_pol.contains(geo_point) == True:เนื่องจากเป็นนัยif geo_pol.contains(geo_point)(เป็น True เสมอ)
ยีน

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

นอกจากนี้ยังจะทำงานร่วมกับปริภูมิ ()แทนภายใน () เมื่อใช้คะแนนมันไม่สำคัญว่าคุณจะใช้แบบไหนเพราะทั้งคู่จะได้ผลลัพธ์เหมือนกัน เมื่อตรวจสอบสำหรับสาย / รูปหลายเหลี่ยม แต่ก็สามารถทำให้ความแตกต่างที่สำคัญ: ภายใน () วัตถุผลตอบแทนที่ทั้งหมดภายในขณะปริภูมิ () retuns วัตถุที่มีทั้งภายในและที่เป็นส่วนหนึ่งภายใน (เช่นที่ตัดด้วยคุณสมบัติที่เป็น ชื่อบ่งชี้)

ป้อนคำอธิบายรูปภาพที่นี่


ฉันลองวิธีแก้ปัญหาของคุณ ใช้งานได้เฉพาะในกรณีที่คุณมีรูปหลายเหลี่ยมหนึ่งรูปเท่านั้นมิฉะนั้นจะมีการเลือกเฉพาะจุดภายในรูปหลายเหลี่ยมแรก
ilFonta
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.