Skip to content

Processing Scripts

API Note: Any QGIS APIs referenced will link out to the Python API Docs.

Note: If you are running QGIS 3.4.1 remember to apply the patch in the data folder

In this example will explore adding a new custom processing script using Python that will split a lines layer based on a given distance.

Adding custom processing scripts allow you to add extra logic to your daily workflow without requiring the development of a full full plugin. By using a processing script they can also be used in the model builder with other processing tools.

What we are building

  • Splitting lines into even length segments

Layers used: Roads

Final Result:

Result

Exercise: Creating a custom script

For this example we will open the processing toolbox via Processing -> Toolbox. Once the toolbox is open we will add a new script using a built in the Create New Script From Template option

Result

Using this we can start with a template of a ready to go and ready to run script. Just for run we can run it right out of the box to see the results it will give us. Lets try that:

Result

We can now start to tweak the script to do what we need. The good news is we can build it in parts and run it to see the results

Add imports

The first thing we need to do is add some imports at the top for later.

Exercise: Add imports

from qgis.core import QgsGeometry, QgsFeature, QgsProcessingParameterDistance, QgsLineString

Adding Input constants

The next thing will be updating the constant that will be using to refer to a input value in the script. We will call it DISTANCE. You will also note the new algorithm inherits from QgsProcessingAlgorithm that will give us access to all the methods that we need for the script.

Exercise: Adding Input constants

class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
    # 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.

    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'
    DISTANCE = 'DISTANCE' # This is the new variable

Exercise: Update metadata

Update the return value in the name function

    def name(self):
        return 'workshopscript'

Do the same for displayName,group and shortHelpScript functions

Adding parameters

The next important part is the the initAlgorithm method as this is called to build the UI options that are required for the script. Inside this we will add a new option for the distance value.

We do this by calling addParameter with a new QgsProcessingParamterDistance. 30 will be our default value.

Exercise: Adding parameters

def initAlgorithm(self, config=None):
    self.addParameter(
        QgsProcessingParameterFeatureSource(
            self.INPUT,
            self.tr('Input layer'),
            [QgsProcessing.TypeVectorLine] # Change me to TypeVectorLine
        )
    )

    # New distance arg
    self.addParameter(
        QgsProcessingParameterDistance(
            self.DISTANCE,
            "Distance",
            30,
            self.INPUT
        )
    )

Reading parameter values

The next method we will focus on is processAlgorithm which is called at runtime to do the real work. Good thing for us most of the work is already done we just need to fetch our new value using parameterAsInt using self.DISTANCE to map to the right paramter id

Fetch distance from the from parameter.

Exercise: Reading parameters values

def processAlgorithm(self, parameters, context, feedback):
    source = self.parameterAsSource(
        parameters,
        self.INPUT,
        context
    )

    distance = self.parameterAsInt(
        parameters,
        self.DISTANCE,
        context
    )

Exercise: Testing what we have

Before we do anything with the values. Lets run and make sure our UI is correct

We can see the new Distance option there.

Result

Exercise: Adding the real logic

The real magic happens inside processAlgorithm.

We need to head to the line for current, feature in enumerate(features): add the following inside the for loop

        for current, feature in enumerate(features):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            attrs = feature.attributes()
            geom = feature.geometry().constGet()
            if isinstance(geom, QgsLineString): continue
            first_part = geom.geometryN(0)
            start = 0
            end = distance
            length = first_part.length()
            feedback.pushInfo(str("Total Length {}".format(length)))

            while start < length:
                if feedback.isCanceled():
                    break

                feedback.pushInfo("Start: {} End: {}".format(start, end))
                newgeom = QgsGeometry(first_part.curveSubstring(start,end))
                f = QgsFeature(feature)
                f.setGeometry(newgeom)
                sink.addFeature(f, QgsFeatureSink.FastInsert)
                start += distance
                end += distance

            # Update the progress
            feedback.setProgress(int(current * total))

Save and run the script using the run button. Select one of the loaded line layers.

Result

Result

When can style the layer to show markers for the end of each road segment and a label using the following in the label expression

round($length, 3) || '\n' || Road_Label

Result

There is a bit in that above script but the overview is:

  • for each feature
  • get the geometry
  • get the length
  • start at 0
  • loop forever while start < length
  • grab a section of line from start to end
  • create a new feature, set the geometry, and pass it back

Summary

Processing scripts a fast and easy way to extend your QGIS workflow to include extra logic when needed.