[FOSSGIS-Talk] Input/Output bei eigenem pyqgis processing tool
Christoph Jung
jagodki.cj at gmail.com
Mo Mär 22 21:02:15 CET 2021
Hallo Christiane,
Folgendes fällt mir auf:
In der Funktion „processAlgorithm“ beim Einlesen der Parameter gibt der Aufruf „self.parameterAsSink(…)“ ein Tupel[QgsFeatureSink, str] zurück, du hast es aber nur in eine Variable aufgelöst (auf folgender Seite in der Doku mal nach parameterAsSink suchen - die Permalinks funktionieren leider nicht richtig: https://qgis.org/pyqgis/master/core/QgsProcessingAlgorithm.html?highlight=self%20parameterassink#module-QgsProcessingAlgorithm).
Du hast in deinem Skript folgende Zeile stehen:
dest_id = self.parameterAsSink(parameters, self.OUTPUT, context, source_input.fields(), source_input.wkbType(), source_input.sourceCrs())
Meiner Meinung nach müsste es wie folgt lauten (Variablennamen sind natürlich frei wählbar):
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source_input.fields(), source_input.wkbType(), source_input.sourceCrs())
Ich habe das ganze nicht getestet, daher keine Garantie, dass dies wirklich die Lösung ist :)
Viele Grüße,
Christoph
> Am 22.03.2021 um 15:59 schrieb Chr. Enderle <enderlec at staff.uni-marburg.de>:
>
> Liebe Liste,
>
> ich habe mit QGIS 3.16 und der Python-Konsole ein lauffähiges Skript zustande gebracht, das eine Abwandlung des Standardtools "Differenz" ist. Das besondere ist, dass die Differenz nur zwischen Polygonen erfolgt, bei denen ein bestimmter Attribut-Wert im Input und im Overlay gleich ist. Dafür wird über den Input-Layer iteriert, bei dem jeder Attribut-Wert genau einmal vorkommt. Durch SelectByExpression werden aus dem Overlay-Layer die passenden Polygone ausgewählt und dann die Differenz durchgeführt. Anschließend werden alle produzierten Differenz-Layer per Standardtool "Vektorlayer zusammenführen" in einen einzigen neuen Layer geschrieben.
>
> Wie gesagt, in der Konsole funktioniert das prima.
>
> Ich brauche das ganze aber als Processing Plugin, damit ich es in ein Modell integrieren kann. Das Tutorial http://www.qgistutorials.com/de/docs/3/processing_python_plugin.html <http://www.qgistutorials.com/de/docs/3/processing_python_plugin.html> habe ich mir angesehen und für meine Zwecke umgesetzt. Problematisch ist für mich die Schnittstelle der Ein- und Ausgabe, weil ich den Code nicht verstehe, auch nicht mit der QGIS Python API Doku. Aktuell bricht das Tool mit der zweifachen Meldung ab:
>
> Kann Algorithmus nicht ausführen
> Konnte Ziellayer für OUTPUT nicht anlegen: ungültiger Wert
> Kann Algorithmus nicht ausführen
> Konnte Ziellayer für OUTPUT nicht anlegen: ungültiger Wert
>
> Im Anhang ist das Skript und das Protokoll.
>
> Über Unterstützung oder Hinweise auf Tutorials, die die Objekte und Methoden von pygis verständlich erklären, würde ich mich sehr freuen.
>
> Viele Grüße,
>
> Christiane
>
> --
> Christiane Enderle, MSc
> Philipps-Universität Marburg
> Fachbereich Geographie
> Deutschhausstr. 10
> 35037 Marburg
> Tel. +49 (0)6421-2822179
> Fax +49 (0)6421-2828950
>
> -------------- nächster Teil --------------
> QGIS-Version: 3.16.4-Hannover
> QGIS-Codeversion: 654e76b3cb
> Qt-Version: 5.11.2
> GDAL-Version: 3.1.4
> GEOS-Version: 3.8.1-CAPI-1.13.3
> PROJ-Version: Rel. 6.3.2, May 1st, 2020
> Verarbeite Algorithmus…
> Algorithmus Use Difference by common ID startet…
> Eingabeparameter:
> { 'INPUT' : 'X:/Extern/Maxima_Parts.shp', 'OUTPUT' : 'TEMPORARY_OUTPUT', 'OVERLAY' : 'X:/Extern/Loecher.shp' }
>
> Kann Algorithmus nicht ausführen
> Konnte Ziellayer für OUTPUT nicht anlegen: ungültiger Wert
> Kann Algorithmus nicht ausführen
> Konnte Ziellayer für OUTPUT nicht anlegen: ungültiger Wert
> Ausführung nach 0.01 Sekunden gescheitert
>
> Lade Ergebnis Layer
> Algorithmus 'Use Difference by common ID' beendet
> -------------- nächster Teil --------------
> # -*- coding: utf-8 -*-
>
> """
> /***************************************************************************
> DifferenceByID
> A QGIS plugin
> This plugin selects polygons and overlapping polygons inside of them by a common ID and performs a difference. Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
> -------------------
> begin : 2021-03-16
> copyright : (C) 2021 by Christiane Enderle
> email : enderlec at staff.uni-marburg.de
> ***************************************************************************/
>
> /***************************************************************************
> * *
> * 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. *
> * *
> ***************************************************************************/
> """
>
> __author__ = 'Christiane Enderle'
> __date__ = '2021-03-16'
> __copyright__ = '(C) 2021 by Christiane Enderle'
>
> # This will get replaced with a git SHA1 when you do a git archive
>
> __revision__ = '$Format:%H$'
>
> from qgis.PyQt.QtCore import QCoreApplication
> from qgis.core import (QgsProcessing,
> QgsFeatureSink,
> QgsProcessingAlgorithm,
> QgsProcessingParameterFeatureSource,
> QgsProcessingParameterFeatureSink,
> QgsVectorLayer)
> import processing
>
>
> class DifferenceByIDAlgorithm(QgsProcessingAlgorithm):
> """
> This is an example algorithm that takes a vector layer and
> creates a new identical one.
>
> It is meant to be used as an example of how to create your own
> algorithms and explain methods and variables used to do it. An
> algorithm like this will be available in all elements, and there
> is not need for additional work.
>
> All Processing algorithms should extend the QgsProcessingAlgorithm
> class.
> """
>
> # Constants used to refer to parameters and outputs. They will be
> # used when calling the algorithm from another algorithm, or when
> # calling from the QGIS console.
>
> OUTPUT = 'OUTPUT'
> INPUT = 'INPUT'
> OVERLAY = 'OVERLAY'
>
> def initAlgorithm(self, config):
> """
> 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,
> # Folgende Zeile gibt den Text ueber dem Eingabefeld an: self.tr('Input layer'),
> [QgsProcessing.TypeVectorAnyGeometry]
> )
> )
> self.addParameter(
> QgsProcessingParameterFeatureSource(
> self.OVERLAY,
> # Folgende Zeile gibt den Text ueber dem Eingabefeld an: self.tr('Overlay layer'),
> [QgsProcessing.TypeVectorAnyGeometry]
> )
> )
>
> # 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,
> # Folgende Zeile gibt den Text ueber dem Ausgabefeld an: self.tr('Output layer')
> )
> )
>
> def processAlgorithm(self, parameters, context, feedback):
> """
> Here is where the processing itself takes place.
> """
>
> # Retrieve the feature source and sink. The 'dest_id' variable is used
> # to uniquely identify the feature sink, and must be included in the
> # dictionary returned by the processAlgorithm function.
> source_input = self.parameterAsSource(parameters, self.INPUT, context)
> print ('input')
> source_overlay = self.parameterAsSource(parameters, self.OVERLAY, context)
> print ('overlay')
> dest_id = self.parameterAsSink(parameters, self.OUTPUT, context, source_input.fields(), source_input.wkbType(), source_input.sourceCrs())
> print ('output')
> maxima = QgsVectorLayer(self.INPUT, 'maxima', 'ogr')
> loecher = QgsVectorLayer(self.OVERLAY, 'loecher', 'ogr')
>
> # # Compute the number of steps to display within the progress bar and
> # # get features from source
> # total = 100.0 / maxima.featureCount() if maxima.featureCount() else 0
> # features = maxima.getFeatures()
>
> # for current, feature in enumerate(features):
> # # Stop the algorithm if cancel button has been clicked
> # if feedback.isCanceled():
> # break
>
> # # Add a feature in the sink
> # sink.addFeature(feature, QgsFeatureSink.FastInsert)
>
> # # Update the progress bar
> # feedback.setProgress(int(current * total))
> # Differenzbildung nach ID: difflayers = []
> print ('Es folgt die Differenzenbildung...')
>
> for m in maxima.getFeatures(): p = m['Part_ID']
> print ('Part_ID = ' + str(p))
> maxima.selectByExpression('\"Part_ID\" = ' + str(p), QgsVectorLayer.SetSelection)
> loecher.selectByExpression('\"Part_ID\" = ' + str(p), QgsVectorLayer.SetSelection)
> diff = processing.run("native:difference",
> {'INPUT': QgsProcessingFeatureSourceDefinition(maxima.id(), True), 'OVERLAY': QgsProcessingFeatureSourceDefinition(loecher.id(), True), 'OUTPUT':'memory:'}, is_child_algorithm=True,
> context=context,
> feedback=feedback
> )['OUTPUT']
> difflayers.append(diff)
> print ('Differenzbildung abgeschlossen')
>
> # -> FUNKTIONIERT, WIE ES SOLL! D.h. auch Polygone ohne Inseln werden ausgegeben (ohne Loecher)
>
>
> # Zusammenfuehren der Differenz-Layer:
> result = processing.run("native:mergevectorlayers", {'LAYERS':difflayers, 'OUTPUT': dest_id},
> is_child_algorithm=True,
> context=context,
> feedback=feedback)['Output']
> # QgsProject.instance().addMapLayer(result)
>
>
> # Loeschen der Differenz-Layer - sie werden nicht mehr gebraucht:
> # difflayers = QgsProject.instance().mapLayersByName('Differenz')
> # for d in difflayers: # QgsProject.instance().removeMapLayer(d.id())
> # Return the results of the algorithm. In this case our only result is
> # the feature sink which contains the processed features, but some
> # algorithms may return multiple feature sinks, calculated numeric
> # statistics, etc. These should all be included in the returned
> # dictionary, with keys matching the feature corresponding parameter
> # or output names.
> return {self.OUTPUT: result}
>
> 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 'Use Difference by common ID'
>
> def displayName(self):
> """
> Returns the translated algorithm name, which should be used for any
> user-visible display of the algorithm name.
> """
> return self.tr(self.name())
>
> def group(self):
> """
> Returns the name of the group this algorithm belongs to. This string
> should be localised.
> """
> return self.tr(self.groupId())
>
> 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 ''
>
> def tr(self, string):
> return QCoreApplication.translate('Processing', string)
>
> def createInstance(self):
> return DifferenceByIDAlgorithm()
> --
> ....................................................................
> FOSSGIS Veranstaltungen
> https://www.fossgis.de/news/fossgis-events/
>
> FOSSGIS e.V, der Verein zur Förderung von Freier Software aus dem
> GIS-Bereich und Freier Geodaten!
> https://www.fossgis.de/ https://twitter.com/fossgis_eV
>
> ____________________________________________________________________
> FOSSGIS-Talk-Liste mailing list
> FOSSGIS-Talk-Liste at fossgis.de
> https://lists.fossgis.de/mailman/listinfo/fossgis-talk-liste
Mehr Informationen über die Mailingliste FOSSGIS-Talk-Liste