Kecepatan mengedit atribut di QGIS dari plugin Python

9

Saya mencoba mengedit nilai atribut untuk setiap fitur dalam sebuah layer menggunakan plugin QGIS Python. Saya menemukan bahwa melakukan ini di luar mode pengeditan jauh lebih lambat daripada saat mengedit (bahkan termasuk melakukan pengeditan). Lihat kode di bawah ini (garis dipertukarkan pada titik yang sama dalam satu lingkaran). Perbedaan kecepatan untuk dataset sampel saya adalah 2 detik (mode edit) vs 72 detik (bukan mode edit).

Mengubah atribut dalam mode edit:

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

Mengubah atribut di luar mode edit:

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

Apakah ini perilaku yang diharapkan? Saya tidak membutuhkan pengguna untuk dapat membatalkan perubahan, jadi saya rasa saya tidak perlu menggunakan mode edit.

Sunting 1: Lihat kode lengkap di bawah ini dengan kedua versi disertakan (tetapi dikomentari):

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)

Kedua metode menghasilkan hasil yang sama, tetapi menulis melalui penyedia data membutuhkan waktu lebih lama. Fungsi mengklasifikasikan kedekatan fitur bangunan ke bidang terdekat (ungu) menggunakan buffer yang dibuat sebelumnya (brown-ish). Kedekatan

Snorfalorpagus
sumber
1
Sepertinya itu tidak benar. Bisakah Anda membagikan kode Anda lagi.
Nathan W
@NathanW Saya telah menambahkan fungsi lengkap. Idenya adalah untuk memeriksa dua lapisan untuk persimpangan, lalu perbarui satu lapisan dengan atribut lapisan lain ketika persimpangan ditemukan.
Snorfalorpagus
Jenis data apa yang Anda gunakan?
Nathan W
Kedua lapisan ESRI Shapefile (poligon). Layer_target memiliki 905 fitur (bangunan), layer_buffered memiliki 1155 fitur (ruang terbuka) dengan poligon yang tumpang tindih mewakili buffer yang berbeda (100m, 50m, 20m, 10m, 5m) - maka atribut 'distance'.
Snorfalorpagus
1
Bagaimana data Anda diakses? (yaitu melalui jaringan, disk tradisional, SSD)? Apakah mungkin, bahwa I / O overhead untuk operasi penulisan tunggal memakan waktu? Sebagai tes: dapatkah Anda mencoba buffering semua atribut Anda yang berubah dalam memori dan kemudian memanggil dataProvider.changeAttributeValues ​​() sekali di akhir.
Matthias Kuhn

Jawaban:

7

Masalahnya adalah, bahwa setiap panggilan untuk QgsDataProvider.changeAttributeValues()memulai transaksi baru dengan semua overhead terkait (tergantung pada penyedia data dan konfigurasi sistem)

Ketika fitur diubah pada layer pertama (seperti pada QgsVectorLayer.changeAttributeValue()) semua perubahan di-cache dalam memori, apa yang jauh lebih cepat dan kemudian dilakukan dalam satu transaksi tunggal pada akhirnya.

Buffer yang sama dapat dicapai dalam skrip, (yaitu di luar buffer edit layer vektor) dan kemudian dilakukan dalam satu transaksi dengan memanggil QgsDataProvider.changeAttributeValues()sekali, di luar loop.

Ada juga jalan pintas yang berguna untuk ini dalam versi QGIS terbaru:

with edit(layer):
    for fid in fids:
        layer.changeAttributeValue(fid, idx, value)
Matthias Kuhn
sumber