Estou tentando editar o valor de um atributo para cada recurso em uma camada usando um plug-in QGIS Python. Descobri que fazer isso fora do modo de edição é muito mais lento do que durante a edição (mesmo incluindo a confirmação das edições). Veja o código abaixo (linhas intercambiáveis no mesmo ponto em um loop). A diferença na velocidade do meu conjunto de dados de amostra é de 2 segundos (modo de edição) vs 72 segundos (não no modo de edição).
Modificando um atributo no modo de edição:
layer.changeAttributeValue(feature.id(), 17, QtCore.QVariant(value))
Modificando um atributo fora do modo de edição:
layer.dataProvider().changeAttributeValues({ feature.id() : { 17 : QtCore.QVariant(value) } })
Esse é um comportamento esperado? Não preciso que o usuário consiga desfazer as alterações; portanto, acho que não preciso usar o modo de edição.
Edição 1: Veja o código completo abaixo com as duas versões incluídas (mas 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 os métodos produzem o mesmo resultado, mas a gravação via provedor de dados leva muito mais tempo. A função classifica a proximidade dos recursos de construção aos campos próximos (roxo) usando buffers pré-criados (marrom-ish).
Respostas:
O problema era que cada chamada
QgsDataProvider.changeAttributeValues()
inicia uma nova transação com toda a sobrecarga relacionada (dependendo do provedor de dados e da configuração do sistema)Quando os recursos são alterados na camada primeiro (como em
QgsVectorLayer.changeAttributeValue()
) todas as alterações são armazenadas em cache na memória, o que é muito mais rápido e, em seguida, são confirmadas em uma única transação no final.O mesmo buffer pode ser alcançado dentro do script (ou seja, fora do buffer de edição da camada vetorial) e, em seguida, confirmado em uma transação, chamando
QgsDataProvider.changeAttributeValues()
uma vez, fora do loop.Há também um atalho útil para isso nas versões recentes do QGIS:
fonte