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:
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
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:
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.
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.
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
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.