Estoy tratando de editar el valor de un atributo para cada característica en una capa usando un complemento QGIS Python. Descubrí que hacer esto fuera del modo de edición es mucho más lento que durante la edición (incluso incluyendo la confirmación de las ediciones). Vea el código a continuación (líneas intercambiables en el mismo punto en un bucle). La diferencia de velocidad para mi conjunto de datos de muestra es de 2 segundos (modo de edición) frente a 72 segundos (no en modo de edición).
Modificación de un atributo en modo edición:
layer.changeAttributeValue(feature.id(), 17, QtCore.QVariant(value))
Modificación de un atributo fuera del modo de edición:
layer.dataProvider().changeAttributeValues({ feature.id() : { 17 : QtCore.QVariant(value) } })
¿Es este un comportamiento esperado? No necesito que el usuario pueda deshacer los cambios, así que no creo que necesite usar el modo de edición.
Edición 1: vea el código completo a continuación con ambas versiones incluidas (pero comentadas):
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)
Ambos métodos producen el mismo resultado, pero la escritura a través del proveedor de datos lleva mucho más tiempo. La función clasifica la proximidad de las características del edificio a los campos cercanos (morado) utilizando buffers pre-creados (marrón-ish).
Respuestas:
El problema era que cada llamada a
QgsDataProvider.changeAttributeValues()
iniciar una nueva transacción con todos los gastos generales relacionados (dependiendo del proveedor de datos y la configuración del sistema)Cuando las características se cambian en la capa primero (como en
QgsVectorLayer.changeAttributeValue()
) todos los cambios se almacenan en la memoria caché, lo que es mucho más rápido y luego se confirman en una sola transacción al final.Se puede lograr el mismo almacenamiento en búfer dentro del script (es decir, fuera del búfer de edición de la capa vectorial) y luego se confirma en una transacción llamando
QgsDataProvider.changeAttributeValues()
una vez, fuera del bucle.También hay un atajo útil para esto en versiones recientes de QGIS:
fuente