3

I've created a processing script in QGIS 3.10, but can't seem to achieve the last step of styling the processed layer. My script follows the new easy processing script format that have come back since version 3.6.

I've referenced QGIS 3.4, how to add .qml style to sink in processing, but my script doesn't seem to run the postProcessAlgorithm bit below.

from math import *
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.processing import alg
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsFeature,
                       QgsVectorLayer,
                       QgsGeometry,
                       QgsPointXY,
                       QgsField,
                       QgsFields,
                       QgsWkbTypes,
                       QgsProject,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink,
                       QgsProcessingParameterField,
                       QgsVectorFileWriter
                       )
from qgis.utils import iface
import processing

@alg(name="distance_buffer", label=alg.tr("Distance Buffer"), group="base", group_label=alg.tr("base"))
@alg.input(type=alg.SOURCE, name="INPUT", label="Input layer")
@alg.input(type=alg.SINK, name="OUTPUT", label="Distance Buffers")
@alg.input(type=alg.FILE, name='STYLEFILE', label='Style file', default='C:/Desktop/Buffer.qml')

def processAlgorithm(instance, parameters, context, feedback, inputs):
    """
    Create accurate distance buffers with reprojection.
    """
    source = instance.parameterAsSource(parameters, "INPUT", context)

    if source is None:
        raise QgsProcessingException(instance.invalidSourceError(parameters, "INPUT"))

    fields = QgsFields()
    fields.append(QgsField("Distance", QVariant.Int))
    fields.append(QgsField("Label", QVariant.String))

    (sink, dest_id) = instance.parameterAsSink(parameters, "OUTPUT", context,
                                              fields,
                                              QgsWkbTypes.Polygon,
                                              source.sourceCrs()
                                          )

    if sink is None:
        raise QgsProcessingException(instance.invalidSinkError(parameters, "OUTPUT"))

    total = 100.0 / source.featureCount() if source.featureCount() else 0
    selection = source.getFeatures()
    for current, feature in enumerate(selection):
        if feedback.isCanceled():
            break
        for select in selection:
            geom = select.geometry()
            start_x = geom.asPoint().x()
            start_y = geom.asPoint().y()
            sides = 64
            radius = 6378137.0 # meters
            distance = [250, 500, 1000, 2000, 3000, 4000, 5000, 10000, 15000, 20000, 25000, 30000, 40000, 50000]
            feedback.pushInfo('Creating layer and adding fields.')
            # Create new layer.
            vl = QgsVectorLayer("Polygon", "Distance Buffers", "memory")
            # Add fields.
            pr = vl.dataProvider()
            pr.addAttributes(fields)
            vl.updateFields()
            for i in range(len(distance)):
                feedback.pushInfo('Creating feature.')
                points = []
                dist = distance[i]
                degrees = 0
                while degrees <= 360:
                    degrees = degrees + 360 / sides
                    start_lon = start_x * pi / 180
                    start_lat = start_y * pi / 180
                    bearing = degrees * pi / 180
                    end_lat = asin((sin(start_lat) * cos(dist / radius)) + (cos(start_lat) * sin(dist / radius) * cos(bearing)))
                    end_lon = start_lon + atan2(sin(bearing) * sin(dist / radius) * cos(start_lat),
                                                cos(dist / radius) - (sin(start_lat) * sin(end_lat)))
                    points.append(QgsPointXY(end_lon * 180 / pi, end_lat * 180 / pi))
                    feat_name = str(distance[i])
                    if distance[i] < 1000:
                        label = str(distance[i]) + "m"
                    else:
                        label = str(distance[i]/1000) + "km"
                feedback.pushInfo('Adding feature to layer.')
                # Add calculated features.
                feat = QgsFeature()
                geometry = QgsGeometry.fromPolygonXY([points])
                feat.setGeometry(geometry)
                feat.setAttributes([feat_name,label])
                sink.addFeature(feat, QgsFeatureSink.FastInsert)

        feedback.setProgress(int(current * total))

    return {"OUTPUT": dest_id}

def postProcessAlgorithm(self, context, feedback):
    style = instance.parameterAsString(parameters, "STYLEFILE", context)
    processed_layer = QgsProcessingUtils.mapLayerFromString(dest_id, context)
    processed_layer.loadNamedStyle(style)
    processed_layer.triggerRepaint()
    feedback.pushInfo('Loaded buffer styling.')

    return {"OUTPUT": dest_id}

Has this changed since version 3.6?

