[GIS] pyqgis – post-processing of layer output of QgsProcessingAlgorithm

pyqgispyqgis-3qgis-processing

While extending a plugin with a Processing Provider and a set of algorithms I mostly followed the defaults of Plugin Builder 3. So, the basic code is smth along the lines of

class ORSisochronesAlgo(QgsProcessingAlgorithm):

    def initAlgorithm(self, configuration, p_str=None, Any=None, *args, **kwargs):

        # add all input parameters

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                'OUTPUT',
                'Output layer'
            )
        )

    def processAlgorithm(self, parameters, context, feedback):

        source = self.parameterAsSource(parameters, self.INPUT, context)
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                context, source.fields(), source.wkbType(), source.sourceCrs())

        features = source.getFeatures()

        for feature in features:
            new_feature = QgsFeature()

            # Some API calls and processing to populate new feature

            sink.addFeature(new_feature, QgsFeatureSink.FastInsert)

        return {self.OUTPUT: dest_id}

So, the output layer is automatically handled and added to the map. Very nice!

But now: How do I post-process that output layer within the Processing Algorithm? Sounds weird maybe, but here are 2 scenarios:

  • Depending on the value of a Boolean input parameter, I'd like to be able to calculate geometric differences of the output features. For this to work, the layer has to be built first. And then handled in some sort of post-processing.

  • Also, I'd like to be able to customize the style of the output layer.

There is the postProcessAlgorithm() method for QgsProcessingAlgorithm, but I'm not so sure how to work with this.. It doesn't seem to take parameters as input, no idea how to access the output layer.

Being able to do this would make the plugin so much more user-friendly.

Best Answer

Just to be more elaborate than the comment.

Basically, the postProcessAlgorithm() does what it says. It also returns a value map, including the OUTPUT. However, you need to look up the layer in the algorithm's context with QgsProcessingUtils.mapLayerFromString(). For that to work, you have to save a reference to the original output layer from processAlgorithm():

class ORSisochronesAlgo(QgsProcessingAlgorithm):
    dest_id = None  # Save a reference to the output layer id

    def initAlgorithm(self, configuration, p_str=None, Any=None, *args, **kwargs):

        # add all input parameters

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                'OUTPUT',
                'Output layer'
            )
        )

    def processAlgorithm(self, parameters, context, feedback):

        source = self.parameterAsSource(parameters, self.INPUT, context)
        (sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                context, source.fields(), source.wkbType(), source.sourceCrs())

        features = source.getFeatures()

        for feature in features:
            new_feature = QgsFeature()

            # Some API calls and processing to populate new feature

            sink.addFeature(new_feature, QgsFeatureSink.FastInsert)

        return {self.OUTPUT: dest_id}

    def postProcessAlgorithm(self, context, feedback):

        processed_layer = QgsProcessingUtils.mapLayerFromString(self.dest_id, context)

        # Do smth with the layer, e.g. style it

        return {self.OUT: self.dest_id}
Related Question