9 votos

Velocidad de edición de atributos en QGIS desde un plugin de Python

Estoy tratando de editar el valor de un atributo para cada característica en una capa utilizando un plugin de QGIS Python. He encontrado que hacer esto fuera del modo de edición es mucho más lento que mientras se edita (incluso incluyendo la confirmación de las ediciones). Véase el código siguiente (líneas intercambiables en el mismo punto de 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 (sin modo de edición).

Modificar un atributo en modo de edición:

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

Modificar un atributo fuera del modo de edición:

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

¿Es un comportamiento esperado? No necesito que el usuario pueda deshacer los cambios, así que no creo que necesite usar el modo de edición.

Editar 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 (púrpura) utilizando topes precreados (marrón). Proximity

1 votos

Eso no parece correcto. ¿Puedes compartir algo más de tu código?

0 votos

@NathanW He añadido la función completa. La idea es comprobar dos capas para las intersecciones, a continuación, actualizar una capa con el atributo de la otra capa cuando se encuentra una intersección.

0 votos

¿Qué tipo de datos está utilizando?

7voto

Michael Puntos 11

El problema era que cada llamada a QgsDataProvider.changeAttributeValues() inicia una nueva transacción con todos los gastos generales relacionados (según el 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 caché en la memoria, lo que es mucho más rápido y luego se comprometen en una sola transacción al final.

El mismo almacenamiento en búfer se puede lograr dentro del script, (es decir, fuera del búfer de edición de la capa vectorial) y luego confirmar en una transacción llamando a QgsDataProvider.changeAttributeValues() una vez, fuera del bucle.

En las versiones recientes de QGIS también hay un práctico atajo para esto:

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

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X