[Qgis-user] Iterative calls of processing algorithm fails

morten at styrke10.dk morten at styrke10.dk
Fri Oct 11 01:38:14 PDT 2024


Hi all,

I have written a processing script for QGIS 3.x, which itself calls a native
processing function (qgis:shortestpathpointtolayer) in a for loop.

---------
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsFeatureRequest,
                       QgsExpression,
                       QgsProcessingContext,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterVectorLayer,
                       QgsProcessingFeatureSourceDefinition)
from qgis import processing


class findDownstreamPathAlgorithm(QgsProcessingAlgorithm):
    """
    This is an algorithm that calculates downstream paths on
    a simplified network.

    """

    INPUT_NETWORK = 'INPUT_NETWORK'
    INPUT_VALVES = 'INPUT_VALVES'

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return findDownstreamPathAlgorithm()

    def name(self):
        return 'iterateclusters'

    def displayName(self):
        return self.tr('Iterate clusters')

    def group(self):
        return self.tr('Thvilum')

    def groupId(self):
        return 'lukkelister'

    def shortHelpString(self):
        return self.tr("Iterate clusters in a simplified network")

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.INPUT_NETWORK,
                self.tr('Netværkslag'),
                [QgsProcessing.SourceType.TypeVectorLine]
            )
        )

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.INPUT_VALVES,
                self.tr('Ventillag'),
                [QgsProcessing.SourceType.TypeVectorPoint]
            )
        )

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

        # Retrieve the feature sources
        network = self.parameterAsVectorLayer(
            parameters,
            self.INPUT_NETWORK,
            context
        )

        valves = self.parameterAsVectorLayer(
            parameters,
            self.INPUT_VALVES,
            context
        )

        if network is None:
            raise QgsProcessingException(self.invalidSourceError(parameters,
self.INPUT_NETWORK))

        if valves is None:
            raise QgsProcessingException(self.invalidSourceError(parameters,
self.INPUT_VALVES))

        # Store inlets from valves layer in separate scractch layer
        inlets =
valves.materialize(QgsFeatureRequest(QgsExpression("situation =
'IsInlet'")))

        # Collect clusters to evaluate
        clusterList = []
        for feat in network.getFeatures():
            clusterList.append(feat["cluster"])
 
        # Remove duplicates and sort list
        clusterList = sorted(set(clusterList))
        if len(clusterList) > 0:
            total = 100.0 / len(clusterList)
        else:
            total = 0

        isCanceled = False
        for current, cluster in enumerate(clusterList):
            # Update the progress bar
            feedback.setProgress(int(current * total))
            
            valves.selectByExpression("cluster = {} and situation =
'Flow'".format(cluster))
            for feat in valves.selectedFeatures():
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    isCanceled = True
                    break

                vertex_id = feat["vertexId"]
                valve_geom = feat.geometry()
                network.selectByExpression("cluster != {}".format(cluster))

                parameters = {
                    'DEFAULT_DIRECTION' : 2,
                    'DEFAULT_SPEED' : 50,
                    'DIRECTION_FIELD' : '',
                    'END_POINTS' : inlets,
                    'INPUT' : QgsProcessingFeatureSourceDefinition(
network.id(), selectedFeaturesOnly=True),
                    'OUTPUT' : 'TEMPORARY_OUTPUT',
                    #'OUTPUT_NON_ROUTABLE' : 'TEMPORARY_OUTPUT',
                    'SPEED_FIELD' : '',
                    'START_POINT' : valve_geom,
                    'STRATEGY' : 0,
                    'TOLERANCE' : 0,
                    'VALUE_BACKWARD' : '',
                    'VALUE_BOTH' : '',
                    'VALUE_FORWARD' : ''
                    }
            
                result = processing.run("qgis:shortestpathpointtolayer",
parameters, context=context, feedback=feedback, is_child_algorithm=True)
                shortest_path =
QgsProcessingContext.takeResultLayer(context, result['OUTPUT'])
                
                # Do something with the path
                    
                del shortest_path
                    
            if isCanceled:
                break


        return {self.INPUT_NETWORK: network}
---------

When running this script on QGIS 3.28.4 it runs a couple of iterations and
then raises an exception (access violation) with the following description:

**Python Stack Trace**
```
Windows fatal exception: access violation

Thread 0x00005d8c (most recent call first):
  File
"C:\PROGRA~1/QGIS32~2.4/apps/qgis-ltr/./python/plugins\processing\gui\Algori
thmExecutor.py", line 72 in execute
    results, ok = alg.run(parameters, context, feedback, {}, False)
  File
"C:\PROGRA~1/QGIS32~2.4/apps/qgis-ltr/./python/plugins\processing\core\Proce
ssing.py", line 187 in runAlgorithm
    ret, results = execute(alg, parameters, context, feedback,
catch_exceptions=False)
  File
"C:\PROGRA~1/QGIS32~2.4/apps/qgis-ltr/./python/plugins\processing\tools\gene
ral.py", line 116 in run
    return Processing.runAlgorithm(algOrName, parameters,
onFinish=post_process, feedback=feedback, context=context)
  File
"C:\Users\morte\AppData\Roaming\QGIS\QGIS3\profiles\default\processing\scrip
ts\iterateClusters.py", line 156 in processAlgorithm
    result = processing.run("qgis:shortestpathpointtolayer", parameters,
context=context, feedback=feedback, is_child_algorithm=True)

Current thread 0x000075e0 (most recent call first):
  File
"C:\PROGRA~1/QGIS32~2.4/apps/qgis-ltr/./python/plugins\processing\Processing
Plugin.py", line 394 in executeAlgorithm
    dlg.exec_()
  File
"C:\PROGRA~1/QGIS32~2.4/apps/qgis-ltr/./python/plugins\processing\gui\Proces
singToolbox.py", line 234 in executeAlgorithm
    self.executeWithGui.emit(alg.id(), self, self.in_place_mode, False)
```

On QGIS 3.38.3 the process also runs for a couple of iteration and then just
freezes the whole application.

Running a simple (=non processing) version of this script from the Python
console works beautifully on both QGIS versions!

What am I missing in my processing script?



Best regards,
Morten

--
Morten Storm
Styrke 10 ApS

House of Innovation | Jernbanegade 27 | 6000 Kolding | Denmark

www: https://styrke10.dk
mobile: +45 51 51 09 29




More information about the QGIS-User mailing list