Próbuję edytować wartość atrybutu dla każdej funkcji w warstwie przy użyciu wtyczki QGIS Python. Przekonałem się, że robienie tego poza trybem edycji jest znacznie wolniejsze niż podczas edycji (nawet włączając wprowadzanie zmian). Zobacz kod poniżej (linie wymienne w tym samym punkcie pętli). Różnica prędkości dla mojego przykładowego zestawu danych wynosi 2 sekundy (tryb edycji) w porównaniu do 72 sekund (nie tryb edycji).
Modyfikowanie atrybutu w trybie edycji:
layer.changeAttributeValue(feature.id(), 17, QtCore.QVariant(value))
Modyfikowanie atrybutu poza trybem edycji:
layer.dataProvider().changeAttributeValues({ feature.id() : { 17 : QtCore.QVariant(value) } })
Czy to jest oczekiwane zachowanie? Nie potrzebuję, aby użytkownik mógł cofnąć zmiany, więc nie sądzę, że muszę używać trybu edycji.
Edycja 1: Zobacz pełny kod poniżej z obiema wersjami dołączonymi (ale skomentowanymi):
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)
Obie metody dają ten sam wynik, ale pisanie za pośrednictwem dostawcy danych trwa znacznie dłużej. Funkcja klasyfikuje bliskość elementów budynku do pobliskich pól (fioletowa) za pomocą wstępnie utworzonych buforów (brązowo-brązowy).
Odpowiedzi:
Problem polegał na tym, że każde wywołanie
QgsDataProvider.changeAttributeValues()
inicjuje nową transakcję ze wszystkimi powiązanymi kosztami ogólnymi (w zależności od dostawcy danych i konfiguracji systemu)Kiedy funkcje są najpierw zmieniane na warstwie (jak w
QgsVectorLayer.changeAttributeValue()
), wszystkie zmiany są buforowane w pamięci, co jest znacznie szybsze, a następnie zostają ostatecznie zatwierdzone w jednej transakcji.To samo buforowanie można osiągnąć w skrypcie (tj. Poza buforem edycji warstwy wektorowej), a następnie zatwierdzić w jednej transakcji, wywołując
QgsDataProvider.changeAttributeValues()
jeden raz poza pętlą.Istnieje również przydatny skrót do tego w najnowszych wersjach QGIS:
źródło