نوشتن اسکریپت های پایتون برای چارچوب پردازش (QGIS3) 

می توان اسکریپت های مستقل pyqgis نوشت که می توانند از طریق کنسول پایتون در QGIS اجرا شوند. با چند ترفند، می توانید اسکریپت های مستقل خود را از طریق Processing Framework اجرا کنید. این چندین مزیت دارد. اول، گرفتن ورودی کاربر و نوشتن فایل‌های خروجی بسیار آسان‌تر است، زیرا Processing Framework رابط کاربری استاندارد شده‌ای را برای این موارد ارائه می‌دهد. دوم، داشتن اسکریپت شما در جعبه ابزار پردازش همچنین به آن اجازه می دهد تا بخشی از هر مدل پردازشی باشد یا به عنوان یک کار دسته ای با ورودی های متعدد اجرا شود. این آموزش نحوه نوشتن یک اسکریپت پایتون سفارشی را نشان می دهد که می تواند بخشی از چارچوب پردازش در QGIS باشد.

توجه داشته باشید

API پردازش به طور کامل در QGIS3 بازنگری شد. لطفاً برای بهترین شیوه ها و نکات به این راهنما مراجعه کنید .

نمای کلی کار 

اسکریپت ما یک عملیات انحلال را بر اساس فیلدی که کاربر انتخاب کرده انجام می دهد. همچنین مقادیر یک فیلد دیگر را برای ویژگی های حل شده خلاصه می کند. در مثال، ما یک shapefile جهان را بر اساس یک CONTINENTویژگی حل می کنیم و فیلد را جمع می کنیم POP_ESTتا کل جمعیت در منطقه حل شده را محاسبه کنیم.

دریافت داده ها 

ما از مجموعه داده Admin 0 – Countries از Natural Earth استفاده خواهیم کرد.

فایل شکل فایل Admin 0 – Country را دانلود کنید . .

منبع داده [NATURALEARTH]

برای راحتی کار، می توانید یک بسته جغرافیایی حاوی لایه فوق را مستقیماً از زیر دانلود کنید:

ne_global.gpkg

  1. در پنل مرورگر QGIS، دایرکتوری را که داده های دانلود شده خود را در آن ذخیره کرده اید پیدا کنید. zipورودی را باز کنید gpkgو ne_10m_admin_0_countriesلایه را انتخاب کنید. لایه را روی بوم بکشید.

../../_images/1146.png

  1. به پردازش ‣ جعبه ابزار بروید . روی دکمه Scripts در نوار ابزار کلیک کنید و Create New Script from Template را انتخاب کنید .

../../_images/2125.png

  1. این الگو حاوی تمام کدهای دیگ بخار است که برای شناسایی چارچوب پردازش به عنوان یک اسکریپت پردازش و مدیریت ورودی/خروجی ها لازم است. بیایید شروع به سفارشی سازی الگوی نمونه بر اساس نیازهای خود کنیم. ابتدا نام کلاس را از ExampleProcessingAlgorithmبه تغییر دهید DissolveProcessingAlgorithm. این نام نیز باید در createInstanceروش به روز شود. یک docstring به کلاس اضافه کنید که توضیح دهد الگوریتم چه کار می کند.

../../_images/360.png

  1. همانطور که به پایین اسکرول می کنید، روش هایی را خواهید دید که نام، گروه، توضیحات و غیره را اختصاص می دهند. به فیلمنامه مقادیر بازگشتی را برای متد نامdissolve_with_sum ، متد displayName به ، متد گروه و متد groupId را به تغییر دهید . مقدار برگشتی متد shortHelpString را به توضیحاتی که برای کاربر ظاهر می شود تغییر دهید. روی دکمه Save کلیک کنید .Dissolve with Sumscripts

../../_images/434.png

  1. اسکریپت را نام گذاری کنید dissolve_with_sumو آن را در مکان پیش فرض در پوشه پروفایل ها ‣ پیش فرض ‣ پردازش ‣ اسکریپت ها ذخیره کنید .

../../_images/530.png

  1. حال ورودی های اسکریپت را تعریف می کنیم. این الگو قبلاً حاوی تعریفی از 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))

../../_images/6a.png../../_images/6b.png

  1. یک گفتگوی Dissolve with Sum را با ورودی های جدید تعریف شده ما خواهید دید . ne_10m_admin_0_countriesلایه را به عنوان لایه ورودی انتخاب کنید . از آنجایی که هر دو فیلد Dissolve Field و Sum Field بر اساس لایه ورودی فیلتر می شوند، از قبل با فیلدهای موجود از لایه ورودی پر می شوند. روی دکمه Close کلیک کنید .

../../_images/729.png

  1. اکنون منطق سفارشی خود را برای پردازش داده ها در 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())

../../_images/8a.png../../_images/8b.png

  1. اکنون ویژگی های ورودی را آماده می کنیم و یک فرهنگ لغت ایجاد می کنیم تا مقادیر منحصر به فرد را از 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)

../../_images/929.png

  1. در مرحله بعد، الگوریتم پردازش داخلی را native:dissolveدر لایه ورودی برای ایجاد هندسه های حل شده فراخوانی می کنیم. هنگامی که هندسه های حل شده را داشتیم، از طریق خروجی الگوریتم انحلال تکرار می کنیم و ویژگی های جدیدی ایجاد می کنیم تا به خروجی اضافه شوند. در پایان dest_idFeatureSink را به عنوان خروجی برمی گردانیم. اکنون فیلمنامه آماده است. روی دکمه 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}

../../_images/10a.png../../_images/10b.png

  1. در گفتگوی Dissolve with Sum ، ne_10m_admin_0_countriesبه عنوان لایه ورودی ، CONTINENTفیلد Dissolve و فیلد Sum راPOP_EST انتخاب کنید . روی Run کلیک کنید .

../../_images/1147.png

  1. پس از اتمام پردازش، روی دکمه Close کلیک کنید و به پنجره اصلی QGIS بروید.

../../_images/1238.png

  1. لایه خروجی حل شده را با یک ویژگی برای هر قاره و کل جمعیت جمع آوری شده از کشورهای جداگانه متعلق به آن قاره را خواهید دید.

../../_images/1336.png

  1. یکی دیگر از مزیت‌های نوشتن اسکریپت پردازش این است که روش‌های درون چارچوب پردازش از انتخاب لایه آگاه هستند و به‌طور خودکار ورودی‌های شما را فیلتر می‌کنند تا فقط از ویژگی‌های انتخاب‌شده استفاده کنند. این اتفاق می افتد زیرا ما ورودی خود را به عنوان یک تعریف می کنیم QgsProcessingParameterFeatureSource. منبع ویژگی اجازه می دهد تا از هر شیئی که حاوی ویژگی های برداری باشد، نه فقط یک لایه برداری، استفاده کنید، بنابراین وقتی ویژگی های انتخاب شده در لایه شما وجود دارد و از پردازش می خواهد از ویژگی های انتخاب شده استفاده کند، ورودی به عنوان یک QgsProcessingFeatureSourceشی حاوی ویژگی های انتخاب شده به اسکریپت شما منتقل می شود. و نه لایه برداری کامل. در اینجا یک نمایش سریع از این قابلیت است. فرض کنید می خواهیم فقط قاره های خاصی را منحل کنیم. بیایید یک انتخاب با استفاده از ابزار Select by Expression ایجاد کنیم .

../../_images/1433.png

  1. عبارت زیر را برای انتخاب ویژگی های آمریکای شمالی و جنوبی وارد کنید و روی Select کلیک کنید .
"CONTINENT" = 'North America' OR "CONTINENT" = 'South America'

../../_images/1529.png

  1. ویژگی های انتخاب شده را خواهید دید که با رنگ زرد برجسته شده اند. اسکریپت را پیدا کنید dissolve_with_sumو برای اجرای آن دوبار کلیک کنید.

../../_images/1628.png

  1. در گفتگوی Dissolve with Sum ، لایه را ne_10m_admin_0_countriesبه عنوان لایه ورودی انتخاب کنید . این بار، مطمئن شوید که کادر Selected features only را علامت بزنید . SUBREGIONفیلد Dissolve و فیلد SumPOP_EST را انتخاب کنید .

../../_images/1729.png

  1. پس از پایان پردازش، روی Close کلیک کنید و به پنجره اصلی QGIS برگردید. شما متوجه یک لایه جدید می شوید که فقط ویژگی های انتخاب شده حل شده است. روی دکمه Identify کلیک کنید و روی یک ویژگی کلیک کنید تا بررسی و تأیید شود که اسکریپت درست کار می کند.

../../_images/1825.pngدر زیر اسکریپت کامل برای مرجع آورده شده است. شما می توانید آن را مطابق با نیاز خود تغییر دهید.

# -*- 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 نیاز دارد)


دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

خانهدربارهتماسارتباط با ما