Pisanie skryptu przetwarzania Pythona za pomocą QGIS 3.0

17

Po aktualizacji QGIS 3.0 bardzo trudno było znaleźć jakiekolwiek informacje dotyczące pisania skryptów przetwarzających w QGIS 3.0.

@Underdark (patrz tutaj ) zapewnił podstawę szkieletu. Wydaje się, że ten kod został również dodany w QGIS podczas pisania nowego skryptu z szablonu (QGIS 3.0.2).

Jednak nie mogłem znaleźć sposobu, aby pomóc początkującym użytkownikom Python, takim jak ja, zrozumieć, jak zmienić ten kod, szczególnie w przypadku warstw wejściowych i wyjściowych.

Moim celem jest napisanie skryptu zawierającego 2 warstwy rastrowe i podwójną jako dane wejściowe, generując dwie warstwy.

Jakie byłyby wymagane zmiany w przykładowym kodzie, aby to umożliwić?

W przypadku QGIS 2.x użyłbym następującej składni:

##Layer1=raster
##Layer2=raster 
##myDouble=Double
##OutLayer1=output raster
##OutLayer2=output raster   

Z tego, co rozumiem, zmiany należy wprowadzić w następującej procedurze, ale nie jestem pewien, co wprowadzić.

def initAlgorithm(self, config=None):
    self.addParameter(QgsProcessingParameterFeatureSource(
        self.INPUT,
        self.tr("Input layer"),
        [QgsProcessing.TypeVectorAnyGeometry]))
    self.addParameter(QgsProcessingParameterFeatureSink(
        self.OUTPUT,
        self.tr("Output layer"),
        QgsProcessing.TypeVectorAnyGeometry))

16 maja opublikowano dokumentację QGIS Python API . Jednak nadal nie jest dla mnie jasne, jak go tutaj używać. (Co może równie dobrze być brakiem wiedzy w języku Python)

Kantan
źródło
1
Czy możesz podać próbkę kodu użytego do tego samego celu w qgis 2.xx Dokumentacja na qgis 3.x będzie dostępna tutaj: docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/... jak tylko będzie to możliwe zaktualizowane. Kwestie doc są śledzone tutaj: github.com/qgis/QGIS-Documentation/issues
nono
Odpowiedź edytowana za pomocą przykładu kodu. Dzięki za linki, już śledziłem książkę kucharską, ale niestety nie mogłem znaleźć tam odpowiedzi!
Kantan
Czytałem o dokumentacji API Qgis, ale nie mogę odnosić się między tym a kodem @Underdark. (patrz edycja linków)
Kantan

Odpowiedzi:

29

Wraz z przejściem z QGIS2.x do QGIS3.x cała struktura przetwarzania została przerobiona, a jej duże części działają teraz jako klasy C ++, z którymi można współdziałać za pomocą Pythona. Niestety prosta składnia parametrów we / wy danych / zestawu danych nie jest już ważna. Nowa struktura parametrów jest znacznie bardziej zorientowana po wbudowanych algorytmach przetwarzania (Python), które znajdują się w preinstalowanym zestawie narzędzi.

Jak widzę, śledziłeś już opis nowej struktury algorytmu przez @underdark. Aby jednak dostosować tę strukturę do swoich wymagań (warstwy rastrowe, podwójne wprowadzanie itp.), Musisz zmienić kod w wielu miejscach skryptu. Zakodowałem szorstki przykład z krótkim wyjaśnieniem (tylko szkielet algorytmu oparty na przykładzie @underdarks):

from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing, QgsProcessingAlgorithm, 
QgsProcessingParameterRasterLayer,QgsProcessingParameterNumber, 
QgsProcessingParameterRasterDestination)

