Я пытаюсь изменить значение атрибута для каждого объекта в слое, используя плагин QGIS Python. Я обнаружил, что делать это вне режима редактирования намного медленнее, чем во время редактирования (даже включая фиксацию правок). Смотрите код ниже (строки взаимозаменяемы в одной и той же точке цикла). Разница в скорости для моего образца набора данных составляет 2 секунды (режим редактирования) по сравнению с 72 секундами (не режим редактирования).
Изменение атрибута в режиме редактирования:
layer.changeAttributeValue(feature.id(), 17, QtCore.QVariant(value))
Изменение атрибута вне режима редактирования:
layer.dataProvider().changeAttributeValues({ feature.id() : { 17 : QtCore.QVariant(value) } })
Это ожидаемое поведение? Мне не нужно, чтобы пользователь мог отменить изменения, поэтому я не думаю, что мне нужно использовать режим редактирования.
Изменить 1: См. Полный код ниже с обеими версиями включены (но закомментированы):
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)
Оба метода дают одинаковый результат, но запись через поставщика данных занимает гораздо больше времени. Функция классифицирует близость элементов здания к близлежащим полям (фиолетовым цветом) с использованием предварительно созданных буферов (коричневого цвета).
Ответы:
Проблема заключалась в том, что каждый вызов
QgsDataProvider.changeAttributeValues()
инициирует новую транзакцию со всеми связанными издержками (в зависимости от поставщика данных и конфигурации системы).Когда объекты меняются на слое вначале (как в
QgsVectorLayer.changeAttributeValue()
), все изменения кэшируются в памяти, что происходит гораздо быстрее, а затем фиксируется в одной транзакции в конце.Та же самая буферизация может быть достигнута внутри скрипта (то есть вне буфера редактирования векторного уровня) и затем зафиксирована в одной транзакции, вызвав
QgsDataProvider.changeAttributeValues()
один раз, вне цикла.В последних версиях QGIS для этого есть удобный ярлык:
источник