通过Python插件在QGIS中编辑属性的速度


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
那似乎不对。您可以共享更多代码了吗?
内森·W

@NathanW我已经添加了完整的功能。这个想法是检查两层的相交,然后在找到相交时用另一层的属性更新一层。
Snorfalorpagus

您正在使用哪种数据类型?
内森·W

这两层都包含一个ESRI Shapefile(多边形)。layer_target具有905个要素(建筑物),layer_buffered具有1155个要素(开放空间),这些多边形具有代表不同缓冲区(100m,50m,20m,10m,5m)的重叠多边形,因此具有'distance'属性。
Snorfalorpagus

1
您如何访问数据?(即通过网络,传统磁盘,SSD)?单个写入操作的I / O开销是否很耗时?作为测试:您是否可以尝试将所有更改的属性缓冲在内存中,然后最后一次调用dataProvider.changeAttributeValues()。
Matthias Kuhn

Answers:


7

问题在于,每次调用都会QgsDataProvider.changeAttributeValues()发起一个具有所有相关开销的新事务(取决于数据提供者和系统配置)

当功能首先在层上更改时(如中所述QgsVectorLayer.changeAttributeValue()),所有更改都被缓存在内存中,这要快得多,然后最后在单个事务中提交。

可以在脚本内(即在矢量层编辑缓冲区之外)实现相同的缓冲,然后在QgsDataProvider.changeAttributeValues()循环外通过调用一次在一个事务中提交该缓冲。

在最新的QGIS版本中,还有一个方便的快捷方式:

with edit(layer):
    for fid in fids:
        layer.changeAttributeValue(fid, idx, value)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.