Velocità di modifica degli attributi in QGIS da un plugin Python


9

Sto cercando di modificare il valore di un attributo per ogni funzione in un livello usando un plug-in QGIS Python. Ho scoperto che farlo al di fuori della modalità di modifica è molto più lento rispetto alla modifica (anche includendo le modifiche). Vedi il codice sotto (linee intercambiabili nello stesso punto in un ciclo). La differenza di velocità per il mio set di dati di esempio è di 2 secondi (modalità di modifica) contro 72 secondi (non modalità di modifica).

Modifica di un attributo in modalità modifica:

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

Modifica di un attributo al di fuori della modalità di modifica:

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

È un comportamento previsto? Non ho bisogno che l'utente sia in grado di annullare le modifiche, quindi non credo di dover utilizzare la modalità di modifica.

Modifica 1: vedi sotto il codice completo con entrambe le versioni incluse (ma commentate):

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)

Entrambi i metodi producono lo stesso risultato, ma la scrittura tramite il fornitore di dati richiede molto più tempo. La funzione classifica la vicinanza delle caratteristiche dell'edificio ai campi vicini (viola) utilizzando buffer pre-creati (brown-ish). Prossimità


1
Non sembra giusto. Puoi condividere altro del tuo codice.
Nathan W

@NathanW Ho aggiunto la funzione completa. L'idea è di controllare due livelli per le intersezioni, quindi aggiornare un livello con l'attributo dell'altro livello quando viene trovata un'intersezione.
Snorfalorpagus,

Che tipo di dati stai usando?
Nathan W,

Entrambi i livelli di Shapefile ESRI (poligono). Il layer_target ha 905 feature (edifici), il layer_buffered ha 1155 features (spazi aperti) con poligoni sovrapposti che rappresentano buffer diversi (100m, 50m, 20m, 10m, 5m) - da qui l'attributo 'distance'.
Snorfalorpagus,

1
Come si accede ai dati? (ovvero su rete, disco tradizionale, SSD)? È possibile che l'overhead I / O per una singola operazione di scrittura richieda tempo? Come test: puoi provare a memorizzare nel buffer tutti gli attributi modificati e quindi chiamare dataProvider.changeAttributeValues ​​() una volta alla fine.
Matthias Kuhn,

Risposte:


7

Il problema era che ogni chiamata per QgsDataProvider.changeAttributeValues()avviare una nuova transazione con tutti i relativi costi generali (a seconda del provider di dati e della configurazione del sistema)

Quando le funzionalità vengono modificate per prime sul livello (come in QgsVectorLayer.changeAttributeValue()), tutte le modifiche vengono memorizzate nella cache, ciò che è molto più veloce e alla fine vengono impegnate in una singola transazione.

Lo stesso buffering può essere ottenuto all'interno dello script, (cioè al di fuori del buffer di modifica del layer vettoriale) e quindi eseguito il commit in una transazione chiamando QgsDataProvider.changeAttributeValues()una volta, al di fuori del loop.

C'è anche un comodo collegamento per questo nelle recenti versioni di QGIS:

with edit(layer):
    for fid in fids:
        layer.changeAttributeValue(fid, idx, value)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.