2

I figured out that besides from the python editor within QGIS, the layers that I'm creating with a Processing Toolbox are not saved and displayed in my .qgs projects.

--> see image: 1) what I get from running a similar script in the python editor (what I want to have) and 2) what I get (no rendering but layer paths are stored in the .qgs project

Here are the steps I follow:

  1. In the processing toolbox, the user gives me the path of the data directory, a path to an excel file and two integer numbers (simulation number start and end) --> THERE ARE NO INPUT layers!!

  2. In my "def processAlgorithm" I make a loop over the simulation numbers. For each loop, I

    • Create a .qgs project
    • Load 2 rasters and 1 vector layer
    • Reclassify the rasters (with saga:reclassifyvaluessimple)
    • Save the two reclassified raster and the vector layer to .qgs project

My .qgs projects are saved but the layers do not display! what is wrong? Something with my "QgsProcessingOutputMultipleLayers"? do I have to create a sink?

Here is my code so far:

from PyQt5.QtGui import *
from PyQt5.QtCore import QCoreApplication, QFileInfo, QSettings
from PyQt5.QtWidgets import QApplication
from qgis.utils import iface
from qgis.PyQt import QtGui
from qgis.core import (QgsProcessing,
                       QgsApplication,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterString,
                       QgsProcessingParameterFile,
                       QgsProcessingParameterNumber,
                       QgsRasterLayer,
                       QgsVectorLayer,
                       QgsProject,
                       QgsCoordinateReferenceSystem,
                       QgsPrintLayout,
                       QgsSingleBandPseudoColorRenderer,
                       QgsColorRampShader,
                       QgsRasterShader,
                       QgsProcessingOutputDefinition,
                       QgsProcessingOutputMultipleLayers,
                       QgsProcessingParameterVectorDestination)
import processing
import os
import xlrd
import shutil


class RAMMSResultstoMap(QgsProcessingAlgorithm):

    ### ALREADY-MADE DEFINITIONS

    INPUT_DIR = 'INPUT_DIR'
    INPUT_EXCEL = 'INPUT_EXCEL'
    START_NBR = 'START_NBR'
    END_NBR = 'END_NBR'
    OUTPUT_LAYERS = 'OUTPUT_LAYERS'

    def __init__(self):
        super().__init__()

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return RAMMSResultstoMap()

    def name(self):
        return 'pyscript'

    def displayName(self):

        return self.tr('GetMap')

    def group(self):
        return self.tr('Scripts for myself')

    def groupId(self):
        return 'ramms'

    def shortHelpString(self):
        return self.tr("This script will generate .pdfs automatically from the RAMMS simulations' results")

    def initAlgorithm(self, config=None):


        self.addParameter(
            QgsProcessingParameterString(
                self.INPUT_DIR,
                self.tr('RAMMS Project path')
            )
        )

        self.addParameter(
            QgsProcessingParameterFile(
                self.INPUT_EXCEL,
                self.tr('RAMMS Excel path')
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.START_NBR,
                self.tr('Startindex')
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.END_NBR,
                self.tr('Stopindex')
            )
        )


        self.addOutput(
            QgsProcessingOutputMultipleLayers(
                self.OUTPUT_LAYERS,
                self.tr('')
            )
        )


    ### START OF PROCESSING
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        ## Definitions taking place in the processAlgorithm
        # read excel sheet
        def readExcelSheet(path, start, end):
            workbook = xlrd.open_workbook(path)
            worksheet = workbook.sheet_by_name('Tabelle1')
            worksheet = workbook.sheet_by_index(0)

            # Get additional information
            project_gemeinde = worksheet.cell(2, 1).value
            project_date = worksheet.cell(0, 18).value
            project_author = worksheet.cell(1, 18).value
            project_number = worksheet.cell(0, 2).value

            # Get table values needed
            if start == end:
                start = start - 1 

            table_selected_values = [[worksheet.cell_value(r, c) for c in range(23)] for r in range(start+6,end+6)]
            table_infos = [project_gemeinde, project_date, project_author, project_number]

            return table_infos, table_selected_values

        # read anriss shp       
        def listAnrissgebiete(path):
            anriss_filelist = os.listdir(path)
            anriss_shplist = []
            for i in anriss_filelist:
                if i.endswith(".shp"):
                    anriss_shplist.append(path + "\\" + i)

            return anriss_shplist



        # Retrieve input parameters
        input_dir = self.parameterAsString(
            parameters,
            self.INPUT_DIR,
            context
            )

        input_excel = self.parameterAsFile(
            parameters,
            self.INPUT_EXCEL
            context
            )

        start_nbr = self.parameterAsInt(
           parameters,
           self.START_NBR,
           context
           )

        end_nbr = self.parameterAsInt(
           parameters,
           self.END_NBR,
           context
           )


        # Get/retrieve paths and variable
        anriss_folder = os.path.dirname(input_dir) + "\\Anrissgebiete"
        main_result_folder = os.path.dirname(input_dir) + "\\Resultate"

        # create specific result folder
        result_folder = main_result_folder + "\\" + os.path.basename(input_dir)
        if os.path.isdir(result_folder): # if result folder already exists, remove it
            shutil.rmtree(result_folder)
        os.mkdir(result_folder)

        # Make selection of the .asc files
        proc_ramms_folder_list = os.listdir(input_dir)
        maxHeight_list_path = []; maxVelocity_list_path = []
        maxHeight_list_name = []; maxVelocity_list_name = []

        for i in proc_ramms_folder_list:
            if  "MaxHeight" in i and i.endswith('.asc'):
                maxHeight_list_path.append(input_dir + "\\" + i)
                maxHeight_list_name.append(i[0:-4]) # takes out the extension from the name
            if "MaxVelocity" in i and i.endswith('.asc'): 
                maxVelocity_list_path.append(input_dir + "\\" + i)
                maxVelocity_list_name.append(i[0:-4])


        # Import excel file
        table_info, table_values = readExcelSheet(input_excel, start_nbr, end_nbr)

        project_nbr = table_info[3]  # e.g. A10061

        # Load anriss zones
        anriss_shp_list = listAnrissgebiete(anriss_folder)

        # Start loop over all .asc files
        for i in range(len(table_values)):

            # create new qgis project
            processing_name = str(table_values[i][13])
            anriss_name = table_values[i][1]
            qgis_project_name = result_folder + "\\" + processing_name + ".qgs"

            if os.path.isfile(qgis_project_name): # if .qgs project already exists, remove it (overwrites)
                os.remove(qgis_project_name)

            # create project instance
            project = QgsProject.instance()
            project.setCrs(QgsCoordinateReferenceSystem(2056))
            project.setFileName(qgis_project_name)
            project.write() # allows modification of the project

            output_layers = []

            # load rasters (velocity and height)
            r_height_asc = [x for x in maxHeight_list_path if anriss_name in x][0]
            r_height_name = [x for x in maxHeight_list_name if anriss_name in x][0]
            r_velocity_asc = [x for x in maxVelocity_list_path if anriss_name in x][0]
            r_velocity_name = [x for x in maxVelocity_list_name if anriss_name in x][0]

            #iface.mainWindow().blockSignals(True) # block pop-up window in .qgs

            # raster 1 (height)
            rlayer1 = QgsRasterLayer(r_height_asc, r_height_name)
            rlayer1.setCrs(QgsCoordinateReferenceSystem(2056))
            if not rlayer1.isValid():
                print("Height raster failed to load!")

            # Reclassify raster
            out_rl1 = result_folder + "\\" + r_height_name + ".sdat"
            reclass_raster = processing.run("saga:reclassifyvaluessimple", {'GRID_IN':rlayer1,
            'METHOD':2,
            'LOOKUP':[0,0.3,0,0.3,0.5,1,0.5,1.0,2,1.0,2.0,3,2.0,1000.0,4],
            'GRID_OUT':out_rl1})

            project.removeMapLayer(rlayer1)
            rlayer1_recl = QgsRasterLayer(out_rl1, "Max. Fliesshöhe (Rohdaten)")
            rlayer1_recl .setCrs(QgsCoordinateReferenceSystem(2056))

            # change raster 1 symbology
            raster_shader = QgsColorRampShader()
            raster_shader.setColorRampType(QgsColorRampShader.Exact)
            colors_list = [ 
                QgsColorRampShader.ColorRampItem(1, QColor.fromRgb(204, 219, 255), "0.3-0.5m"),
                QgsColorRampShader.ColorRampItem(2, QColor.fromRgb(102, 178, 255), "0.5-1.0m"),
                QgsColorRampShader.ColorRampItem(3, QColor.fromRgb(0, 128, 255), "1.0-2.0m"),
                QgsColorRampShader.ColorRampItem(4, QColor.fromRgb(0, 0, 153), ">2m")]

            raster_shader.setColorRampItemList(colors_list) 
            shader = QgsRasterShader()
            shader.setRasterShaderFunction(raster_shader)  
            renderer = QgsSingleBandPseudoColorRenderer(rlayer1_recl.dataProvider(), 1, shader)    
            rlayer1_recl.setRenderer(renderer)
            rlayer1_recl.triggerRepaint()    

            project.addMapLayer(rlayer1_recl)
            output_layers.append(out_rl1)

            # raster 2 (velocity)
            rlayer2 = QgsRasterLayer(r_velocity_asc, r_velocity_name)
            rlayer2.setCrs(QgsCoordinateReferenceSystem(2056))
            if not rlayer2.isValid():
                print("Velocity raster failed to load!")

            # Reclassify raster
            out_rl2 = result_folder + "\\" + r_velocity_name + ".sdat"
            reclass_raster = processing.run("saga:reclassifyvaluessimple", {'GRID_IN':rlayer2,
            'METHOD':2,
            'LOOKUP':[0.0,1.0,0,1.0,3.0,1,3.0,10.0,2,10.0,20.0,3,20.0,1000.0,4],
            'GRID_OUT':out_rl2})

            project.removeMapLayer(rlayer2)
            rlayer2_recl = QgsRasterLayer(out_rl2, "Max. Fliessgeschwindigkeit (Rohdaten)")
           rlayer2_recl.setCrs(QgsCoordinateReferenceSystem(2056))

            # change raster 2 symbology
            raster_shader = QgsColorRampShader()
            raster_shader.setColorRampType(QgsColorRampShader.Exact)
            colors_list = [ 
                QgsColorRampShader.ColorRampItem(1, QColor.fromRgb(255, 204, 255), "1.0-3.0m/s"),
                QgsColorRampShader.ColorRampItem(2, QColor.fromRgb(255, 102, 255), "3.0-10.0m/s"),
                QgsColorRampShader.ColorRampItem(3, QColor.fromRgb(204, 0, 204), "10.0-20.0m/s"),
                QgsColorRampShader.ColorRampItem(4, QColor.fromRgb(102, 0, 102), ">20m/s")]

            raster_shader.setColorRampItemList(colors_list) 
            shader = QgsRasterShader()
            shader.setRasterShaderFunction(raster_shader)  
            renderer = QgsSingleBandPseudoColorRenderer(rlayer2_recl.dataProvider(), 1, shader)    
            rlayer2_recl.setRenderer(renderer)
            rlayer2_recl.triggerRepaint()    

            project.addMapLayer(rlayer2_recl)
            output_layers.append(out_rl1)

            # Load anrissgebiet
            anriss_shp = [x for x in anriss_shp_list if anriss_name in x][0]

            #iface.mainWindow().blockSignals(True)

            shplayer = QgsVectorLayer(anriss_shp, anriss_name, "ogr")
            shplayer.setCrs(QgsCoordinateReferenceSystem(2056))
            if not shplayer.isValid():
                print("Anriss .shp failed to load!")

            project.addMapLayer(shplayer)
            output_layers.append(anriss_shp)


            # Save and close qgis document
            #iface.mainWindow().blockSignals(False)


            project.write(qgis_project_name)

            # clear current qgis project
            project.clear()

        return {self.OUTPUT_LAYERS: output_layers}

enter image description here

6
  • Does changing processing.run() to processing.runAndLoadResults() help? Commented Aug 20, 2019 at 8:25
  • Is it necessary to use project.write() four times in your script or could you use it once at the end?
    – Joseph
    Commented Aug 20, 2019 at 9:36
  • @ spatialthoughts: I just tried and nothing changed :-S
    – SaskiaG
    Commented Aug 20, 2019 at 11:17
  • @Joseph: No it's not necessary! I changed the code
    – SaskiaG
    Commented Aug 20, 2019 at 11:20
  • @SaskiaG - I wonder if the layers are not being added to the panel because of iface.mainWindow().blockSignals(True) as this tends to block all signals. I'm guessing you're using this to stop the CRS window from showing? Try removing these lines and add the layer using rlayer1 = QgsRasterLayer(path, 'someName'); rlayer1.setCrs(...); QgsProject.instance().addMapLayer(rlayer1)
    – Joseph
    Commented Aug 22, 2019 at 10:38

1 Answer 1

4

It would help if you could create a minimal version of your script.

I observed that output layers from sinks are automatically added to the project, e.g.:

def initAlgorithm(self, config=None):
    [...]
    self.addParameter(QgsProcessingParameterFeatureSink('Result', 'result', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue=None))

However, when using a dedicated output QgsProcessingOutputMultipleLayers you must call addLayerToLoadOnCompletion for the layers to be added to the project.

def initAlgorithm(self, config=None):
    [...]
    self.addOutput(QgsProcessingOutputMultipleLayers(self.OUTPUT_DEBUG, 'the debug output goes here'))

def processAlgorithm(self, parameters, context, model_feedback):
    # do some processing to get several result layers in result_layers_to_show
    [...]
    results[self.OUTPUT_DEBUG] = result_layers_to_show
    # add the first layer
    context.addLayerToLoadOnCompletion(result_layers_to_show[0], QgsProcessingContext.LayerDetails('first result layer', QgsProject.instance(), ''))
    return results

Your Answer

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

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