0

I am writing a standalone processing algorithm with PyQGIS. The intention of the script is to create an offline basemap that is created only within the extents of objects of one layer. Therefore I am using the native:rasterize tool in a for loop to create the list rasters of temporary rasters. This list is eventually used in gdal:merge to be merged together to one raster. The script runs smoothly until the final step of loading the final raster back to the QGIS project or save it.

Somewhere, I am missing the final link to save the output raster. So far I am using a combination of defining a output path as parameter QgsProcessingParameterRasterDestination and use this parameter to set a output layer self.parameterAsOutputLayer (following this post)

Using this combination QGIS says:

The following layer could not be created: C:/Users/lorenz.beck/AppData/Local/Temp/processing_oPONNl/1b663b5b7c8f4ad28f3477d8e4b9ec93/OUTPUT_RASTER.tif You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.

And within the 'Log Messages Panel' in QGIS it gives me two warning at the same time:

2025年04月10日T10:10:46 WARNING Cannot open C:/Users/lorenz.beck/AppData/Local/Temp/processing_oPONNl/42c640bc19ee492698f93ab0be05b1e2/OUTPUT.tif ().()

2025年04月10日T10:10:46 WARNING Cannot open C:/Users/lorenz.beck/AppData/Local/Temp/processing_oPONNl/91f5715b037a4c2ea0d2d557d866405d/OUTPUT.tif ().()

Can you help me to successfully save the last raster and load it back into my QGIS project? I have also run processing.runAndLoadResults("gdal:merge"...) which adds the raster to my QGIS project. So the output is created, but it doesn't feel like the cleanest way to go.

Here is the code I am using:

