Ejemplo n.º 1
0
    def processAlgorithm(self, parameters, context, model_feedback):
        ## Import des paramètres en tant que variables
        mnt = self.parameterAsRasterLayer(parameters, self.INPUT, context)
        iteration = self.parameterAsInt(parameters, self.ITERATION, context)
        bruitage_min = self.parameterAsDouble(parameters, self.BRUITAGE_MIN,
                                              context)
        bruitage_max = self.parameterAsDouble(parameters, self.BRUITAGE_MAX,
                                              context)
        seuil = self.parameterAsInt(parameters, self.SEUIL, context)
        tailleMinWatershed = self.parameterAsInt(
            parameters, self.MINIMUMSIZEOFEXTERIORWATERSHEDBASIN, context)
        supprFichiersIntermed = self.parameterAsBoolean(
            parameters, self.SUPPRFICHIERSINTERMED, context)
        mntExzeco = self.parameterAsOutputLayer(parameters, self.OUTPUT,
                                                context)

        ## Calcul des variables utiles au script
        # Pour le nom des fichiers créés
        len_iter = len(str(iteration))

        # Emprise du raster au format GRASS
        minX, minY, maxX, maxY = mnt.extent().toString(2).replace(
            " : ", ",").split(",")
        emprise = "{minX},{maxX},{minY},{maxY} [{scr_authid}]".format(
            minX=minX,
            maxX=maxX,
            minY=minY,
            maxY=maxY,
            scr_authid=mnt.crs().authid())

        # Résolution du MNT d'entrée pour la taille de la cellule GRASS
        taillePixel = mnt.rasterUnitsPerPixelX()

        # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
        # overall progress through the model
        feedback = QgsProcessingMultiStepFeedback(5 * iteration,
                                                  model_feedback)
        results = {}
        outputs = {}

        feedback.pushInfo("Emprise calculée : {}".format(emprise))
        feedback.pushInfo("Taille pixel : {}".format(taillePixel))

        for i in range(iteration):
            # Stop l'algorithme si Cancel est cliqué
            if feedback.isCanceled():
                return {}

            # Création des fichiers temporaires interméaires
            aleatoire = QgsProcessingUtils.generateTempFilename(
                str(i).zfill(len_iter) + "_aleatoire.tif")
            MNTBruite = QgsProcessingUtils.generateTempFilename(
                str(i).zfill(len_iter) + "_MNTBruite.tif")
            surfacesDrainees = QgsProcessingUtils.generateTempFilename(
                str(i).zfill(len_iter) + "_surfacesDrainees.tif")
            surfacesDraineesSeuil = QgsProcessingUtils.generateTempFilename(
                str(i).zfill(len_iter) + "_surfacesDraineesSeuil.tif")
            surfacesDraineesSeuilMax = QgsProcessingUtils.generateTempFilename(
                str(i).zfill(len_iter) + "_surfacesDraineesSeuilMax.tif")

            feedback.setProgressText(
                "Itération {}/{} : Création du raster aléatoire".format(
                    i + 1, iteration))
            feedback.setCurrentStep(5 * i)

            # Créer une couche raster aléatoire (distribution uniforme)
            alg_params = {
                "EXTENT": emprise,
                "LOWER_BOUND": bruitage_min,
                "OUTPUT_TYPE": 5,
                "PIXEL_SIZE": taillePixel,
                "TARGET_CRS": "ProjectCrs",
                "UPPER_BOUND": bruitage_max,
                "OUTPUT": aleatoire,
            }
            outputs["aleatoire"] = processing.run(
                "native:createrandomuniformrasterlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setProgressText(
                "Itération {}/{} : Bruitage du MNT source".format(
                    i + 1, iteration))
            feedback.setCurrentStep(5 * i + 1)
            if feedback.isCanceled():
                return {}

            # Bruitage du MNT source à l'aide du raster aléatoire créé précédemment
            alg_params = default_algCalc_params.copy()

            alg_params["INPUT_A"] = aleatoire
            alg_params["INPUT_B"] = mnt.source()
            alg_params["BAND_B"] = 1
            alg_params[
                "FORMULA"] = "A + B"  # Explications formule complexe OAT ?
            alg_params["OUTPUT"] = MNTBruite

            outputs["CalculatriceRaster"] = processing.run(
                "gdal:rastercalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setProgressText(
                "Itération {}/{} : Calcul des talwegs à partir du MNT bruité".
                format(i + 1, iteration))
            feedback.setCurrentStep(5 * i + 2)
            if feedback.isCanceled():
                return {}

            # Calcul des talwegs
            alg_params = {
                "elevation": MNTBruite,
                "accumulation": surfacesDrainees,
                "-4": True,
                "-a": True,
                "-b": True,
                "-m": False,
                "-s": False,
                "GRASS_RASTER_FORMAT_META": "",
                "GRASS_RASTER_FORMAT_OPT": "",
                "GRASS_REGION_CELLSIZE_PARAMETER": 0,
                "GRASS_REGION_PARAMETER": None,
                "blocking": None,
                "convergence": 5,
                "depression": None,
                "disturbed_land": None,
                "flow": None,
                "max_slope_length": None,
                "memory": 300,
                "threshold": tailleMinWatershed,
            }
            outputs["Rwatershed"] = processing.run(
                "grass7:r.watershed",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setProgressText(
                "Itération {}/{} : Suppression des cellules dont la valeur drainée est inférieure au seuil"
                .format(i + 1, iteration))
            feedback.setCurrentStep(5 * i + 3)
            if feedback.isCanceled():
                return {}

            # Suppression des cellules dont la valeur drainée est inférieure au seuil
            alg_params = default_algCalc_params.copy()

            alg_params["INPUT_A"] = surfacesDrainees
            alg_params[
                "FORMULA"] = "(A > {seuil}) * A + (A <= {seuil}) * 0".format(
                    seuil=seuil)
            alg_params["OUTPUT"] = surfacesDraineesSeuil

            outputs["CalculatriceRaster"] = processing.run(
                "gdal:rastercalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setProgressText(
                "Itération {}/{} : Calcul des maximums des cellules drainées".
                format(i + 1, iteration))
            feedback.setCurrentStep(5 * i + 4)
            if feedback.isCanceled():
                return {}

            # Initialisation de variables pour utilisation à l'itération suivante
            if i == 0:
                surfacesDraineesSeuilMax_iterPrec = surfacesDraineesSeuil

            elif i == iteration - 1:
                alg_params = default_algCalc_params.copy()

                alg_params["INPUT_A"] = surfacesDraineesSeuil
                alg_params["INPUT_B"] = surfacesDraineesSeuilMax_iterPrec
                alg_params["BAND_B"] = 1
                alg_params[
                    "FORMULA"] = "(greater_equal(A, B)) * A + (greater(B, A)) * B"
                alg_params["OUTPUT"] = mntExzeco

                outputs["CalculatriceRaster"] = processing.run(
                    "gdal:rastercalculator",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

                # Cas particulier de la suppression des fichiers intermédiaires
                if supprFichiersIntermed:
                    self.suppressionFichier(surfacesDraineesSeuilMax_iterPrec,
                                            outputs, context, feedback)

            else:
                alg_params = default_algCalc_params.copy()

                alg_params["INPUT_A"] = surfacesDraineesSeuil
                alg_params["INPUT_B"] = surfacesDraineesSeuilMax_iterPrec
                alg_params["BAND_B"] = 1
                alg_params[
                    "FORMULA"] = "(greater_equal(A, B)) * A + (greater(B, A)) * B"
                alg_params["OUTPUT"] = surfacesDraineesSeuilMax

                outputs["CalculatriceRaster"] = processing.run(
                    "gdal:rastercalculator",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

                # Cas particulier de la suppression des fichiers intermédiaires
                if supprFichiersIntermed:
                    self.suppressionFichier(surfacesDraineesSeuilMax_iterPrec,
                                            outputs, context, feedback)

                surfacesDraineesSeuilMax_iterPrec = surfacesDraineesSeuilMax

            # Suppression des fichiers intermédaires si le paramètre est coché (coché par défaut)
            if supprFichiersIntermed:
                self.suppressionFichier(aleatoire, outputs, context, feedback)
                self.suppressionFichier(MNTBruite, outputs, context, feedback)
                self.suppressionFichier(surfacesDrainees, outputs, context,
                                        feedback)
                if i > 0:
                    self.suppressionFichier(surfacesDraineesSeuil, outputs,
                                            context, feedback)

        return {self.OUTPUT: mntExzeco}
Ejemplo n.º 2
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        # Check if version and resolution parameters are compatible
        compatibility = {
            'I DHM': ['5m', '25m', '100m'],
            'II DTM': ['1m', '5m', '25m', '100m'],
            'II DSM': ['1m', '5m']
        }
        dhmv = self.DHMV_ENUM[self.parameterAsEnum(parameters, self.DHMV, context)]
        resolution = self.RESOLUTION_ENUM[self.parameterAsEnum(parameters, self.RESOLUTION, context)]
        if resolution not in compatibility[dhmv]:
            raise QgsProcessingException(f'Resolution {resolution} not available for DHMV {dhmv}.\n\
                Resolutions {compatibility[dhmv]} are available for DHMV {dhmv}.')

        if feedback.isCanceled():
            return {}

        output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)

        # Determine feature kaartbladen
        feedback.setProgressText(self.tr('Determining kaartbladen...'))
        if (dhmv[:2] == 'I ' and resolution == '100m') or (dhmv[:2] == 'II' and resolution in ('25m', '100m')):
            # Don't download the same Vlaanderen kaartblad for high resolutions
            kbls = {'1'}
        else:
            kbl = processing.run(
                'native:extractbylocation', 
                { 
                    'INPUT' : QgsVectorLayer(os.path.join(os.path.dirname(__file__), 'Kbl/Kbl.shp')),
                    'INTERSECT' : parameters['INPUT'], 
                    'METHOD' : 0, 
                    'PREDICATE' : [0,1,6], 
                    'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
                },
                is_child_algorithm=True, 
                context=context, 
                feedback=feedback
            )['OUTPUT']

            kbls = {int(feature.attribute('CODE')) for feature in context.getMapLayer(kbl).getFeatures()}
        
        if feedback.isCanceled():
            return {}

        # Split feedback in multiple steps according to the amount of kaartbladen
        multistep_feedback = QgsProcessingMultiStepFeedback(len(kbls) + 1, feedback)

        # Download DHM kaartbladen
        dhms = []
        for index, kbl in enumerate(kbls):
            multistep_feedback.setCurrentStep(index)
            multistep_feedback.setProgressText(f"Downloading kaartblad {index+1}/{len(kbls)}...")
            # Download zipfiles
            if dhmv == 'I DHM':
                url = f'https://downloadagiv.blob.core.windows.net/digitaal-hoogtemodel-vlaanderen-raster-{resolution}/geoTIFF/{f"Gegroepeerd%20per%20kaartblad/R{resolution[:-1]}_{kbl:02d}.zip" if resolution != "100m" else "R100.zip"}'
            else:
                url = f'https://downloadagiv.blob.core.windows.net/dhm-vlaanderen-ii-{dhmv[-3:].lower()}-raster-{resolution}/DHMVII{dhmv[-3:]}RAS{resolution}{f"_k{kbl:02d}.zip" if resolution in ("1m", "5m") else ".zip"}'
            zip_file_name = processing.run(
                'native:filedownloader', 
                {
                    'URL': url, 
                    'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
                },
                is_child_algorithm=True,
                context=context,
                feedback=multistep_feedback
            )['OUTPUT']

            if multistep_feedback.isCanceled():
                return {}
            
            # Extract tiffs from zipfile
            feedback.setProgressText(f'Extracting {index+1}/{len(kbls)}...')
            with zipfile.ZipFile(zip_file_name) as zpf:
                for name in zpf.namelist():
                    if name.endswith('.tif') or name.endswith('.tiff'):
                        dhms.append(QgsRasterLayer(zpf.extract(name, path=os.path.dirname(zip_file_name))))
                    if name.endswith('.tfw'):
                        zpf.extract(name, path=os.path.dirname(zip_file_name))
            
            if multistep_feedback.isCanceled():
                return {}
        
        multistep_feedback.setCurrentStep(len(kbls))
        # Divide the last step in more steps for the last algorithms to come
        multistep_feedback1 = QgsProcessingMultiStepFeedback(2, multistep_feedback)

        multistep_feedback1.setCurrentStep(0)
        multistep_feedback1.setProgressText("Building Virtual Raster...")
        # Build a virtual raster of the downloaded tiffs
        vrt = processing.run(
            'gdal:buildvirtualraster', 
            {
                'INPUT': dhms, 
                'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT,
                'SEPARATE': False, 
                'SRC_NODATA': '-9999'
            },
            is_child_algorithm=True,
            context=context,
            feedback=multistep_feedback1
        )['OUTPUT']

        if multistep_feedback1.isCanceled():
            return {}

        multistep_feedback1.setCurrentStep(1)
        multistep_feedback1.setProgressText('Clipping to layer...')
        # Clip the DHM to to study area
        output = processing.run(
            'gdal:cliprasterbymasklayer',
            {
                'INPUT': vrt,
                'MASK': parameters['INPUT'],
                'CROP_TO_CUTLINE': True,
                'KEEP_RESOLUTION': True,
                'OUTPUT': output
            },
            is_child_algorithm=True,
            context=context,
            feedback=multistep_feedback1
        )['OUTPUT']

        feedback.setProgressText('Done.')

        # Return the results
        return {self.OUTPUT: output}
Ejemplo n.º 3
0
    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(4, model_feedback)
        outputs = {}
        self.doRenderer = False
        feedback.setProgressText(
            self.
            tr('Preprocessing input (fix geometries, explode lines and remove null geometries)'
               ))
        # Fix geometries
        alg_params = {
            'INPUT': parameters[self.INPUT],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['FixGeometries'] = processing.run('native:fixgeometries',
                                                  alg_params,
                                                  context=context,
                                                  feedback=feedback,
                                                  is_child_algorithm=True)

        if feedback.isCanceled():
            return {}

        # Explode lines
        alg_params = {
            'INPUT': outputs['FixGeometries']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['ExplodeLines'] = processing.run('native:explodelines',
                                                 alg_params,
                                                 context=context,
                                                 feedback=feedback,
                                                 is_child_algorithm=True)

        if feedback.isCanceled():
            return {}

        # Remove null geometries
        alg_params = {
            'INPUT': outputs['ExplodeLines']['OUTPUT'],
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['RemoveNullGeometries'] = processing.run(
            'native:removenullgeometries',
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True)

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

        source = self.parameterAsSource(outputs['RemoveNullGeometries'],
                                        'OUTPUT', context)
        fields = source.fields()

        cluster_field = self.parameterAsString(parameters, self.CLUSTER_FIELD,
                                               context)
        compatibility = self.parameterAsDouble(parameters, self.COMPATIBILITY,
                                               context)
        cycles = self.parameterAsInt(parameters, self.CYCLES, context)
        iterations = self.parameterAsInt(parameters, self.ITERATIONS, context)
        initial_step_size = self.parameterAsDouble(parameters,
                                                   self.INITIAL_STEP_SIZE,
                                                   context)
        max_distance = self.parameterAsDouble(parameters, self.MAX_DISTANCE,
                                              context)
        self.max_distance = max_distance
        weight_field = self.parameterAsString(parameters, self.WEIGHT_FIELD,
                                              context)
        weight_field_index = None
        if weight_field != '':
            weight_field_index = fields.lookupField(weight_field)

        # Create edge list
        features = source.getFeatures(QgsFeatureRequest())
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        edges = []
        for current, feat in enumerate(features):
            if feedback.isCanceled(): break
            edges.append(Edge(feat, weight_field_index))

        # Create clusters
        clusters = []
        if cluster_field != '':
            # Arrange edges in clusters according to cluster-id
            labels = []
            for edge in edges:
                if feedback.isCanceled(): return {}
                labels.append(edge[cluster_field])
            feedback.pushDebugInfo(cluster_field)
            for l in range(0, len(labels) + 1):
                if feedback.isCanceled(): return {}
                clusters.append(list())
            for i, label in enumerate(labels):
                if feedback.isCanceled(): return {}
                if label >= 0:
                    clusters[label].append(edges[i])
                else:
                    clusters.append([edges[i]])
            for i, cluster in enumerate(clusters):
                if feedback.isCanceled(): return {}
                clusters[i] = EdgeCluster(cluster, initial_step_size,
                                          iterations, cycles, compatibility)
        else:
            # If clustering should not be used, create only one big cluster containing all edges
            clusters = [
                EdgeCluster(edges, initial_step_size, iterations, cycles,
                            compatibility)
            ]
        fields.append(QgsField('CLUSTER', QVariant.Int))
        cluster_index = fields.lookupField('CLUSTER')
        if max_distance > 0:
            self.doRenderer = True
            fields.append(QgsField('PATH', QVariant.Int))
            fields.append(QgsField('OVERLAP_COUNT', QVariant.Int))
            overlap_index = fields.lookupField('OVERLAP_COUNT')

        (sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                                    context, fields,
                                                    source.wkbType(),
                                                    source.sourceCrs())

        # Do edge-bundling (separately for all clusters)
        if max_distance == 0:
            feedback.setProgressText(
                self.tr('Compute Flow without merging lines'))
            for c, cl in enumerate(clusters):
                if feedback.isCanceled(): return {}
                if cl.E > 1:
                    cl.force_directed_eb(feedback)
            feedback.setCurrentStep(3)
            for id, cl in enumerate(clusters):
                if feedback.isCanceled(): return {}
                feedback.setProgress(100.0 * id / len(clusters))
                for e, edge in enumerate(cl.edges):
                    feat = QgsFeature()
                    feat.setGeometry(edge.geometry())
                    attr = edge.attributes()
                    attr.append(id)
                    feat.setAttributes(attr)
                    sink.addFeature(feat, QgsFeatureSink.FastInsert)
        else:
            feedback.setProgressText(
                self.tr('Compute Flow and try to merge lines'))
            for c, cl in enumerate(clusters):
                if feedback.isCanceled(): return {}
                if cl.E > 1:
                    cl.force_directed_eb(feedback)
            feedback.setCurrentStep(3)
            for id, cl in enumerate(clusters):
                if feedback.isCanceled(): return {}
                feedback.setProgressText(
                    "Segment lines for cluster {0}".format(id))
                cl.create_segments(weight_field_index, feedback)
                feedback.setProgressText(
                    "Collapse lines for cluster {0}".format(id))
                cl.collapse_lines(max_distance, feedback)
            fid = 0
            for id, cl in enumerate(clusters):
                if feedback.isCanceled(): return {}
                for e, edge in enumerate(cl.edges):
                    segments = cl.get_segments(edge)
                    for key, segment in segments.items():
                        feat = QgsFeature()
                        feat.setGeometry(segment.geometry())
                        attr = edge.attributes()
                        path_id = attr[0]
                        attr[0] = fid
                        attr.append(id)
                        attr.append(path_id)
                        attr.append(int(
                            segment.get_agg_weight()))  #Overlap count
                        feat.setAttributes(attr)
                        sink.addFeature(feat, QgsFeatureSink.FastInsert)
                        fid += 1

        return {self.OUTPUT: self.dest_id}