编写用于处理框架的Python脚本(QGIS3)¶
独立的 Pyqgis 程式码可以从 QGIS 中的 Python 主控台执行,不过只要经过些微调整,它也可以在处理框架中执行。这么做的好处有两个,首先因为处理框架提供了标准化的介面,所以指定输出入档变容易了;再来就是你可以指定你的脚本成为处理建模中的一部份,这样一来就可以使用批次执行一次处理许多输入档。本教学将示范如何撰写可以当成 QGIS 中处理框架的一部份的 Python 程式码。
注解
在QGIS3中彻底修改了Processing API。请 参阅本指南 了解最佳做法和提示。
内容说明¶
本脚本会依照使用者挑选的属性,进行融合(Dissolve)操作;除此之外,还会把所有被融合的图征的另一个属性值进行加总。在本例中,我们要依照全球范围的 shapefile 的 CONTINENT
属性进行融合,然后依照 POP_EST
属性加总估计融合后的新区域的总人口。
取得资料¶
我们要使用 Natural Earth 提供的 Admin 0 - 国界 资料集。
下载 Admin 0 - 国界(Countries)shapefile。
资料来源 [NATURALEARTH]
为了方便起见,你也可以直接用下面的连结下载包含此图层的地理数据集:
操作流程¶
在QGIS浏览器面板中,找到保存下载数据的目录。展开
zip
或gpkg
条目,然后选择ne_10m_admin_0_countries
层。将图层拖到画布上。
转到
。单击工具栏上的脚本
按钮,然后选择 Create New Script from Template。
该模板包含处理框架将其识别为处理脚本并管理输入/输出所需的所有样板代码。让我们开始根据需要定制示例模板。首先将类名称从
ExampleProcessingAlgorithm
更改为DissolveProcessingAlgorithm
。此名称也需要在createInstance
方法中进行更新。在该类中添加一个文档字符串,以说明算法的作用。
向下滚动时,您将看到为脚本分配名称,组,描述等的方法。 将 name 方法的返回值更改为
dissolve_with_sum
,将displayName
方法更改为Sum溶解
,将groupgroup
方法和 groupId 方法更改为scripts。将 shortHelpString 方法的返回值更改为将显示给用户的描述。点击 Save 按钮。
将脚本命名为
dissolve_with_sum
并将其保存在 folder默认位置。
现在,我们将定义脚本的输入。该模板已经包含
输入
矢量层和输出
层的定义。我们将添加2个新输入,允许用户选择DISSOLVE_FIELD
和SUM_FIELD
。在initAlgorithm
方法的顶部和下面的代码中添加一个新的导入。单击 Run 按钮以预览更改。
from qgis.core import QgsProcessingParameterField
self.addParameter(
QgsProcessingParameterField(
self.DISSOLVE_FIELD,
'Choose Dissolve Field',
'',
self.INPUT))
self.addParameter(
QgsProcessingParameterField(
self.SUM_FIELD,
'Choose Sum Field',
'',
self.INPUT))
您将看到一个带有我们新定义的输入的 Dissum with Sum 对话框。选择
ne_10m_admin_0_countries
层作为 Input layer。由于 Dissolve Field 和 Sum Fields 都是基于输入层进行过滤的,因此它们将被输入层中的现有字段预先填充。点击 Close 按钮。
现在,我们在
processAlgorithm
方法中定义用于处理数据的自定义逻辑。该方法通过了一个名为parameters
的字典。它包含用户已选择的输入。有一些帮助程序方法,使您可以接受这些输入并创建适当的对象。我们首先使用parameterAsSource
和parameterAsString
方法获取输入。接下来,我们要创建一个特征接收器,在其中写入输出。QGIS3有一个名为QgsFeatureSink
的新类,它是创建可以接受新功能的对象的首选方法。输出仅需要2个字段-一个用于溶解字段的值,另一个用于所选字段的总和。
from PyQt5.QtCore import QVariant
from qgis.core import QgsField, QgsFields
source = self.parameterAsSource(
parameters,
self.INPUT,
context)
dissolve_field = self.parameterAsString(
parameters,
self.DISSOLVE_FIELD,
context)
sum_field = self.parameterAsString(
parameters,
self.SUM_FIELD,
context)
fields = QgsFields()
fields.append(QgsField(dissolve_field, QVariant.String))
fields.append(QgsField('SUM_' + sum_field, QVariant.Double))
(sink, dest_id) = self.parameterAsSink(
parameters,
self.OUTPUT,
context, fields, source.wkbType(), source.sourceCrs())
现在,我们将准备输入功能,并创建一个字典来保存dissolve_field中的唯一值和sum_field中的值之和。注意使用
feedback.pushInfo()
方法与用户进行状态通信。
feedback.pushInfo('Extracting unique values from dissolve_field and computing sum')
features = source.getFeatures()
unique_values = set(f[dissolve_field] for f in features)
# Get Indices of dissolve field and sum field
dissolveIdx = source.fields().indexFromName(dissolve_field)
sumIdx = source.fields().indexFromName(sum_field)
# Find all unique values for the given dissolve_field and
# sum the corresponding values from the sum_field
sum_unique_values = {}
attrs = [{dissolve_field: f[dissolveIdx], sum_field: f[sumIdx]} for f in source.getFeatures()]
for unique_value in unique_values:
val_list = [ f_attr[sum_field] for f_attr in attrs if f_attr[dissolve_field] == unique_value]
sum_unique_values[unique_value] = sum(val_list)
接下来,我们将在输入层上将内置处理算法称为
native:dissolve
以生成溶解的几何体。一旦有了可溶的几何形状,我们便会遍历可溶算法的输出,并创建要添加到输出中的新特征。最后,我们返回dest_id
FeatureSink作为输出。现在脚本已准备就绪。点击 Run 按钮。
注解
注意使用 parameters [self.INPUT]
直接从参数字典中获取输入层,而没有将其定义为源。由于我们无需对输入对象进行任何处理就将其传递给算法,因此不必将其定义为源。
from qgis.core import QgsFeature
# Running the processing dissolve algorithm
feedback.pushInfo('Dissolving features')
dissolved_layer = processing.run("native:dissolve", {
'INPUT': parameters[self.INPUT],
'FIELD': dissolve_field,
'OUTPUT': 'memory:'
}, context=context, feedback=feedback)['OUTPUT']
# Read the dissolved layer and create output features
for f in dissolved_layer.getFeatures():
new_feature = QgsFeature()
# Set geometry to dissolved geometry
new_feature.setGeometry(f.geometry())
# Set attributes from sum_unique_values dictionary that we had computed
new_feature.setAttributes([f[dissolve_field], sum_unique_values[f[dissolve_field]]])
sink.addFeature(new_feature, QgsFeatureSink.FastInsert)
return {self.OUTPUT: dest_id}
在 Dissolve with Sum 对话框中,选择
ne_10m_admin_0_countries
作为 Input layer,以CONTINENT
作为 Dissolve field 并且POP_EST
作为 Sum field 字段。点击 Run。
处理完成后,单击 Close 按钮,然后切换到QGIS主窗口。
您将看到每个大陆具有一个要素的已分解输出层,以及该大陆各个国家的总人口合计。
编写处理脚本的另一个优点是,处理框架中的方法知道图层选择,并自动过滤输入以仅使用所选功能。发生这种情况是因为我们将输入定义为
QgsProcessingParameterFeatureSource
。特征源允许使用包含向量特征的任何对象,而不仅仅是向量层,因此当层中有选定特征并要求Processing使用选定特征时,输入将作为QgsProcessingFeatureSource
传递到脚本中 包含所选要素而不是完整矢量层的对象。这是此功能的快速演示。假设我们只想溶解某些大洲。让我们使用表达式工具的 Select feature by Expression 来创建选择。
输入以下的表达式,按下 Select 之后,就可把南北美洲选取起来。
"CONTINENT" = 'North America' OR "CONTINENT" = 'South America'
您将看到以黄色突出显示的所选功能。找到
dissolve_with_sum
脚本并双击运行它。
在 Dissolve with Sum 对话框中,选择
ne_10m_admin_0_countries
作为 Input layer。 这次,请确保选中 Selected features only 框。 选择SUBSUBION作为Dissolve字段,选择POP_EST
作为 Sum field。
处理完成后,单击 Close,然后切换回QGIS主窗口。您会注意到一个新图层,其中只有选定的特征被分解。单击 Identify 按钮,然后单击一项功能以检查并验证脚本是否正常运行。
以下放上完整的脚本供各位参考,你也可以依照个人需求编辑调整此档案。
# -*- coding: utf-8 -*-
"""
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing,
QgsFeatureSink,
QgsFeature,
QgsField,
QgsFields,
QgsProcessingException,
QgsProcessingAlgorithm,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterField,
)
import processing
class DissolveProcessingAlgorithm(QgsProcessingAlgorithm):
"""
Dissolve algorithm that dissolves features based on selected
attribute and summarizes the selected field by cumputing the
sum of dissolved features.
"""
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
DISSOLVE_FIELD = 'dissolve_field'
SUM_FIELD = 'sum_field'
def tr(self, string):
"""
Returns a translatable string with the self.tr() function.
"""
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return DissolveProcessingAlgorithm()
def name(self):
"""
Returns the algorithm name, used for identifying the algorithm. This
string should be fixed for the algorithm, and must not be localised.
The name should be unique within each provider. Names should contain
lowercase alphanumeric characters only and no spaces or other
formatting characters.
"""
return 'dissolve_with_sum'
def displayName(self):
"""
Returns the translated algorithm name, which should be used for any
user-visible display of the algorithm name.
"""
return self.tr('Dissolve with Sum')
def group(self):
"""
Returns the name of the group this algorithm belongs to. This string
should be localised.
"""
return self.tr('scripts')
def groupId(self):
"""
Returns the unique ID of the group this algorithm belongs to. This
string should be fixed for the algorithm, and must not be localised.
The group id should be unique within each provider. Group id should
contain lowercase alphanumeric characters only and no spaces or other
formatting characters.
"""
return 'scripts'
def shortHelpString(self):
"""
Returns a localised short helper string for the algorithm. This string
should provide a basic description about what the algorithm does and the
parameters and outputs associated with it..
"""
return self.tr("Dissolves selected features and creates and sums values of features that were dissolved")
def initAlgorithm(self, config=None):
"""
Here we define the inputs and output of the algorithm, along
with some other properties.
"""
# We add the input vector features source. It can have any kind of
# geometry.
self.addParameter(
QgsProcessingParameterFeatureSource(
self.INPUT,
self.tr('Input layer'),
[QgsProcessing.TypeVectorAnyGeometry]
)
)
self.addParameter(
QgsProcessingParameterField(
self.DISSOLVE_FIELD,
'Choose Dissolve Field',
'',
self.INPUT))
self.addParameter(
QgsProcessingParameterField(
self.SUM_FIELD,
'Choose Sum Field',
'',
self.INPUT))
# We add a feature sink in which to store our processed features (this
# usually takes the form of a newly created vector layer when the
# algorithm is run in QGIS).
self.addParameter(
QgsProcessingParameterFeatureSink(
self.OUTPUT,
self.tr('Output layer')
)
)
def processAlgorithm(self, parameters, context, feedback):
"""
Here is where the processing itself takes place.
"""
source = self.parameterAsSource(
parameters,
self.INPUT,
context
)
dissolve_field = self.parameterAsString(
parameters,
self.DISSOLVE_FIELD,
context)
sum_field = self.parameterAsString(
parameters,
self.SUM_FIELD,
context)
fields = QgsFields()
fields.append(QgsField(dissolve_field, QVariant.String))
fields.append(QgsField('SUM_' + sum_field, QVariant.Double))
(sink, dest_id) = self.parameterAsSink(
parameters,
self.OUTPUT,
context, fields, source.wkbType(), source.sourceCrs())
# Create a dictionary to hold the unique values from the
# dissolve_field and the sum of the values from the sum_field
feedback.pushInfo('Extracting unique values from dissolve_field and computing sum')
features = source.getFeatures()
unique_values = set(f[dissolve_field] for f in features)
# Get Indices of dissolve field and sum field
dissolveIdx = source.fields().indexFromName(dissolve_field)
sumIdx = source.fields().indexFromName(sum_field)
# Find all unique values for the given dissolve_field and
# sum the corresponding values from the sum_field
sum_unique_values = {}
attrs = [{dissolve_field: f[dissolveIdx], sum_field: f[sumIdx]}
for f in source.getFeatures()]
for unique_value in unique_values:
val_list = [ f_attr[sum_field]
for f_attr in attrs if f_attr[dissolve_field] == unique_value]
sum_unique_values[unique_value] = sum(val_list)
# Running the processing dissolve algorithm
feedback.pushInfo('Dissolving features')
dissolved_layer = processing.run("native:dissolve", {
'INPUT': parameters[self.INPUT],
'FIELD': dissolve_field,
'OUTPUT': 'memory:'
}, context=context, feedback=feedback)['OUTPUT']
# Read the dissolved layer and create output features
for f in dissolved_layer.getFeatures():
new_feature = QgsFeature()
# Set geometry to dissolved geometry
new_feature.setGeometry(f.geometry())
# Set attributes from sum_unique_values dictionary that we had computed
new_feature.setAttributes([f[dissolve_field], sum_unique_values[f[dissolve_field]]])
sink.addFeature(new_feature, QgsFeatureSink.FastInsert)
return {self.OUTPUT: dest_id}