نوشتن اسکریپت های پایتون برای چارچوب پردازش (QGIS3)
می توان اسکریپت های مستقل pyqgis نوشت که می توانند از طریق کنسول پایتون در QGIS اجرا شوند. با چند ترفند، می توانید اسکریپت های مستقل خود را از طریق Processing Framework اجرا کنید. این چندین مزیت دارد. اول، گرفتن ورودی کاربر و نوشتن فایلهای خروجی بسیار آسانتر است، زیرا Processing Framework رابط کاربری استاندارد شدهای را برای این موارد ارائه میدهد. دوم، داشتن اسکریپت شما در جعبه ابزار پردازش همچنین به آن اجازه می دهد تا بخشی از هر مدل پردازشی باشد یا به عنوان یک کار دسته ای با ورودی های متعدد اجرا شود. این آموزش نحوه نوشتن یک اسکریپت پایتون سفارشی را نشان می دهد که می تواند بخشی از چارچوب پردازش در QGIS باشد.
توجه داشته باشید
API پردازش به طور کامل در QGIS3 بازنگری شد. لطفاً برای بهترین شیوه ها و نکات به این راهنما مراجعه کنید .
نمای کلی کار
اسکریپت ما یک عملیات انحلال را بر اساس فیلدی که کاربر انتخاب کرده انجام می دهد. همچنین مقادیر یک فیلد دیگر را برای ویژگی های حل شده خلاصه می کند. در مثال، ما یک shapefile جهان را بر اساس یک CONTINENT
ویژگی حل می کنیم و فیلد را جمع می کنیم POP_EST
تا کل جمعیت در منطقه حل شده را محاسبه کنیم.
دریافت داده ها
ما از مجموعه داده Admin 0 – Countries از Natural Earth استفاده خواهیم کرد.
فایل شکل فایل Admin 0 – Country را دانلود کنید . .
منبع داده [NATURALEARTH]
برای راحتی کار، می توانید یک بسته جغرافیایی حاوی لایه فوق را مستقیماً از زیر دانلود کنید:
- در پنل مرورگر QGIS، دایرکتوری را که داده های دانلود شده خود را در آن ذخیره کرده اید پیدا کنید.
zip
ورودی را باز کنیدgpkg
وne_10m_admin_0_countries
لایه را انتخاب کنید. لایه را روی بوم بکشید.
- به بروید . روی دکمه Scripts در نوار ابزار کلیک کنید و Create New Script from Template را انتخاب کنید .
- این الگو حاوی تمام کدهای دیگ بخار است که برای شناسایی چارچوب پردازش به عنوان یک اسکریپت پردازش و مدیریت ورودی/خروجی ها لازم است. بیایید شروع به سفارشی سازی الگوی نمونه بر اساس نیازهای خود کنیم. ابتدا نام کلاس را از
ExampleProcessingAlgorithm
به تغییر دهیدDissolveProcessingAlgorithm
. این نام نیز باید درcreateInstance
روش به روز شود. یک docstring به کلاس اضافه کنید که توضیح دهد الگوریتم چه کار می کند.
- همانطور که به پایین اسکرول می کنید، روش هایی را خواهید دید که نام، گروه، توضیحات و غیره را اختصاص می دهند. به فیلمنامه مقادیر بازگشتی را برای متد نام
dissolve_with_sum
، متد displayName به ، متد گروه و متد groupId را به تغییر دهید . مقدار برگشتی متد shortHelpString را به توضیحاتی که برای کاربر ظاهر می شود تغییر دهید. روی دکمه Save کلیک کنید .Dissolve with Sum
scripts
- اسکریپت را نام گذاری کنید
dissolve_with_sum
و آن را در مکان پیش فرض در پوشه ذخیره کنید .
- حال ورودی های اسکریپت را تعریف می کنیم. این الگو قبلاً حاوی تعریفی از
INPUT
لایه برداری و یکOUTPUT
لایه است. ما ۲ ورودی جدید اضافه می کنیم که به کاربر اجازه می دهد یکDISSOLVE_FIELD
و یک را انتخاب کندSUM_FIELD
. یک import جدید در بالا و کد زیر را در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))
- یک گفتگوی Dissolve with Sum را با ورودی های جدید تعریف شده ما خواهید دید .
ne_10m_admin_0_countries
لایه را به عنوان لایه ورودی انتخاب کنید . از آنجایی که هر دو فیلد Dissolve Field و Sum Field بر اساس لایه ورودی فیلتر می شوند، از قبل با فیلدهای موجود از لایه ورودی پر می شوند. روی دکمه Close کلیک کنید .
- اکنون منطق سفارشی خود را برای پردازش داده ها در
processAlgorithm
متد تعریف می کنیم. این روش با دیکشنری به نام رد می شودparameters
. این شامل ورودی هایی است که کاربر انتخاب کرده است. روش های کمکی وجود دارد که به شما امکان می دهد این ورودی ها را بگیرید و اشیاء مناسب ایجاد کنید. ابتدا ورودی های خود را با استفاده از روش هاparameterAsSource
وparameterAsString
روش ها دریافت می کنیم. بعد می خواهیم یک ویژگی سینک ایجاد کنیم که در آن خروجی را بنویسیم. QGIS3 یک کلاس جدید به نام داردQgsFeatureSink
که روش ترجیحی برای ایجاد اشیایی است که می توانند ویژگی های جدید را بپذیرند. خروجی فقط به ۲ فیلد نیاز دارد – یکی برای مقدار فیلد حل شده و دیگری برای مجموع فیلد انتخاب شده.
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
به عنوان لایه ورودی ،CONTINENT
فیلد Dissolve و فیلد Sum راPOP_EST
انتخاب کنید . روی Run کلیک کنید .
- پس از اتمام پردازش، روی دکمه Close کلیک کنید و به پنجره اصلی QGIS بروید.
- لایه خروجی حل شده را با یک ویژگی برای هر قاره و کل جمعیت جمع آوری شده از کشورهای جداگانه متعلق به آن قاره را خواهید دید.
- یکی دیگر از مزیتهای نوشتن اسکریپت پردازش این است که روشهای درون چارچوب پردازش از انتخاب لایه آگاه هستند و بهطور خودکار ورودیهای شما را فیلتر میکنند تا فقط از ویژگیهای انتخابشده استفاده کنند. این اتفاق می افتد زیرا ما ورودی خود را به عنوان یک تعریف می کنیم
QgsProcessingParameterFeatureSource
. منبع ویژگی اجازه می دهد تا از هر شیئی که حاوی ویژگی های برداری باشد، نه فقط یک لایه برداری، استفاده کنید، بنابراین وقتی ویژگی های انتخاب شده در لایه شما وجود دارد و از پردازش می خواهد از ویژگی های انتخاب شده استفاده کند، ورودی به عنوان یکQgsProcessingFeatureSource
شی حاوی ویژگی های انتخاب شده به اسکریپت شما منتقل می شود. و نه لایه برداری کامل. در اینجا یک نمایش سریع از این قابلیت است. فرض کنید می خواهیم فقط قاره های خاصی را منحل کنیم. بیایید یک انتخاب با استفاده از ابزار Select by Expression ایجاد کنیم .
- عبارت زیر را برای انتخاب ویژگی های آمریکای شمالی و جنوبی وارد کنید و روی Select کلیک کنید .
"CONTINENT" = 'North America' OR "CONTINENT" = 'South America'
- ویژگی های انتخاب شده را خواهید دید که با رنگ زرد برجسته شده اند. اسکریپت را پیدا کنید
dissolve_with_sum
و برای اجرای آن دوبار کلیک کنید.
- در گفتگوی Dissolve with Sum ، لایه را
ne_10m_admin_0_countries
به عنوان لایه ورودی انتخاب کنید . این بار، مطمئن شوید که کادر Selected features only را علامت بزنید .SUBREGION
فیلد Dissolve و فیلد SumPOP_EST
را انتخاب کنید .
- پس از پایان پردازش، روی 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}
اگر می خواهید بازخورد بدهید یا تجربه خود را در مورد این آموزش به اشتراک بگذارید، لطفاً در زیر نظر دهید. (به حساب GitHub نیاز دارد)