[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