ความเร็วของการแก้ไขคุณสมบัติใน QGIS จากปลั๊กอิน Python


9

ฉันกำลังพยายามแก้ไขค่าของคุณลักษณะสำหรับแต่ละคุณลักษณะในเลเยอร์โดยใช้ปลั๊กอิน QGIS Python ฉันพบว่าการทำสิ่งนี้นอกโหมดแก้ไขจะช้ากว่าขณะแก้ไข (แม้รวมถึงการแก้ไข) ดูรหัสด้านล่าง (บรรทัดที่เปลี่ยนได้ที่จุดเดียวกันในลูป) ความแตกต่างของความเร็วสำหรับชุดข้อมูลตัวอย่างของฉันคือ 2 วินาที (โหมดแก้ไข) และ 72 วินาที (ไม่ใช่โหมดแก้ไข)

การปรับเปลี่ยนแอตทริบิวต์ในโหมดแก้ไข:

layer.changeAttributeValue(feature.id(), 17, QtCore.QVariant(value))

การปรับเปลี่ยนแอตทริบิวต์นอกโหมดแก้ไข:

layer.dataProvider().changeAttributeValues({ feature.id() : { 17 : QtCore.QVariant(value) } })

นี่เป็นพฤติกรรมที่คาดหวังหรือไม่? ฉันไม่ต้องการให้ผู้ใช้สามารถยกเลิกการเปลี่ยนแปลงได้ดังนั้นฉันไม่คิดว่าฉันต้องใช้โหมดแก้ไข

แก้ไข 1:ดูรหัสเต็มด้านล่างโดยมีทั้งสองเวอร์ชันรวมไว้ (แต่ใส่ความเห็น)

def run(self):
    try:
        # create spatial index of buffered layer
        index = QgsSpatialIndex()
        self.layer_buffered.select()
        for feature in self.layer_buffered:
            index.insertFeature(feature)

        # enable editing
        #was_editing = self.layer_target.isEditable()
        #if was_editing is False:
        #    self.layer_target.startEditing()

        # check intersections
        self.layer_target.select()
        self.feature_count = self.layer_target.featureCount()
        for feature in self.layer_target:
            distance_min = None
            fids = index.intersects(feature.geometry().boundingBox())
            for fid in fids:
                # feature's bounding box and buffer bounding box intersect
                feature_buffered = QgsFeature()
                self.layer_buffered.featureAtId(fid, feature_buffered)
                if feature.geometry().intersects(feature_buffered.geometry()):
                    # feature intersects buffer
                    attrs = feature_buffered.attributeMap()
                    distance = attrs[0].toPyObject()
                    if distance_min is None or distance < distance_min:
                        distance_min = distance
                if self.abort is True: break
            if self.abort is True: break

            # update feature's distance attribute
            self.layer_target.dataProvider().changeAttributeValues({feature.id(): {self.field_index: QtCore.QVariant(distance_min)}})
            #self.layer_target.changeAttributeValue(feature.id(), self.field_index, QtCore.QVariant(distance_min))

            self.calculate_progress()

        # disable editing
        #if was_editing is False:
        #    self.layer_target.commitChanges()

    except:
        import traceback
        self.error.emit(traceback.format_exc())
    self.progress.emit(100)
    self.finished.emit(self.abort)

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


1
ดูเหมือนจะไม่ถูกต้อง คุณสามารถแบ่งปันรหัสของคุณอีกต่อไปได้ไหม
นาธาน W

@NathanW ฉันได้เพิ่มฟังก์ชั่นที่สมบูรณ์แล้ว แนวคิดคือการตรวจสอบสองเลเยอร์เพื่อหาจุดตัดจากนั้นอัปเดตหนึ่งเลเยอร์ด้วยคุณลักษณะของเลเยอร์อื่นเมื่อพบจุดตัด
Snorfalorpagus

คุณใช้ประเภทข้อมูลใด
นาธาน W

ทั้งสองเลเยอร์ ESRI Shapefiles (รูปหลายเหลี่ยม) layer_target มี 905 คุณสมบัติ (อาคาร), layer_buffered มีคุณสมบัติ 1155 (พื้นที่เปิดโล่ง) ที่มีรูปหลายเหลี่ยมซ้อนทับกันซึ่งแสดงบัฟเฟอร์ที่แตกต่างกัน (100m, 50m, 20m, 10m, 5m) - ดังนั้นคุณลักษณะ 'ระยะทาง'
Snorfalorpagus

1
คุณเข้าถึงข้อมูลได้อย่างไร? (เช่นผ่านเครือข่ายดิสก์แบบดั้งเดิม SSD) เป็นไปได้หรือไม่ที่ค่าใช้จ่าย I / O สำหรับการดำเนินการเขียนครั้งเดียวใช้เวลานาน? เป็นการทดสอบ: คุณสามารถลองบัฟเฟอร์คุณลักษณะที่เปลี่ยนแปลงทั้งหมดของคุณในหน่วยความจำแล้วเรียก dataProvider.changeAttributeValues ​​() หนึ่งครั้งในตอนท้าย
Matthias Kuhn

คำตอบ:


7

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

เมื่อมีการเปลี่ยนแปลงคุณสมบัติในเลเยอร์แรก (ในQgsVectorLayer.changeAttributeValue()) การเปลี่ยนแปลงทั้งหมดจะถูกเก็บไว้ในหน่วยความจำสิ่งที่เร็วขึ้นมากและมีความมุ่งมั่นในการทำธุรกรรมเดียวในที่สุด

การบัฟเฟอร์เดียวกันสามารถทำได้ภายในสคริปต์ (เช่นภายนอกบัฟเฟอร์การแก้ไขเลเยอร์เวกเตอร์) และจากนั้นส่งมอบในหนึ่งธุรกรรมโดยการเรียกQgsDataProvider.changeAttributeValues()หนึ่งครั้งนอกลูป

นอกจากนี้ยังมีทางลัดที่มีประโยชน์สำหรับสิ่งนี้ในเวอร์ชั่น QGIS ล่าสุด:

with edit(layer):
    for fid in fids:
        layer.changeAttributeValue(fid, idx, value)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.