from typing import Any, Optional
from qgis.PyQt.QtCore import QVariant
from qgis.core import (
 QgsFeatureSink,
 QgsProcessing,
 QgsProcessingAlgorithm,
 QgsProcessingContext,
 QgsProcessingException,
 QgsProcessingFeedback,
 QgsProcessingOutputRasterLayer,
 QgsProcessingParameterFeatureSource,
 QgsProcessingParameterMapLayer,
 QgsProcessingParameterMapTheme,
 QgsProcessingParameterNumber,
 QgsProcessingParameterRasterDestination,
 QgsCoordinateReferenceSystem
)
from qgis import processing
class ExportMapToMultipleRaster(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_OBJECTS = "INPUT_OBJECTS"
 INPUT_RASTER_LAYER = "INPUT_RASTER_LAYER"
 INPUT_MAP_THEME = "INPUT_MAP_THEME"
 INPUT_DOUBLE = "INPUT_DOUBLE"
 OUTPUT_RASTER = "OUTPUT_RASTER"
 def name(self) -> str:
 return "exportmaptomultipleraster"
 def displayName(self) -> str:
 return "Exportiere Karte in mehrere Raster"
 def group(self) -> str:
 return "Raster"
 def groupId(self) -> str:
 return "raster"
 def shortHelpString(self) -> str:
 return "Exportiert einen ein Kartenthema in mehrere Raster basierend auf den Objektausdehnungen eines Polygon-Layers. Wenn kein Kartenthema ausgewählt ist wird die aktuelle Kartendarstellung in ein Raster exportiert"
 def initAlgorithm(self, config: Optional[dict[str, Any]] = None):
 # We add the input vector feature source. A polygon layer with objects
 # that are used to export the map in their extents
 self.addParameter(
 QgsProcessingParameterFeatureSource(
 self.INPUT_OBJECTS,
 "Input Polygon Objekte",
 [QgsProcessing.SourceType.TypeVectorPolygon],
 )
 )
 
 # We add a map theme that can be used for an ovelayed visualiziation of the map
 self.addParameter(
 QgsProcessingParameterMapTheme(
 self.INPUT_MAP_THEME,
 "Kartenthema",
 optional=True
 )
 )
 
 # We add a double number for setting the resolution of the output raster
 self.addParameter(QgsProcessingParameterNumber(
 self.INPUT_DOUBLE, 
 "Pixelgröße (m)", 
 QgsProcessingParameterNumber.Double,
 QVariant(1.0)))
 # We add a output raster Layer Destination.
 self.addParameter(
 QgsProcessingParameterRasterDestination(
 self.OUTPUT_RASTER, 
 "Output Raster"
 )
 )
 def processAlgorithm(
 self,
 parameters: dict[str, Any],
 context: QgsProcessingContext,
 feedback: QgsProcessingFeedback,
 ) -> dict[str, Any]:
 """
 Here is where the processing itself takes place.
 """
 # Retrieve the feature source
 polygon_source = self.parameterAsSource(parameters, self.INPUT_OBJECTS, context)
 if polygon_source is None:
 raise QgsProcessingException(
 self.invalidSourceError(parameters, self.INPUT_OBJECTS)
 )
 
 # Retrieve the map layer
 map_theme_source = self.parameterAsString(parameters, self.INPUT_MAP_THEME, context)
 
 # User feedback which map style will be exported
 if map_theme_source != '':
 feedback.pushInfo(f'Das Kartenthema {map_theme_source} wird für das Ausgaberaster verwendet')
 else:
 feedback.pushInfo('Die aktuelle Kartendarstellung wird für das Ausgaberaster verwendet')
 
 # Retrieve cell size for final raster
 cell_size_source = self.parameterAsDouble(parameters, self.INPUT_DOUBLE, context)
 
 # Retrieve output raster
 output_raster = self.parameterAsOutputLayer(parameters, self.OUTPUT_RASTER, context)
 
 # User Feedback of CRS and projection if needed
 source_crs = polygon_source.sourceCrs().authid()
 feedback.pushInfo('CRS is {}'.format(polygon_source.sourceCrs().authid()))
 
 # Compute the number of steps to display within the progress bar and
 # get features from source
 total = 100.0 / polygon_source.featureCount() if polygon_source.featureCount() else 0
 features = polygon_source.getFeatures()
 rasters = []
 for current, feature in enumerate(features):
 # Stop the algorithm if cancel button has been clicked
 if feedback.isCanceled():
 break
 
 # Get maximum and minimum of each feature's bounding box
 xmin = feature.geometry().boundingBox().xMinimum()
 xmax = feature.geometry().boundingBox().xMaximum()
 ymin = feature.geometry().boundingBox().yMinimum()
 ymax = feature.geometry().boundingBox().yMaximum()
 
 # Hereby, we can create the Extent object 
 # 'xMin, yMax, xMax, yMin, yMax [EPSG:Code]'
 extent = f"{xmin},{xmax},{ymin},{ymax} [{source_crs}]"
 
 # If Map theme is not None then take it as input
 feedback.pushInfo(f'Map theme is converted to raster')
 memory_raster = processing.run(
 "native:rasterize", 
 {'EXTENT':extent,
 'EXTENT_BUFFER':0,
 'TILE_SIZE':100,
 'MAP_UNITS_PER_PIXEL':cell_size_source,
 'MAKE_BACKGROUND_TRANSPARENT':False,
 'MAP_THEME':map_theme_source,
 'LAYERS':None,
 'OUTPUT':'TEMPORARY_OUTPUT'},
 context=context,
 feedback=feedback,
 is_child_algorithm=True
 )["OUTPUT"]
 rasters.append(memory_raster)
 # Update the progress bar
 feedback.setProgress(int(current * total))
 
 merged_raster = processing.run(
 "gdal:merge", 
 {'INPUT':rasters,
 'PCT':False,
 'SEPARATE':False,
 'NODATA_INPUT':None,
 'NODATA_OUTPUT':0,
 'OPTIONS':None,
 'EXTRA':'',
 'DATA_TYPE':1,
 'OUTPUT':'TEMPORARY_OUTPUT'},
 context=context,
 feedback=feedback,
 is_child_algorithm=True
 )["OUTPUT"]
 # Update the progress bar
 feedback.setProgress(int(current * total))
 
 results = {}
 results[self.OUTPUT_RASTER] = merged_raster
 return results
 def createInstance(self):
 return self.__class__()
PolyGeo
65.5k29 gold badges115 silver badges349 bronze badges
asked Apr 10 at 8:16
0

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.