Accessing Processing Algorithm Outputs In Standalone Pyqgis

Reading Algorithm Output Layers

When an algorithm is executed in QGIS, it often generates one or more output layers that are automatically loaded into the QGIS project. However, when running algorithms in standalone PyQGIS scripts, the output layers are not automatically loaded.

To access the output layers in PyQGIS, the processing.run() method returns a dictionary containing the algorithm outputs. The keys in this dictionary are the names defined in the algorithm itself.

For example, after running the Buffer algorithm, the outputs can be accessed like this:


outputs = processing.run("native:buffer", {
  'INPUT': vector_layer,
  'DISTANCE': 10,
  'SEGMENTS': 5,
  'END_CAP_STYLE': 0, 
  'JOIN_STYLE': 0,
  'MITER_LIMIT': 2,
  'DISSOLVE': False,
  'OUTPUT': 'memory:' 
})

buffered_layer = outputs['OUTPUT']

The ‘OUTPUT’ value will contain the buffer layer that was generated. Its geometry type can then be inspected to confirm it is a polygon layer:


print(buffered_layer.geometryType()) # Polygon 

From there, the output layer can be added to the map, styled, analyzed, saved to disk, etc.

Accessing Layer Names and Labels

In some cases, an algorithm may generate multiple output layers with generic names like ‘OUTPUT’ and ‘OUTPUT2’. To understand what each layer represents, use:


algorithm = QgsApplication.processingRegistry().algorithmById('native:buffer')
output_definitions = algorithm.outputDefinitions()

for output in output_definitions:
  print(output.name(), output.description()) 

This will print the names and descriptions for each output generated by the algorithm.

Accessing Individual Output Objects

When running an algorithm that outputs individual geometric objects, like centroids, the outputs may not be wrapped in a handy layer like the buffer example above.

Instead, the output is typically a QgsFeatureSink that can be iterated to access each feature individually. For example:


outputs = processing.run("native:centroids", {'INPUT': polygon_layer, 'OUTPUT': 'memory:'}) 

centroids = outputs['OUTPUT']

for feat in centroids.getFeatures():
  print(feat.geometry())

This allows you to work with one centroid feature at a time. Each feature could be processed, converted to JSON, added to another layer, etc.

Getting Output Length

To get the number of features in the output sink, use the featureCount() method:


num_centroids = outputs['OUTPUT'].featureCount()
print(num_centroids)

Filtering Output Features

Output features can also be filtered using normal QgsExpression filters:


centroids = outputs['OUTPUT'] 
exp = QgsExpression('area($geometry) > 100')

for feat in centroids.getFeatures(QgsFeatureRequest(exp)):
  print(feat['id']) 

Working with Output Rasters

For algorithms that output raster layers, the process is much like vector outputs.

The run() method returns outputs that can be accessed by name. For rasters, this will typically return a QgsRasterLayer that can be used like any other raster layer.


outputs = processing.run("native:slope", {'INPUT': dem_layer, 'OUTPUT': 'memory:'})  

slope_layer = outputs['OUTPUT']

print(slope_layer.bandCount())
print(slope_layer.extent())