2
  • 1
    This ain't a fix ... just aworkaround that doesn't use post processing. I'm trying to use postprocessing to apply the same renaming/style operation on a buch of algos and avoid boilerplate but NEVER found any working example Commented Apr 28, 2022 at 14:16
  • In postProcessAlgorithm, you need to reference self.dest_id instead of dest_id and self. instead of instance. Commented Oct 24, 2022 at 15:08

1 Answer 1

2

Figured it out. With the new easy scripting, I simply had to embed the styling step at the end of the processing function:

from math import *
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.processing import alg
from qgis.core import (QgsProcessing,
                       QgsProcessingUtils,
                       QgsFeatureSink,
                       QgsFeature,
                       QgsVectorLayer,
                       QgsGeometry,
                       QgsPointXY,
                       QgsField,
                       QgsFields,
                       QgsWkbTypes,
                       QgsProject,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink,
                       QgsProcessingParameterField,
                       QgsVectorFileWriter
                       )
from qgis.utils import iface
import processing

@alg(name="distance_buffer", label=alg.tr("Distance Buffer"), group="base", group_label=alg.tr("base"))
@alg.input(type=alg.SOURCE, name="INPUT", label="Input layer")
@alg.input(type=alg.SINK, name="OUTPUT", label="Distance Buffers")
@alg.input(type=alg.FILE, name='STYLEFILE', label='Style file', default='C:/Desktop/Buffer.qml')

def processAlgorithm(instance, parameters, context, feedback, inputs):
    """
    Create accurate distance buffers with reprojection.
    """
    source = instance.parameterAsSource(parameters, "INPUT", context)

    if source is None:
        raise QgsProcessingException(instance.invalidSourceError(parameters, "INPUT"))

    fields = QgsFields()
    fields.append(QgsField("Distance", QVariant.Int))
    fields.append(QgsField("Label", QVariant.String))

    (sink, dest_id) = instance.parameterAsSink(parameters, "OUTPUT", context,
                                              fields,
                                              QgsWkbTypes.Polygon,
                                              source.sourceCrs()
                                          )

    if sink is None:
        raise QgsProcessingException(instance.invalidSinkError(parameters, "OUTPUT"))

    total = 100.0 / source.featureCount() if source.featureCount() else 0
    selection = source.getFeatures()
    for current, feature in enumerate(selection):
        if feedback.isCanceled():
            break
        for select in selection:
            geom = select.geometry()
            start_x = geom.asPoint().x()
            start_y = geom.asPoint().y()
            sides = 64
            radius = 6378137.0 # meters
            distance = [250, 500, 1000, 2000, 3000, 4000, 5000, 10000, 15000, 20000, 25000, 30000, 40000, 50000]
            feedback.pushInfo('Creating layer and adding fields.')
            # Create new layer.
            vl = QgsVectorLayer("Polygon", "Distance Buffers", "memory")
            # Add fields.
            pr = vl.dataProvider()
            pr.addAttributes(fields)
            vl.updateFields()
            for i in range(len(distance)):
                feedback.pushInfo('Creating feature.')
                points = []
                dist = distance[i]
                degrees = 0
                while degrees <= 360:
                    degrees = degrees + 360 / sides
                    start_lon = start_x * pi / 180
                    start_lat = start_y * pi / 180
                    bearing = degrees * pi / 180
                    end_lat = asin((sin(start_lat) * cos(dist / radius)) + (cos(start_lat) * sin(dist / radius) * cos(bearing)))
                    end_lon = start_lon + atan2(sin(bearing) * sin(dist / radius) * cos(start_lat),
                                                cos(dist / radius) - (sin(start_lat) * sin(end_lat)))
                    points.append(QgsPointXY(end_lon * 180 / pi, end_lat * 180 / pi))
                    feat_name = str(distance[i])
                    if distance[i] < 1000:
                        label = str(distance[i]) + "m"
                    else:
                        label = str(distance[i]/1000) + "km"
                feedback.pushInfo('Adding feature to layer.')
                # Add calculated features.
                feat = QgsFeature()
                geometry = QgsGeometry.fromPolygonXY([points])
                feat.setGeometry(geometry)
                feat.setAttributes([feat_name,label])
                sink.addFeature(feat, QgsFeatureSink.FastInsert)

        feedback.setProgress(int(current * total))

    style = instance.parameterAsString(parameters, "STYLEFILE", context)
    processed_layer = QgsProcessingUtils.mapLayerFromString(dest_id, context)
    processed_layer.loadNamedStyle(style)
    processed_layer.triggerRepaint()
    feedback.pushInfo('Loaded buffer styling.')

    return {"OUTPUT": dest_id}
1
  • This approach could lead to crashes, using the post processor is safer. Commented Apr 6, 2023 at 15:20

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.