จะเข้าถึงฟีเจอร์ที่ส่งคืนโดย QgsSpatialIndex อย่างมีประสิทธิภาพได้อย่างไร


9

PyQGIS ตำราอธิบายถึงวิธีการตั้งค่าดัชนีเชิงพื้นที่ แต่เพียงอธิบายถึงครึ่งหนึ่งของการใช้งาน:

สร้างดัชนีเชิงพื้นที่ - รหัสต่อไปนี้สร้างดัชนีที่ว่างเปล่า

index = QgsSpatialIndex()

เพิ่มคุณสมบัติในการทำดัชนี - ดัชนีใช้วัตถุ QgsFeature และเพิ่มลงในโครงสร้างข้อมูลภายใน คุณสามารถสร้างวัตถุด้วยตนเองหรือใช้จากการเรียกไปยังคุณลักษณะถัดไปของผู้ให้บริการ ()

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>

ขอบคุณ! Snorfalorpagus กล่าวว่า setFilterFids จะช้ากว่าโซลูชั่นที่เขาโพสต์ คุณยืนยันสิ่งนี้หรือไม่
underdark

ฉันไม่ได้ใช้มันในชุดผลลัพธ์ขนาดใหญ่ดังนั้นจึงไม่สามารถยืนยันได้
gsherman

1
ฉันยืนยันและในกรณีของฉันrtreeเร็วกว่า QgsSpatialIndex () (สำหรับการสร้างPlanar Graphsจากชั้น polylines ที่ใหญ่มากการขนย้ายของPlanarGraphโมดูลด้วย Shapely ใน PyQGIS แต่การแก้ปัญหาด้วย Fiona, Shapely และ rtree ยังคงเป็น เร็วที่สุด)
ยีน

1
ฉันเชื่อว่าคำถามเกี่ยวกับการได้รับคุณสมบัติจริงจากรหัสคุณลักษณะที่ส่งคืนแทนที่จะเป็นความเร็วของวิธีการจัดทำดัชนีต่างๆ
gsherman

7

ในการโพสต์บล็อกในเรื่อง , Nathan Woodrow ให้รหัสต่อไปนี้:

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)ดูเหมือนจะเร็วกว่านี้มาก


1
การใช้พจนานุกรมนั้นเร็วกว่าlayer.getFeatures(QgsFeatureRequest().setFilterFid(fid))มาก ฉันกำลังทำงานกับเลเยอร์ที่มีคุณสมบัติ 140k และเวลาโดยรวมสำหรับการค้นหา 140k ก็เพิ่มขึ้นจากหลายนาทีเป็นวินาที
Håvard Tveite

5

สำหรับการเปรียบเทียบลักษณะที่มีประสิทธิภาพมากขึ้นเชิงพื้นที่เข้าร่วมในหลามโดยไม่ต้อง QGIS, ArcGIS, PostGIS ฯลฯ วิธีการแก้ปัญหาที่นำเสนอการใช้งานโมดูลหลามFiona , หุ่นดีและ RTree (Spatial ดัชนี)

ด้วย 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 ()) คุณทำซ้ำผ่านรูปทรงเรขาคณิตที่มีโอกาสที่จะตัดกันกับรูปทรงเรขาคณิตปัจจุบันของคุณ ('ตัวกรอง' ซึ่งสามารถบันทึกการคำนวณและเวลาจำนวนมาก ... )
  • นอกจากนี้คุณยังสามารถใช้อื่น ๆ ดัชนีเชิงพื้นที่หลามโมดูล ( RTree , Pyrtreeหรือควอดทรี ) กับ PyQGIS ในขณะที่การใช้ QGIS ดัชนีเชิงพื้นที่เพื่อเพิ่มความเร็วในรหัสของคุณ (กับ QgsSpatialIndex () และRTree )
  • แต่ดัชนีเชิงพื้นที่ไม่ใช่ไม้เท้าวิเศษ เมื่อส่วนใหญ่ของชุดข้อมูลจะต้องถูกดึงดัชนีอวกาศไม่สามารถให้ผลประโยชน์ความเร็วใด ๆ

ตัวอย่างอื่น ๆ ใน GIS se: ค้นหาบรรทัดที่ใกล้ที่สุดถึงจุดหนึ่งใน QGIS ได้อย่างไร [ซ้ำ]


ขอบคุณสำหรับคำอธิบายเพิ่มเติมทั้งหมด โดยทั่วไปวิธีการแก้ปัญหาของคุณใช้รายการแทนการเขียนตามที่ Snorfalorpagus ทำ ดังนั้นดูเหมือนว่าจะไม่มีเลเยอร์ฟังก์ชัน getFeatures ([ids]) ...
underdark

วัตถุประสงค์ของคำอธิบายนี้เป็นเรื่องของเรขาคณิตอย่างหมดจดและเป็นเรื่องง่ายมากที่จะเพิ่มฟังก์ชั่น layer.getFeatures ([ID]) เช่นเดียวกับในพื้นที่ที่มีประสิทธิภาพยิ่งขึ้นเข้าร่วมในงูใหญ่โดยไม่มี QGIS, ArcGIS, PostGIS ฯลฯ
ยีน

0

เห็นได้ชัดว่าวิธีเดียวที่จะได้รับประสิทธิภาพที่ดีคือการหลีกเลี่ยงหรือมัดสายไปที่ layer.getFeatures () แม้ว่าตัวกรองจะง่ายเหมือน fid

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

แหล่งที่มา: http://nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/

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