Швидкість редагування атрибутів у QGIS з плагіна Python


9

Я намагаюся редагувати значення атрибута для кожної функції в шарі, використовуючи плагін 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)

Обидва способи дають однаковий результат, але запис через постачальника даних займає набагато більше часу. Функція класифікує близькість функцій побудови до довколишніх полів (фіолетовий), використовуючи попередньо створені буфери (коричневі). Близькість


1
Це не здається правильним. Чи можете ви поділитися ще своїм кодом.
Nathan W

@NathanW Я додав повну функцію. Ідея полягає у тому, щоб перевірити два шари на перехрестя, а потім оновити один шар атрибутом іншого шару, коли буде знайдено перетин.
Snorfalorpagus

Який тип даних ви використовуєте?
Nathan W

Обидва шари ESRI Shapefiles (багатокутник). У шарі_таргета є 905 функцій (будівлі), у шарі_буфер є 1155 особливостей (відкриті простори) з багатокутниками, що перекриваються, що представляють різні буфери (100 м, 50 м, 20 м, 10 м, 5 м) - отже, атрибут "відстань".
Snorfalorpagus

1
Як ви отримуєте доступ до даних? (тобто через мережу, традиційний диск, SSD)? Чи можливо, що накладні витрати вводу / виводу для однієї операції запису забирають багато часу? Як тест: чи можете ви спробувати буферизувати всі змінені атрибути в пам'яті, а потім один раз в кінці викликати dataProvider.changeAttributeValues ​​().
Маттіас Кун

Відповіді:


7

Проблема полягала в тому, що кожен виклик QgsDataProvider.changeAttributeValues()ініціював нову транзакцію з усіма пов'язаними накладними витратами (залежно від постачальника даних та конфігурації системи)

Коли функції змінюються на шарі спочатку (як і в QgsVectorLayer.changeAttributeValue()), всі зміни зберігаються в кеш-пам'яті, що набагато швидше, а згодом здійснюються однією транзакцією.

Таке ж буферизація може бути досягнуто в сценарії (тобто поза буфером редагування векторного шару), а потім здійснюватися в одній транзакції шляхом виклику QgsDataProvider.changeAttributeValues()один раз, поза циклу.

Для цього також є зручний ярлик для останніх версій QGIS:

with edit(layer):
    for fid in fids:
        layer.changeAttributeValue(fid, idx, value)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.