Advertisements

In “Running Processing tools“, we explored the basics of running Processing tools from PyQGIS. This time, we’ll look into how to chain multiple tools into a workflow.

In this example, we’ll be building a workflow that computes which cities are located along the Danube river (data from NaturalEarthData). We’ll first use the GUI to run the corresponding tools (Extract by attribute, Buffer, and Extract by location) and test our workflow. Then we will go forward and create a PyQGIS version of the workflow.

After using the GUI, we can look up the corresponding code in the Processing history. It looks something like this:

processing.run("native:extractbyexpression", {'INPUT':'E:/Geodata/.../ne_110m_rivers_lake_centerlines.shp',
    'EXPRESSION':'name = \'Danube\'','OUTPUT':'memory:'})
processing.run("native:buffer",{'INPUT':'MultiLineString?crs=EPSG:4326&...','DISTANCE':0.1,'SEGMENTS':5,
    'END_CAP_STYLE':0,'JOIN_STYLE':0,'MITER_LIMIT':2,'DISSOLVE':False,'OUTPUT':'memory:'})
processing.run("native:extractbylocation",{'INPUT':'E:/Geodata/.../ne_110m_populated_places.shp',
    'PREDICATE':[0],'INTERSECT':'MultiPolygon?crs=EPSG:4326&...','OUTPUT':'memory:'})

(I’ve shortened the file paths for readability reasons.)

These code snippets from the Processing history provide us with a guide to the necessary syntax but we cannot use the code directly. When we want to convert this to a script, we need to take care of handling the intermediate results. Above, these intermediate results are stored in memory layers. The memory layer resulting from Extract by attribute is passed to the Buffer tool input as 'MultiLineString?crs=EPSG:4326&...'. For our script, we need to go a different way and store a reference to the result in a variable.

The following script shows how the results of one tool can be passed to the next: in case of the tools we are using in this example, processing.run() always returns a dictionary with just one entry called 'OUTPUT'.  This way, we store the result layer of Extract by expression in the variable danube. Then we use the danube layer as an input for the buffer tool, and so on:

rivers = 'E:/Geodata/NaturalEarth/vector_v4/natural_earth_vector.shp_v4.0.0/110m_physical/ne_110m_rivers_lake_centerlines.shp'
places = 'E:/Geodata/NaturalEarth/vector_v4/natural_earth_vector.shp_v4.0.0/110m_cultural/ne_110m_populated_places.shp'

expression = "name = 'Danube'"
danube = processing.run("native:extractbyexpression", 
    {'INPUT':rivers,'EXPRESSION':expression,'OUTPUT':'memory:'}
    )['OUTPUT']

buffer_distance = 0.1 #degrees 

buffered_danube = processing.run("native:buffer", 
    'INPUT':danube,'DISTANCE':buffer_distance,'SEGMENTS':5,'END_CAP_STYLE':0,
    'JOIN_STYLE':0,'MITER_LIMIT':2,'DISSOLVE':False,'OUTPUT':'memory:'}
    )['OUTPUT']

places_along_danube = processing.run("native:extractbylocation", 
    {'INPUT':places,'PREDICATE':[0],'INTERSECT':buffered_danube,'OUTPUT':'memory:'}
    )['OUTPUT']

QgsProject.instance().addMapLayer(places_along_danube)

for feature in places_along_danube.getFeatures():
    print(feature["name"])

Don’t worry if the syntax of some Processing tools looks a bit complex. Remember that you can look up the correct syntax for every Processing tool you’ve run through the GUI in the Processing history.

Running this script, returns us the following four cities:

Bratislava
Belgrade
Budapest
Vienna

These are the basics of chaining Processing tools to build more complex workflows. The key is to figure out the correct syntax by examining the Processing history. Then we can store the results in variables and pass them on to successive tools. Finally, we can load the results and explore, style, or print them.


PyQGIS 101 is a work in progress. I’d appreciate any feedback, particularly from beginners!

Advertisements
6 comments
  1. soli004 said:

    I saved the csv file as a geopackage and to the desktop, changed the path for the desktop and now it runs thru but with no result. As I’m not using lat/lon I changed the buffer distance to 500 ( I guess that would be meters) as I use ESPG 3006. I did change to like 100, 500, 1000, 5000 but the result is empty… so something is still not working for me…. sorry for this long problem solving… the “vlayer.setSubsetString” is working for me at least half thru my process so a big improvement compare to do it manually :)

  2. soli004 said:

    data sent!
    Thanks

    • I’m confused. The river dataset you sent only covers Sweden and thus does not contain a river with name danube. How should the altered script you posted work in that case?

      • soli004 said:

        Sorry, my bad. I thought you could put any name you wanted so I let it be danube….
        That is way it did not work… :#) I misunderstood you….

        Free and Open Source GIS Ramblings skrev den 2019-01-05 kl. 23:44:

  3. Leo said:

    Really cool stuff. Thanks for mentioning the processing history, it was completely unknown to me!
    Running these outside of the QGIS Python Console seems to be a lot more fiddly since QGIS 3. I tried a million different ways to set up the environmental variables, etc, but no luck. I currently have to use the QGIS python shell given with the stand-alone version while calling OSGeo4W paths. The normal OSGeo4w shell doesnt work :(
    A neat install guide for python using OSGeo4W/QGIS tools outside of QGIS would be amazing!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: