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}
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}
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}