Ich versuche, den Wert eines Attributs für jedes Feature in einer Ebene mithilfe eines QGIS Python-Plugins zu bearbeiten. Ich habe festgestellt, dass dies außerhalb des Bearbeitungsmodus viel langsamer ist als beim Bearbeiten (einschließlich des Festschreibens der Änderungen). Siehe Code unten (Linien, die an derselben Stelle in einer Schleife austauschbar sind). Der Geschwindigkeitsunterschied für meinen Beispieldatensatz beträgt 2 Sekunden (Bearbeitungsmodus) gegenüber 72 Sekunden (kein Bearbeitungsmodus).
Ändern eines Attributs im Bearbeitungsmodus:
layer.changeAttributeValue(feature.id(), 17, QtCore.QVariant(value))
Ändern eines Attributs außerhalb des Bearbeitungsmodus:
layer.dataProvider().changeAttributeValues({ feature.id() : { 17 : QtCore.QVariant(value) } })
Ist das ein erwartetes Verhalten? Der Benutzer muss die Änderungen nicht rückgängig machen können, daher muss ich den Bearbeitungsmodus nicht verwenden.
Bearbeiten 1: Siehe vollständigen Code unten mit beiden Versionen enthalten (aber auskommentiert):
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)
Beide Methoden führen zum gleichen Ergebnis, das Schreiben über den Datenprovider dauert jedoch viel länger. Die Funktion klassifiziert die Nähe von Gebäudeelementen zu nahe gelegenen Feldern (lila) mithilfe vorgefertigter Puffer (braun).
Antworten:
Das Problem war, dass jeder Aufruf zum
QgsDataProvider.changeAttributeValues()
Initiieren einer neuen Transaktion mit dem gesamten damit verbundenen Overhead (abhängig vom Datenprovider und der Systemkonfiguration)Wenn die Features zuerst (wie in
QgsVectorLayer.changeAttributeValue()
) auf der Ebene geändert werden, werden alle Änderungen im Speicher zwischengespeichert, was viel schneller ist und am Ende in einer einzigen Transaktion festgeschrieben wird.Die gleiche Pufferung kann innerhalb des Skripts erreicht werden (dh außerhalb des Bearbeitungspuffers der Vektorebene) und dann in einer Transaktion durch
QgsDataProvider.changeAttributeValues()
einmaliges Aufrufen außerhalb der Schleife festgeschrieben werden .In den neuesten QGIS-Versionen gibt es dafür auch eine praktische Verknüpfung:
quelle