# Save raster as GeoTiff
slope_layer.writeAsRasterFormat('slope.tif', 'GTiff', crs=QgsCoordinateReferenceSystem('EPSG:32610))

Raster Pixel Access

For low-level access to the raster pixels values, the dataProvider() and block() methods can be used:


provider = slope_layer.dataProvider()  

# Get extent and columns/rows
extent = provider.extent()
cols = provider.xSize() 
rows = provider.ySize()

# Access 2x2 block 
block = provider.block(0, extent, 2, 2)  
print(block.value(0,0)) 
print(block.value(0,1))

This allows reading the raw raster data values without needing GDAL Python bindings.

Working with Output Vectors

Vector outputs use the same processing.run() pattern as rasters. The only difference is the outputs will typically be QgsVectorLayer objects that can be used in the same way as any vector layer.

Common operations with output vector layers:

  • Count number of features
  • Iterate over features
  • Access/edit geometry and attributes
  • Apply symbology
  • Write to GeoPackage/Shapefile/GeoJSON
  • Spatial queries and geoprocessing

For example:


outputs = processing.run("native:buffer", {'INPUT': points_layer, 'DISTANCE': 100, 'OUTPUT': 'memory:'}) 

buffered = outputs['OUTPUT']  

# Number of features
print(buffered.featureCount())

# Write to shapefile
error = QgsVectorFileWriter.writeAsVectorFormat(buffered, 'buffered.shp', 'UTF-8', 
  buffered.crs(), 'ESRI Shapefile')  

if error[0] == QgsVectorFileWriter.NoError:
  print('Success!')

# Spatial query
overlap_count = 0

rect_geom = QgsGeometry.fromRect(QgsRectangle(50000, 50000, 60000, 65000))
request = QgsFeatureRequest().setFilterRect(rect_geom.boundingBox())  

for f in buffered.getFeatures(request):
  if f.geometry().intersects(rect_geom):
    overlap_count += 1

print(overlap_count)

Using Attribute Tables

Output vector attributes can be analyzed and edited using the layer’s attribute table interface.

First, initialize the table:


table = buffered.attributeTable()

Then query, edit and add features:


# Find features with area > 5000
area_filter = QgsAttributeTableFilter()
area_filter.setField('area')
area_filter.setFilterOperator('>')
area_filter.setFilterValue('5000')

ids = table.allFeatureIds()
filtered_ids = area_filter.filterFeatures(ids)

print(len(filtered_ids))

# Edit specific feature
feat = buffered.getFeature(45)
feat['class'] = 2  

buffered.updateFeature(feat)

# Add new feature
f = QgsFeature(buffered.fields())
f.setGeometry(QgsGeometry.fromWkt(...))
f['class'] = 1

buf_table.addFeatures([f])

This provides SQL-like analysis of output layers without needing psycopg2 or sqlite3.

Handling Errors and Warnings

It is always a good idea to handle errors and warnings when calling processing algorithms in PyQGIS scripts.

All processing.run() calls return a dictionary with two keys:

  • results – the algorithm outputs
  • info – information like warnings and errors

Example of handling potential issues:


result = processing.run(...) 

if result['info'] is None:
  print('Success!')
else:
  # Print any warnings
  for warn in result['info']: 
    print('Warning:', warn)  

  # Check for errors
  if result['info'].fatalError: 
    print('Fatal Error Occurred!')
    raise Exception(result['info'].toString()) 

By anticipating errors, scripts can avoid silent failures and become easier to debug.

Common Errors

Some common errors include:

  • Invalid file paths
  • Incorrect parameter data types
  • No features in input layer
  • Invalid geometries
  • Output layer write access problems

Checking algorithm info allows anticipating these issues.

Example Code for Accessing Outputs

Here is a full script example:


import processing  

input_path = '.../buildings.shp'

bld_layer = QgsVectorLayer(input_path, 'Buildings', 'ogr') 

if not bld_layer.isValid():
  print('Invalid layer')
  quit()
  
params = {
  'INPUT': bld_layer,
  'DISTANCE': 10,
  'SEGMENTS': 8,
  'END_CAP_STYLE': 0,
  'JOIN_STYLE': 0,
  'MITER_LIMIT': 2,
  'DISSOLVE': True,
  'OUTPUT': 'memory:'
}
  
result = processing.run('native:buffer', params)

if result['info']:
  # Handle errors and warnings
else:  
  buffered_layer = result['OUTPUT']
  
  print(buffered_layer.featureCount())
  
  error = QgsVectorFileWriter.writeAsVectorFormat(buffered_layer, 
    'buffered.shp', 'utf-8', buffered_layer.crs(), 'ESRI Shapefile')
  
  if error[0] == QgsVectorFileWriter.NoError:
    print('Success!')

This shows a typical pattern for running an algorithm, accessing the outputs, evaluating any errors, and writing the outputs to file geodatabase or shapefile if needed.

The outputs can then be used in further analysis and visualization operations.

Leave a Reply

Your email address will not be published. Required fields are marked *