class RasterAlg(QgsProcessingAlgorithm):
    INPUT_RASTER_A = 'INPUT_RASTER_A'
    INPUT_RASTER_B = 'INPUT_RASTER_B'
    INPUT_DOUBLE = 'INPUT_DOUBLE'
    OUTPUT_RASTER_A = 'OUTPUT_RASTER_A'
    OUTPUT_RASTER_B = 'OUTPUT_RASTER_B'

    def __init__(self):
        super().__init__()

    def name(self):
        return "RasterAlg"

    def tr(self, text):
        return QCoreApplication.translate("RasterAlg", text)

    def displayName(self):
        return self.tr("RasterAlg script")

    def group(self):
        return self.tr("RasterAlgs")

    def groupId(self):
        return "RasterAlgs"

    def shortHelpString(self):
        return self.tr("RasterAlg script without logic")

    def helpUrl(self):
        return "https://qgis.org"

    def createInstance(self):
        return type(self)()

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterRasterLayer(
            self.INPUT_RASTER_A,
            self.tr("Input Raster A"), None, False))
        self.addParameter(QgsProcessingParameterRasterLayer(
            self.INPUT_RASTER_B,
            self.tr("Input Raster B"), None, False))
        self.addParameter(QgsProcessingParameterNumber(
            self.INPUT_DOUBLE, 
            self.tr("Input Double"), 
            QgsProcessingParameterNumber.Double,
            QVariant(1.0)))
        self.addParameter(QgsProcessingParameterRasterDestination(
            self.OUTPUT_RASTER_A,
            self.tr("Output Raster A"),
            None, False))
        self.addParameter(QgsProcessingParameterRasterDestination(
            self.OUTPUT_RASTER_B,
            self.tr("Output Raster B"),
            None, False))

    def processAlgorithm(self, parameters, context, feedback):
        raster_a = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER_A, context)
        raster_b = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER_B, context)
        double_val = self.parameterAsDouble(parameters, self.INPUT_DOUBLE,context)
        output_path_raster_a = self.parameterAsOutputLayer(parameters, self.OUTPUT_RASTER_A, context)
        output_path_raster_b = self.parameterAsOutputLayer(parameters, self.OUTPUT_RASTER_B, context)

        #DO SOME CALCULATION

        results = {}
        results[self.OUTPUT_RASTER_A] = output_path_raster_a
        results[self.OUTPUT_RASTER_B] = output_path_raster_b
        return results

Jakie kroki są zrobione?

  1. Zaimportuj wszystkie niezbędne klasy.
  2. Zdefiniuj algorytm jako klasę dziedziczącą po QgsProcessingAlgorithm.
  3. Najpierw musisz zadeklarować nazwy parametrów wejściowych i wyjściowych jako zmienne łańcuchowe (nazwy parametrów) klasy algorytmu (tj. INPUT_RASTER_A = 'INPUT_RASTER_A') W celu odniesienia swojego algorytmu do parametrów dostarczonych przez strukturę przetwarzania.
  4. Dodaj metody, które łączą algorytm z interfejsem GUI przybornika przetwarzania i dostarczają pomocy itp.
  5. Następnie dodajesz parametry struktury przetwarzania. Są one definiowane jako klasach potomnych QgsProcessingParameterType- w przypadku swojego algorytmu: QgsProcessingParameterRasterLayer, QgsProcessingParameterNumber, i tak dalej. Możesz przejrzeć wpisy API (tj. QgsProcessingParameterRasterLayer), Aby przekazać odpowiednie argumenty i zbudować obiekty parametrów.
  6. Przekaż parametry obok contexti feedbackobiekty do processAlgorithm()metody, w której uzyskujesz wejściowe zestawy danych z parametrów w czasie wykonywania (w tym przypadku obiekty QgsRasterLayer przy użyciu parameterAsRasterLayer()metody itp.).
  7. Wykonaj swoje obliczenia.
  8. Dodaj dane wyjściowe do słownika wyników i zwróć je w wyniku wywołania processAlgorithm().

Mam nadzieję, że mógłbym dać ci wgląd w sposób projektowania algorytmów Python w QGIS3. Ilekroć utkniesz, zawsze pomocne jest sprawdzenie, jak istniejące algorytmy struktury przetwarzania przetwarzają parametry. Możesz je zobaczyć tutaj .

root676
źródło
1
Dobry napis! Czy mogę dodać to do github.com/qgis/QGIS/blob/master/doc/porting_processing.dox ?
ndawson
Byłbym zaszczycony, jeśli dodasz go do dokumentacji qgis. Zrób tak, proszę! Czy są jakieś warunki wstępne, aby wnieść więcej dokumentacji do Pythona dla qgis3? Myślę, że jest to niezbędne dla szerszej bazy użytkowników w zakresie skryptów i programistów.
root676
1
Brak wymagań wstępnych. W rzeczywistości dość łatwo jest dodać do oficjalnej książki kucharskiej Pythona za pośrednictwem żądań ściągania GitHub (wszystkie zmiany można nawet wprowadzić na stronie GitHub: github.com/qgis/QGIS-Documentation/tree/master/source/docs/… ). Bardzo mile widziane byłoby dodanie większej liczby przykładów do oficjalnych dokumentów!
ndawson
1
Dziękuję za odpowiedź! Byłem dzisiaj zajęty, ale spróbuję wkopać się jutro. Wygląda naprawdę obiecująco.
Kantan
2
To zdecydowanie świetna odpowiedź, dziękuję za szczegóły i referencje. Link do skryptów na gitHub to prawdziwa kopalnia złota! Na początku deklaracja QVariant dała mi błąd, ale kiedy ponownie wpisałem ją w edytorze i użyłem automatycznego uzupełniania, błąd zniknął. Teraz naprawdę robi duży krok do nurkowania w skryptach, mam nadzieję, że nie zniechęci to nowych programistów. Ponieważ dostępnych jest więcej dokumentacji, mam nadzieję, że stanie się ona coraz jaśniejsza!
Kantan