2

I have converted a graphic model into a script. I want to expend this script and would like to store the result of the Reprojected Algorithm as a layer so as to get the output layers extent.

I am using QgsVectorLayer class to create a vector layer from the result of the algorithm. When I am checking if the layer is valid, the layer is invalid. How can I convert a result of the processing algorithm as a valid vector layer, from which I can extract the extent with .extent() method.

Code Snippet:

        # Reproject layer
        alg_params = {
            'INPUT': parameters['areaboundary'],
            'OPERATION': '',
            'TARGET_CRS': QgsCoordinateReferenceSystem('EPSG:4326'),
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['ReprojectLayer'] = processing.run('native:reprojectlayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(1)
        if feedback.isCanceled():
            return {}

        feedback.pushInfo(str(outputs['ReprojectLayer']['OUTPUT']))
        area_layer = QgsVectorLayer(outputs['ReprojectLayer']['OUTPUT'], 'Reprojected', 'memory')
        if not area_layer.isValid():
            feedback.pushInfo('not valid')
        else:
            QgsProject.instance().addMapLayer(area_layer)

Full Script:


from qgis.core import QgsProcessing, QgsVectorLayer
from qgis.core import QgsProcessingAlgorithm
from qgis.core import QgsProcessingMultiStepFeedback
from qgis.core import QgsProcessingParameterVectorLayer
from qgis.core import QgsProcessingParameterFeatureSink
from qgis.core import QgsCoordinateReferenceSystem
import processing


class DownloadSoil(QgsProcessingAlgorithm):

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterVectorLayer('areaboundary', 'Area Boundary', types=[QgsProcessing.TypeVectorPolygon], defaultValue=None))
        self.addParameter(QgsProcessingParameterFeatureSink('Swapped', 'Swapped', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue=None))


    def processAlgorithm(self, parameters, context, model_feedback):
        # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
        # overall progress through the model
        feedback = QgsProcessingMultiStepFeedback(3, model_feedback)
        results = {}
        outputs = {}

        # Reproject layer
        alg_params = {
            'INPUT': parameters['areaboundary'],
            'OPERATION': '',
            'TARGET_CRS': QgsCoordinateReferenceSystem('EPSG:4326'),
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['ReprojectLayer'] = processing.run('native:reprojectlayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(1)
        if feedback.isCanceled():
            return {}

        feedback.pushInfo(str(outputs['ReprojectLayer']['OUTPUT']))
        area_layer = QgsVectorLayer(outputs['ReprojectLayer']['OUTPUT'], 'Reprojected', 'memory')
        if not area_layer.isValid():
            feedback.pushInfo('not valid')
        else:
            QgsProject.instance().addMapLayer(area_layer)



        feedback.pushInfo(str(area_layer.sourceName()))
        xmin = area_layer.extent().xMinimum()
        ymin = area_layer.extent().yMinimum()
        xmax = area_layer.extent().xMaximum()
        ymax = area_layer.extent().yMaximum()


        feedback.pushInfo(str(ymin))

        requestURL = 'https://sdmdataaccess.sc.egov.usda.gov/Spatial/SDMWGS84GEOGRAPHIC.wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=MapunitPoly&SRSNAME=EPSG:4326&BBOX=' + str(xmin) + ',' + str(ymin) + ',' + str(xmax) + ',' + str(ymax)
        feedback.pushInfo(requestURL)


        # Download file
        alg_params = {
            'URL': requestURL,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['DownloadFile'] = processing.run('native:filedownloader', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(2)
        if feedback.isCanceled():
            return {}

        # Swap X and Y coordinates
        alg_params = {
            'INPUT': outputs['DownloadFile']['OUTPUT'],
            'OUTPUT': parameters['Swapped']
        }
        outputs['SwapXAndYCoordinates'] = processing.run('native:swapxy', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
        results['Swapped'] = outputs['SwapXAndYCoordinates']['OUTPUT']
        return results

    def name(self):
        return 'Download Soil'

    def displayName(self):
        return 'Download Soil'

    def group(self):
        return 'Test Models'

    def groupId(self):
        return 'Test Models'

    def createInstance(self):
        return DownloadSoil()
3
+50

native:reprojectlayer is executed as child algorithm. PyQGIS states in [%qgis_install_dir%\python\plugins\processing\tools\general.py:108] that:

for child algorithms, we disable to default post-processing step where layer ownership is transferred from the context to the caller. In this case, we NEED the ownership to remain with the context, so that further steps in the algorithm have guaranteed access to the layer.

This means that the result of the native:reprojectlayer is a string with the layer id and the ownership of this layer is of the QgsProcessingContext in use during algorithm execution. To get access to the layer, one possible way would be to take back its ownership using QgsProcessingContenxt.takeResultLayer(%layer_id%) The short example hereafter takes back the ownership of the layer and pushes the information about the extent to the log of the algorithm:

def initAlgorithm(self, config=None):
    # No differences from Someone191's code

def processAlgorithm(self, parameters, context, feedback):
    s_alg_id = "native:reprojectlayer"

    di_input = {
        "INPUT": parameters[self.AREA_BND],
        "TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:4326"),
        "OUTPUT": parameters["Swapped"]
    }
    di_out = processing.run(s_alg_id, di_input, context=context, feedback=feedback, is_child_algorithm=True)
    # This is a string with the id of the memory layer # 
    lyr_out_str = di_out["OUTPUT"]
    # This is how to take back the ownership of the layer from the context # 
    lyr_out = context.takeResultLayer(lyr_out_str)
    if lyr_out.isValid():
        feedback.pushInfo(lyr_out.extent().asWktPolygon())

    return {"OUTPUT": lyr_out_str, "DI_OUT": di_out}

A sketch of the output is presented in the following picture: enter image description here

A more extensive example of how to use the takeResultLayer() is given directly in the implementation of PyQGIS is e.g., [%qgis_install_dir%\python\plugins\processing\core\Processing.py:151-176]. I hope this helps you!

1
  • This is exactly what I needed: lyr_out = context.takeResultLayer(lyr_out_str) , I can now move between algorithm outputs and qgis layer edits and manipulate further - thanks so much! – GIS_py Aug 7 '20 at 10:14
4

when you set a temporary output for the 'native:reprojectlayer' algorithm, the OUTPUT is already a QgsMapLayer (in this case a vector) whose extent you can check.

parametros = {'INPUT' : 'C:/Users/.../someshapefile.shp',
              'OUTPUT' : 'TEMPORARY_OUTPUT',
              'TARGET_CRS' : QgsCoordinateReferenceSystem('EPSG:5344') }
output = processing.run('native:reprojectlayer', parametros) ['OUTPUT'] 
extent = output.extent()

hope it helps

2
  • Your method works in a standalonde code, but in my case the algorithm is part of a bigger script and when I do outputs['ReprojectLayer']['OUTPUT'].extent() I am getting error that str object do not have any attribute .extent() – ar-siddiqui May 12 '20 at 5:36
  • any idea why it is doing that and how to solve it? – ar-siddiqui May 13 '20 at 9:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.