19

Following the update to QGIS 3.0, it has become very difficult to find any information concerning the writing of processing scripts in QGIS 3.0.

@Underdark (see here ) has provided a basis for the skeleton. This code also seems to have been added in QGIS, when writing a new script from template (QGIS 3.0.2).

However, I couldn't find any way to help Python newbies like me to understand how to change that code, especially for the input and output layers.

My goal is to write a script taking 2 raster layers and a double as input, outputting two layers.

What would be the changes required to the example code to allow that?

For QGIS 2.x I would have used the following syntax :

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

From what I understand, the changes have to be made in the following procedure, but I'm not sure what to put in place.

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))

On May 16th, the QGIS Python API documentation was released. However it is still unclear for me how to use it here. (Which might very well be a lack of Python knowledge)

Kadir Şahbaz
78.6k57 gold badges260 silver badges407 bronze badges
asked May 14, 2018 at 9:16
3
  • 1
    Could you provide a sample of code you used for the same purpose on qgis 2.x.x. Documentation on qgis 3.x will be available here : docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/… as soon as it will be updated. Doc issues are tracked here : github.com/qgis/QGIS-Documentation/issues Commented May 15, 2018 at 7:35
  • Answer edited with the code example. Thanks for the links, I was already following the cookbook but unfortunately I couldn't find my answer there! Commented May 17, 2018 at 6:40
  • I read about the Qgis API docs, but I can't relate between that and @Underdark 's code. (see edit for the links) Commented May 18, 2018 at 8:52

1 Answer 1

33
+50

With the transition from QGIS2.x to QGIS3.x the whole processing framework has been reworked and large parts of it run now as C++ classes that you can interact with using Python. Unfortunately the simple parameter syntax for data/dataset IO are no longer valid. The new parameter structure is much more orientated after the builtin (Python-) Processing algorithms that you find preinstalled in the toolbox.

As I see, you already followed the description of the new algorithm structure by @underdark. But in order to adjust this structure for your requirements (raster layers, double input, etc.) you have to change the code at multiple locations in the script. I have coded a rough example with a short explanation for you (just an algorithm skeleton based on @underdarks example):

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

Which steps are done?

  1. Import all necessary classes.
  2. Define the algorithm as a class inheriting from QgsProcessingAlgorithm.
  3. First you have to declare the names of the input and output parameters as string variables (parameter names) of the algorithm class (ie. INPUT_RASTER_A = 'INPUT_RASTER_A') in order to reference your algorithm with the parameters provided by the processing framework.
  4. Add the methods that wire your algorithm to the processing toolbox gui and provide helpstrings, etc.
  5. Then you add the parameters of the processing framework. Those are defined as child classes of QgsProcessingParameterType - in the case of your algorithm: QgsProcessingParameterRasterLayer, QgsProcessingParameterNumber, and so on. You can consult the API entries (ie. QgsProcessingParameterRasterLayer) in order to pass the right arguments and construct the parameter objects.
  6. Pass the parameters alongside context and feedback objects to the processAlgorithm() method where you obtain the input datasets from the parameters at runtime (in this case QgsRasterLayer objects by using the parameterAsRasterLayer() method, etc.).
  7. Do your computation.
  8. Add the outputs to the results dictionary and return them as result of calling processAlgorithm().

I hope I could give you some insights on how to design your python algorithms in QGIS3. Whenever you are stuck, it is always helpful to look at how existing algorithms of the processing framework handle the parameters. You can have a look at them here.

answered May 28, 2018 at 15:37
7
  • 1
    Good writeup! Mind if I add it to github.com/qgis/QGIS/blob/master/doc/porting_processing.dox? Commented May 28, 2018 at 22:38
  • I would be honored if you add it to the qgis documentation. Please do so! Are there any prerequisites for contributing more python documentation for qgis3? I think this is essential for a broader user base in terms of scripters and programmers. Commented May 29, 2018 at 6:43
  • 1
    No prerequisites. It's actually quite easy to add to the official python cookbook via GitHub pull requests (all edits can even be done on the GitHub site: github.com/qgis/QGIS-Documentation/tree/master/source/docs/… ). Adding more examples to the official docs would also be very welcome! Commented May 29, 2018 at 7:38
  • 1
    Thanks for your answer! I was busy today, but I'll try to dig in tomorrow. It looks really promising. Commented May 29, 2018 at 15:26
  • 2
    That's definitely a great answer, thanks for the details and references. The link to the scripts on gitHub is a real goldmine! At first the QVariant declaration gave me an error, but when I retyped it in the editor and used the automatic completion, the error disappeared. It now really takes a big step to dive in the scripting, I hope it doesn't discourage new programmers. As more documentation is available, though, I hope it's becoming clearer! Commented May 31, 2018 at 9:22

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.