def processAlgorithm(self, parameters, context, feedback):
        communes = self.parameterAsString(parameters, self.LISTE_CODE_INSEE,
                                          context)
        filtre = self.parameterAsString(parameters, self.FILTRE, context)
        date = self.parameterAsString(parameters, self.DATE, context)
        url = self.parameterAsString(parameters, self.URL_TEMPLATE, context)
        directory = Path(
            self.parameterAsString(parameters, self.DOSSIER, context))

        if not directory.exists():
            feedback.pushDebugInfo(
                "Création du répertoire {}".format(directory))
            os.makedirs(directory, exist_ok=True)

        filtre = [c.strip() for c in filtre.split(',')]

        communes = [c.strip() for c in communes.split(',')]
        departements = []
        self.results = {
            self.DOSSIER: str(directory),
            self.NB_COMMUNES: len(communes),
            self.NB_FEUILLES: 0,
            self.DEPARTEMENTS: "",
        }

        multi_feedback = QgsProcessingMultiStepFeedback(
            len(communes), feedback)

        for i, commune_insee in enumerate(communes):

            commune = Commune(commune_insee, date=date, base_url=url)
            if not self.download_commune(directory, commune, filtre,
                                         multi_feedback, context):
                multi_feedback.reportError("Erreur sur la commune {}".format(
                    commune.insee))
                break

            if multi_feedback.isCanceled():
                break

            multi_feedback.setCurrentStep(i)

            if commune.departement not in departements:
                departements.append(commune.departement)

        self.results[self.DEPARTEMENTS] = ','.join(departements)

        multi_feedback.pushInfo("\n")
        multi_feedback.pushInfo("\n")
        multi_feedback.pushInfo(
            "Téléchargement terminé pour {} communes".format(len(communes)))
        multi_feedback.pushInfo("{} feuilles".format(
            self.results[self.NB_FEUILLES]))
        multi_feedback.pushInfo("dans {}".format(str(directory)))
        multi_feedback.pushInfo("\n")
        multi_feedback.pushInfo("\n")
        return self.results
Beispiel #2
0
 def processAlgorithm(self, parameters, context, model_feedback):
     """
     Process the algorithm
     :param parameters: parameters of the process
     :param context: context of the process
     :param model_feedback: feedback instance for the process
     :return:
     """
     # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
     # overall progress through the model
     self.xml_path = parameters["XMLPATH"]
     if not self.xml_path.lower().endswith(".xml"):
         feedback = QgsProcessingMultiStepFeedback(0, model_feedback)
         feedback.reportError("XML Workspace Definition is not an XML file!", True)
         return {}
     self.pg_conn_name = parameters["DBNAME"]
     self.pg_schema = parameters["SCHEMA"]
     self.pg_drop_before = parameters["DROPIFEXISTS"]
     domain_list = self.getDomains()
     feedback = QgsProcessingMultiStepFeedback(1+len(domain_list), model_feedback)        
     step=0
     for domain in domain_list:
         step+=1
         definition = self.getDomainDef(domain)
         try:
             alg_params = {
                 'DATABASE': self.pg_conn_name,
                 'SQL': definition[1]
             }
             processing.run(
                 'qgis:postgisexecutesql',
                 alg_params, context=context, feedback=feedback, is_child_algorithm=True)
             feedback.pushInfo("Domain: " + definition[0])
             feedback.pushInfo("   SQL: " + definition[1])
             feedback.pushInfo("   Rows: " + str(definition[2]))
         except Exception as e:
             feedback.reportError("Error importing domain " + definition[0] + ": " + str(e), False)
         feedback.setCurrentStep(step)
     results = {}
     outputs = {}
     return results
Beispiel #3
0
    def processAlgorithm(self, params, context, feedback):
        isValid = lambda x: 0 if x is None else 1
        isBusStop = isValid(params['BUSSTOP'])
        isTramStop = isValid(params['TRAMSTOP'])
        isBikeStop = isValid(params['BIKESTOP'])
        isBikeWay = isValid(params['BIKEWAY'])
        isCrossWalk = isValid(params['CROSSWALK'])
        isRoads = isValid(params['ROADS'])
        totalValides = isBusStop + isTramStop + isBikeStop + isBikeWay + isCrossWalk

        if (totalValides >= 3):
            if isRoads == 0 and params['DISTANCE_OPTIONS'] == 0:
                feedback.reportError(
                    str(('Distancia isocrona requiere la red vial')))
                return {}

            steps = 0
            totalStpes = 37
            fieldPopulateOrHousing = params['FIELD_POPULATE_HOUSING']
            DISTANCE_BUSSTOP = 300
            DISTANCE_TRAMSTOP = 500
            DISTANCE_BKESTOP = 300
            DISTANCE_BIKEWAY = 300
            DISTANCE_CROSSWALK = 300

            MIN_FACILITIES = 3
            OPERATOR_GE = 3

            feedback = QgsProcessingMultiStepFeedback(totalStpes, feedback)
            """
        -----------------------------------------------------------------
        Calcular las facilidades
        -----------------------------------------------------------------
        """

            steps = steps + 1
            feedback.setCurrentStep(steps)
            if not OPTIONAL_GRID_INPUT: params['CELL_SIZE'] = P_CELL_SIZE
            grid, isStudyArea = buildStudyArea(params['CELL_SIZE'],
                                               params['BLOCKS'],
                                               params['STUDY_AREA_GRID'],
                                               context, feedback)
            gridNeto = grid

            steps = steps + 1
            feedback.setCurrentStep(steps)
            blocks = calculateArea(params['BLOCKS'], 'area_bloc', context,
                                   feedback)

            steps = steps + 1
            feedback.setCurrentStep(steps)
            segments = intersection(blocks['OUTPUT'], gridNeto['OUTPUT'],
                                    'area_bloc;' + fieldPopulateOrHousing,
                                    'id_grid', context, feedback)

            steps = steps + 1
            feedback.setCurrentStep(steps)
            segmentsArea = calculateArea(segments['OUTPUT'], 'area_seg',
                                         context, feedback)

            steps = steps + 1
            feedback.setCurrentStep(steps)
            formulaPopulationSegments = '(area_seg/area_bloc) * ' + fieldPopulateOrHousing
            populationForSegments = calculateField(segmentsArea['OUTPUT'],
                                                   'pop_seg',
                                                   formulaPopulationSegments,
                                                   context, feedback)
            steps = steps + 1
            feedback.setCurrentStep(steps)
            blocksWithId = calculateField(populationForSegments['OUTPUT'],
                                          'id_block',
                                          '$id',
                                          context,
                                          feedback,
                                          type=1)

            steps = steps + 1
            feedback.setCurrentStep(steps)
            centroidsBlocks = createCentroids(blocksWithId['OUTPUT'], context,
                                              feedback)

            result = []

            idxs = [
                'idxbus', 'idxtram', 'idxbikestop', 'idkbikeway', 'idxwalk'
            ]

            layers = []

            if (params['DISTANCE_OPTIONS'] == 0):
                steps = steps + 1
                feedback.setCurrentStep(steps)
                feedback.pushConsoleInfo(str(('Cálculo de áreas de servicio')))

                pointsBikeWay = pointsAlongLines(params['BIKEWAY'], 50,
                                                 context, feedback)
                pointsCrossWalk = pointsAlongLines(params['CROSSWALK'], 50,
                                                   context, feedback)

                if isBusStop == 1:
                    layers.append([
                        params['BUSSTOP'], STRATEGY_DISTANCE, DISTANCE_BUSSTOP
                    ])
                if isTramStop == 1:
                    layers.append([
                        params['TRAMSTOP'], STRATEGY_DISTANCE,
                        DISTANCE_TRAMSTOP
                    ])
                if isBikeStop == 1:
                    layers.append([
                        params['BIKESTOP'], STRATEGY_DISTANCE, DISTANCE_BKESTOP
                    ])
                if isBikeWay == 1:
                    layers.append([
                        pointsBikeWay['OUTPUT'], STRATEGY_DISTANCE,
                        DISTANCE_BIKEWAY
                    ])
                if isCrossWalk == 1:
                    layers.append([
                        pointsCrossWalk['OUTPUT'], STRATEGY_DISTANCE,
                        DISTANCE_CROSSWALK
                    ])

                serviceAreas = multiBufferIsocrono(params['ROADS'], layers,
                                                   context, feedback)

                iidx = -1
                for serviceArea in serviceAreas:
                    iidx = iidx + 1
                    idx = idxs[iidx]
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    serviceArea = calculateField(serviceArea,
                                                 idx,
                                                 '$id',
                                                 context,
                                                 feedback,
                                                 type=1)
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    centroidsBlocks = joinByLocation(centroidsBlocks['OUTPUT'],
                                                     serviceArea['OUTPUT'],
                                                     [idx], [INTERSECTA],
                                                     [COUNT],
                                                     UNDISCARD_NONMATCHING,
                                                     context, feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                # formulaDummy = 'idxbus_count * 1'
                formulaDummy = 'coalesce(idxbus_count, 0) + coalesce(idxtram_count, 0) + coalesce(idxbikestop_count,0) + coalesce(idkbikeway_count, 0) + coalesce(idxwalk_count, 0)'
                facilitiesCover = calculateField(centroidsBlocks['OUTPUT'],
                                                 'facilities', formulaDummy,
                                                 context, feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                facilitiesFullCover = filter(facilitiesCover['OUTPUT'],
                                             'facilities', OPERATOR_GE,
                                             MIN_FACILITIES, context, feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                gridNetoFacilitiesCover = joinByLocation(
                    gridNeto['OUTPUT'], facilitiesCover['OUTPUT'],
                    ['pop_seg', 'facilities'], [CONTIENE], [SUM],
                    UNDISCARD_NONMATCHING, context, feedback)

                fieldsMapping = [{
                    'expression': '"id_grid"',
                    'length': 10,
                    'name': 'id_grid',
                    'precision': 0,
                    'type': 4
                }, {
                    'expression': '"area_grid"',
                    'length': 16,
                    'name': 'area_grid',
                    'precision': 3,
                    'type': 6
                }, {
                    'expression': '"pop_seg_sum"',
                    'length': 20,
                    'name': 'ptotal',
                    'precision': 2,
                    'type': 6
                }, {
                    'expression': '"facilities_sum"',
                    'length': 20,
                    'name': 'facilities',
                    'precision': 2,
                    'type': 6
                }]

                steps = steps + 1
                feedback.setCurrentStep(steps)
                gridNetoFacilitiesCover = refactorFields(
                    fieldsMapping, gridNetoFacilitiesCover['OUTPUT'], context,
                    feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                gridNetoFacilities = joinByLocation(
                    gridNetoFacilitiesCover['OUTPUT'],
                    facilitiesFullCover['OUTPUT'], ['pop_seg'], [CONTIENE],
                    [SUM], UNDISCARD_NONMATCHING, context, feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                formulaProximity = 'coalesce((coalesce(pop_seg_sum,0) / coalesce(ptotal,""))*100,"")'
                proximity2AlternativeTransport = calculateField(
                    gridNetoFacilities['OUTPUT'], NAMES_INDEX['IC04'][0],
                    formulaProximity, context, feedback, params['OUTPUT'])

                result = proximity2AlternativeTransport

            else:
                feedback.pushConsoleInfo(str(('Cálculo de buffer radial')))
                blocksJoined = blocksWithId

                steps = steps + 1
                feedback.setCurrentStep(steps)
                blockBuffer4BusStop = createBuffer(centroidsBlocks['OUTPUT'],
                                                   DISTANCE_BUSSTOP, context,
                                                   feedback)

                # ------------------------------------

                if isBusStop == 1:
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    layerBusStop = calculateField(params['BUSSTOP'],
                                                  'idx',
                                                  '$id',
                                                  context,
                                                  feedback,
                                                  type=1)

                    layerBusStop = layerBusStop['OUTPUT']
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    counterBusStop = joinByLocation(
                        blockBuffer4BusStop['OUTPUT'], layerBusStop, 'idx',
                        [INTERSECTA], [COUNT], UNDISCARD_NONMATCHING, context,
                        feedback)
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    blocksJoined = joinByAttr(blocksJoined['OUTPUT'],
                                              'id_block',
                                              counterBusStop['OUTPUT'],
                                              'id_block', 'idx_count',
                                              UNDISCARD_NONMATCHING, 'bs_',
                                              context, feedback)

                # ---------------------------------------------------
                if isTramStop == 1:
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    blockBuffer4TramStop = createBuffer(
                        centroidsBlocks['OUTPUT'], DISTANCE_TRAMSTOP, context,
                        feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    layerTramStop = calculateField(params['TRAMSTOP'],
                                                   'idx',
                                                   '$id',
                                                   context,
                                                   feedback,
                                                   type=1)

                    layerTramStop = layerTramStop['OUTPUT']
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    counterTramStop = joinByLocation(
                        blockBuffer4TramStop['OUTPUT'], layerTramStop, 'idx',
                        [INTERSECTA], [COUNT], UNDISCARD_NONMATCHING, context,
                        feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    blocksJoined = joinByAttr(blocksJoined['OUTPUT'],
                                              'id_block',
                                              counterTramStop['OUTPUT'],
                                              'id_block', 'idx_count',
                                              UNDISCARD_NONMATCHING, 'ts_',
                                              context, feedback)

                # -----------------------------------------------
                if isBikeStop == 1:
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    blockBuffer4BikeStop = createBuffer(
                        centroidsBlocks['OUTPUT'], DISTANCE_BKESTOP, context,
                        feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    layerBikeStop = calculateField(params['BIKESTOP'],
                                                   'idx',
                                                   '$id',
                                                   context,
                                                   feedback,
                                                   type=1)

                    layerBikeStop = layerBikeStop['OUTPUT']
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    counteBikeStop = joinByLocation(
                        blockBuffer4BikeStop['OUTPUT'], layerBikeStop, 'idx',
                        [INTERSECTA], [COUNT], UNDISCARD_NONMATCHING, context,
                        feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    blocksJoined = joinByAttr(blocksJoined['OUTPUT'],
                                              'id_block',
                                              counteBikeStop['OUTPUT'],
                                              'id_block', 'idx_count',
                                              UNDISCARD_NONMATCHING, 'bks_',
                                              context, feedback)

                # -----------------------------------------

                if isBikeWay == 1:
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    BlockBuffer4BikeWay = createBuffer(
                        centroidsBlocks['OUTPUT'], DISTANCE_BIKEWAY, context,
                        feedback)

                    pointsBikeWay = pointsAlongLines(params['BIKEWAY'], 50,
                                                     context, feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    layerBikeWay = calculateField(pointsBikeWay['OUTPUT'],
                                                  'idx',
                                                  '$id',
                                                  context,
                                                  feedback,
                                                  type=1)

                    layerBikeWay = layerBikeWay['OUTPUT']
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    counterBikeWay = joinByLocation(
                        BlockBuffer4BikeWay['OUTPUT'], layerBikeWay, 'idx',
                        [INTERSECTA], [COUNT], UNDISCARD_NONMATCHING, context,
                        feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    blocksJoined = joinByAttr(blocksJoined['OUTPUT'],
                                              'id_block',
                                              counterBikeWay['OUTPUT'],
                                              'id_block', 'idx_count',
                                              UNDISCARD_NONMATCHING, 'bw_',
                                              context, feedback)

                # ------------------------------------------
                if isCrossWalk == 1:
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    BlockBuffer4CrossWalk = createBuffer(
                        centroidsBlocks['OUTPUT'], DISTANCE_CROSSWALK, context,
                        feedback)

                    pointsCrossWalk = pointsAlongLines(params['CROSSWALK'], 50,
                                                       context, feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    layerCrossWalk = calculateField(pointsCrossWalk['OUTPUT'],
                                                    'idx',
                                                    '$id',
                                                    context,
                                                    feedback,
                                                    type=1)

                    layerCrossWalk = layerCrossWalk['OUTPUT']
                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    counterCrossWalk = joinByLocation(
                        BlockBuffer4CrossWalk['OUTPUT'], layerCrossWalk, 'idx',
                        [INTERSECTA], [COUNT], UNDISCARD_NONMATCHING, context,
                        feedback)

                    steps = steps + 1
                    feedback.setCurrentStep(steps)
                    blocksJoined = joinByAttr(blocksJoined['OUTPUT'],
                                              'id_block',
                                              counterCrossWalk['OUTPUT'],
                                              'id_block', 'idx_count',
                                              UNDISCARD_NONMATCHING, 'cw_',
                                              context, feedback)
                # --------------------------------------------

                #TODO: CAMBIAR POR UN METODO BUCLE

                formulaParseBS = 'CASE WHEN coalesce(bs_idx_count, 0) > 0 THEN 1 ELSE 0 END'
                steps = steps + 1
                feedback.setCurrentStep(steps)
                blocksFacilities = calculateField(blocksJoined['OUTPUT'],
                                                  'parse_bs', formulaParseBS,
                                                  context, feedback)

                formulaParseTS = 'CASE WHEN coalesce(ts_idx_count, 0) > 0 THEN 1 ELSE 0 END'
                steps = steps + 1
                feedback.setCurrentStep(steps)
                blocksFacilities = calculateField(blocksFacilities['OUTPUT'],
                                                  'parse_ts', formulaParseTS,
                                                  context, feedback)

                formulaParseBKS = 'CASE WHEN coalesce(bks_idx_count, 0) > 0 THEN 1 ELSE 0 END'
                steps = steps + 1
                feedback.setCurrentStep(steps)
                blocksFacilities = calculateField(blocksFacilities['OUTPUT'],
                                                  'parse_bks', formulaParseBKS,
                                                  context, feedback)

                formulaParseBW = 'CASE WHEN coalesce(bw_idx_count, 0) > 0 THEN 1 ELSE 0 END'
                steps = steps + 1
                feedback.setCurrentStep(steps)
                blocksFacilities = calculateField(blocksFacilities['OUTPUT'],
                                                  'parse_bw', formulaParseBW,
                                                  context, feedback)

                formulaParseCW = 'CASE WHEN coalesce(cw_idx_count, 0) > 0 THEN 1 ELSE 0 END'
                steps = steps + 1
                feedback.setCurrentStep(steps)
                blocksFacilities = calculateField(blocksFacilities['OUTPUT'],
                                                  'parse_cw', formulaParseCW,
                                                  context, feedback)

                formulaFacilities = 'parse_bs + parse_ts + parse_bks + parse_bw + parse_cw'

                steps = steps + 1
                feedback.setCurrentStep(steps)
                blocksFacilities = calculateField(blocksFacilities['OUTPUT'],
                                                  'facilities',
                                                  formulaFacilities, context,
                                                  feedback)

                # Haciendo el buffer inverso aseguramos que los segmentos
                # quden dentro de la malla
                steps = steps + 1
                feedback.setCurrentStep(steps)
                facilitiesForSegmentsFixed = makeSureInside(
                    blocksFacilities['OUTPUT'], context, feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                gridNetoAndSegments = joinByLocation(
                    gridNeto['OUTPUT'], facilitiesForSegmentsFixed['OUTPUT'],
                    'bs_idx_count;ts_idx_count;bks_idx_count;bw_idx_count;cw_idx_count;facilities;pop_seg',
                    [CONTIENE], [MAX, SUM], UNDISCARD_NONMATCHING, context,
                    feedback)

                # tomar solo los que tienen cercania simultanea (descartar lo menores de 3)
                steps = steps + 1
                feedback.setCurrentStep(steps)
                facilitiesNotNullForSegmentsFixed = filter(
                    facilitiesForSegmentsFixed['OUTPUT'], 'facilities',
                    OPERATOR_GE, MIN_FACILITIES, context, feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                gridNetoAndSegmentsSimulta = joinByLocation(
                    gridNeto['OUTPUT'],
                    facilitiesNotNullForSegmentsFixed['OUTPUT'], 'pop_seg',
                    [CONTIENE], [MAX, SUM], UNDISCARD_NONMATCHING, context,
                    feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                totalHousing = joinByAttr(gridNetoAndSegments['OUTPUT'],
                                          'id_grid',
                                          gridNetoAndSegmentsSimulta['OUTPUT'],
                                          'id_grid', 'pop_seg_sum',
                                          UNDISCARD_NONMATCHING, 'net_',
                                          context, feedback)

                steps = steps + 1
                feedback.setCurrentStep(steps)
                formulaProximity = 'coalesce((coalesce(net_pop_seg_sum,0) /  coalesce(pop_seg_sum,""))*100,"")'
                proximity2AlternativeTransport = calculateField(
                    totalHousing['OUTPUT'], NAMES_INDEX['IC04'][0],
                    formulaProximity, context, feedback, params['OUTPUT'])

                result = proximity2AlternativeTransport

            return result
        else:
            feedback.reportError(
                str(('Se necesita al menos tres redes de transporte')))

            return {}
    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(13, model_feedback)
        results = {}
        outputs = {}

        input_layer = self.parameterAsVectorLayer(parameters, "inputlayer",
                                                  context)
        overlay_layer = self.parameterAsVectorLayer(parameters, "overlaylayer",
                                                    context)

        input_epsg_code = input_layer.crs().authid()
        overlay_epsg_code = overlay_layer.crs().authid()

        crs_input = QgsCoordinateReferenceSystem(input_epsg_code)
        crs_overlay = QgsCoordinateReferenceSystem(overlay_epsg_code)

        if crs_input.isGeographic():
            feedback.reportError(
                "CRS of the Input Layer is Geographic. Results accuracy may get impacted. For most accurate results, both input and overlay layers should be in the same Projected CRS\n"
            )

        if crs_overlay.isGeographic():
            feedback.reportError(
                "CRS of the Input Layer is Geographic. Results accuracy may get impacted. For most accurate results, both input and overlay layers should be in the same Projected CRS\n"
            )

        if input_epsg_code == overlay_epsg_code:
            pass
        else:
            feedback.reportError(
                "Input and Overlay Layers are in different CRS. For most accurate results, both input and overlay layers should be in the same Projected CRS\n"
            )

        # add_ID_field to input layer
        alg_params = {
            "FIELD_NAME": "input_feat_id",
            "GROUP_FIELDS": [""],
            "INPUT": parameters["inputlayer"],
            "SORT_ASCENDING": True,
            "SORT_EXPRESSION": "",
            "SORT_NULLS_FIRST": False,
            "START": 1,
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Add_id_field"] = processing.run(
            "native:addautoincrementalfield",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # add_area_field to input layer
        alg_params = {
            "FIELD_LENGTH": 0,
            "FIELD_NAME": "area_awa",
            "FIELD_PRECISION": 0,
            "FIELD_TYPE": 0,
            "FORMULA": "area($geometry)",
            "INPUT": outputs["Add_id_field"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Add_area_field"] = processing.run(
            "qgis:fieldcalculator",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # dissolve all overlay fields so as not to repeat record in reporting
        alg_params = {
            "FIELD": [parameters["fieldtoaverage"]] + [
                field for field in parameters["additionalfields"]
                if field != str(parameters["fieldtoaverage"])
            ],
            "INPUT":
            parameters["overlaylayer"],
            "OUTPUT":
            QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Dissolve"] = processing.run(
            "native:dissolve",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # intersection between input and overlay layer
        # delete no field in input layer and all fields in overlay layer
        # except field to average and additional fields
        alg_params = {
            "INPUT":
            outputs["Add_area_field"]["OUTPUT"],
            "INPUT_FIELDS": [""],
            "OVERLAY":
            outputs["Dissolve"]["OUTPUT"],
            "OVERLAY_FIELDS": [str(parameters["fieldtoaverage"])] +
            parameters["additionalfields"],
            "OVERLAY_FIELDS_PREFIX":
            "",
            "OUTPUT":
            QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Intersection"] = processing.run(
            "native:intersection",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # add_Weight
        alg_params = {
            "FIELD_LENGTH": 0,
            "FIELD_NAME": parameters["fieldtoaverage"] + "_area",
            "FIELD_PRECISION": 0,
            "FIELD_TYPE": 0,
            "FORMULA":
            ' "' + parameters["fieldtoaverage"] + '"  *  area($geometry)',
            "INPUT": outputs["Intersection"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Add_Weight"] = processing.run(
            "qgis:fieldcalculator",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # area_average
        weighted_field = "weighted_" + parameters["fieldtoaverage"]
        alg_params = {
            "FIELD_LENGTH": 0,
            "FIELD_NAME": weighted_field,
            "FIELD_PRECISION": 0,
            "FIELD_TYPE": 0,
            "FORMULA": ' sum("' + parameters["fieldtoaverage"] + "_area"
            '","input_feat_id")/"area_awa"',
            "INPUT": outputs["Add_Weight"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["area_average"] = processing.run(
            "qgis:fieldcalculator",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # remerge input layer elements
        alg_params = {
            "FIELD": ["input_feat_id"],
            "INPUT": outputs["area_average"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Dissolve2"] = processing.run(
            "native:dissolve",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        input_layer = self.parameterAsVectorLayer(parameters, "inputlayer",
                                                  context)
        result_name = input_layer.name() + "_" + parameters["fieldtoaverage"]
        parameters["result"].destinationName = result_name

        # drop field(s) for Result
        alg_params = {
            "COLUMN":
            ["input_feat_id", "area_awa"] + [parameters["fieldtoaverage"]] + [
                field for field in parameters["additionalfields"]
                if field != str(parameters["fieldtoaverage"])
            ] + [parameters["fieldtoaverage"] + "_area"],
            "INPUT":
            outputs["Dissolve2"]["OUTPUT"],
            "OUTPUT":
            parameters["result"],
        }
        outputs["Drop1"] = processing.run(
            "qgis:deletecolumn",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        results["result"] = outputs["Drop1"]["OUTPUT"]

        # Reporting

        # Drop field(s) for Report
        int_layer = context.takeResultLayer(outputs["area_average"]["OUTPUT"])
        all_fields = [f.name() for f in int_layer.fields()]
        fields_to_keep = (["input_feat_id", weighted_field] + [
            field for field in parameters["additionalfields"]
            if field != str(parameters["fieldtoaverage"])
        ] + [parameters["fieldtoaverage"]] +
                          [parameters["identifierfieldforreport"]])
        fields_to_drop = [f for f in all_fields if f not in fields_to_keep]
        alg_params = {
            "COLUMN": fields_to_drop,
            "INPUT": int_layer,
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["Drop2"] = processing.run(
            "qgis:deletecolumn",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # update area
        alg_params = {
            "FIELD_LENGTH": 20,
            "FIELD_NAME": "area_crs_units",
            "FIELD_PRECISION": 5,
            "FIELD_TYPE": 0,
            "FORMULA": "round(area($geometry),5)",
            "INPUT": outputs["Drop2"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["update_area"] = processing.run(
            "qgis:fieldcalculator",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        parameters["reportaslayer"].destinationName = "Report as Layer"
        # add area %
        alg_params = {
            "FIELD_LENGTH": 9,
            "FIELD_NAME": "area_prcnt",
            "FIELD_PRECISION": 5,
            "FIELD_TYPE": 0,
            "FORMULA":
            ' round("area_crs_units" *100/  sum(  "area_crs_units" ,  "input_feat_id" ),5)',
            "INPUT": outputs["update_area"]["OUTPUT"],
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["area_prcnt"] = processing.run(
            "qgis:fieldcalculator",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # Order by expression
        alg_params = {
            "ASCENDING": True,
            "EXPRESSION": ' "input_feat_id" + area_prcnt" ',
            "INPUT": outputs["area_prcnt"]["OUTPUT"],
            "NULLS_FIRST": False,
            "OUTPUT": parameters["reportaslayer"],
        }
        outputs["OrderByExpression"] = processing.run(
            "native:orderbyexpression",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        results["reportaslayer"] = outputs["OrderByExpression"]["OUTPUT"]

        output_file = self.parameterAsFileOutput(parameters, "reportasHTML",
                                                 context)

        # create HTML report
        if output_file:

            try:
                try:
                    import pandas as pd
                except ImportError:
                    feedback.pushInfo(
                        "Python library pandas was not found. Installing pandas to QGIS python ..."
                    )
                    import pathlib as pl
                    import subprocess

                    qgis_Path = pl.Path(sys.executable)
                    qgis_python_path = (qgis_Path.parent /
                                        "python3.exe").as_posix()

                    subprocess.check_call([
                        qgis_python_path, "-m", "pip", "install", "--user",
                        "pandas"
                    ])
                    import pandas as pd

                    feedback.pushInfo(
                        "Python library pandas was successfully installed for QGIS python"
                    )
            except:
                feedback.reportError(
                    "Failed to import pandas. Tried installing pandas but failed.\nPlease manually install pandas for the python that comes with your QGIS.",
                    True,
                )
                return results

            # Drop geometries
            alg_params = {
                "INPUT": outputs["area_prcnt"]["OUTPUT"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["DropGeometries"] = processing.run(
                "native:dropgeometries",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            with tempfile.TemporaryDirectory() as td:
                f_name = os.path.join(td, "report_df.csv")

                report_layer = context.takeResultLayer(
                    outputs["DropGeometries"]["OUTPUT"])

                QgsVectorFileWriter.writeAsVectorFormat(
                    report_layer,
                    f_name,
                    fileEncoding="utf-8",
                    driverName="CSV",
                )

                df = pd.read_csv(f_name)

            total_FIDs = df["input_feat_id"].max()
            ident_name = parameters["identifierfieldforreport"]
            html = ""
            df.sort_values(by="area_prcnt", ascending=False, inplace=True)
            pd.set_option("display.float_format", "{:.5f}".format)

            for i in range(1, total_FIDs + 1):
                df_sub = df.loc[df["input_feat_id"] == i]
                df_sub.reset_index(inplace=True, drop=True)
                avg_value = df_sub.at[0, weighted_field]
                if ident_name:
                    feature_name = df_sub.at[0, ident_name]
                    df_sub.drop(
                        columns=["input_feat_id", ident_name, weighted_field],
                        inplace=True,
                    )
                    html += f"<p><b>{i}. {feature_name}</b><br>{weighted_field}: {avg_value}<br>count of distinct intersecting features: {len(df_sub.index)}<br></p>\n"
                else:
                    df_sub.drop(columns=["input_feat_id", weighted_field],
                                inplace=True)
                    html += f"<p><b>Feature ID: {i}</b><br>{weighted_field}: {avg_value}<br>count of distinct intersecting features: {len(df_sub.index)}<br></p>\n"
                html += f"{df_sub.to_html(bold_rows=False, index=False, na_rep='Null',justify='left')}<br>\n"

                with codecs.open(output_file, "w", encoding="utf-8") as f:
                    f.write("<html><head>\n")
                    f.write(
                        '<meta http-equiv="Content-Type" content="text/html; \
                            charset=utf-8" /></head><body>\n')
                    f.write(html)
                    f.write("</body></html>\n")

                results["reportasHTML"] = output_file

        # log usage
        with open(os.path.join(cmd_folder, "usage_counter.log"), "r+") as f:
            counter = int(f.readline())
            f.seek(0)
            f.write(str(counter + 1))

        # check if counter is a milestone
        if (counter + 1) % 25 == 0:
            appeal_file = NamedTemporaryFile("w", suffix=".html", delete=False)
            self.createHTML(appeal_file.name, counter + 1)
            results["Message"] = appeal_file.name

        return results
    def processAlgorithm(self, parameters, context, model_feedback):
        """
        Process the algorithm
        :param parameters: parameters of the process
        :param context: context of the process
        :param model_feedback: feedback instance for the process
        :return:
        """
        # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
        # overall progress through the model
        self.xml_path = parameters["XMLPATH"]
        self.gpkg_path = parameters["GPKGPATH"]
        if not self.xml_path.lower().endswith(".xml"):
            feedback = QgsProcessingMultiStepFeedback(0, model_feedback)
            feedback.reportError(
                "XML Workspace Definition is not an XML file!", True)
            return {}
        if not self.gpkg_path.lower().endswith(".gpkg"):
            feedback = QgsProcessingMultiStepFeedback(0, model_feedback)
            feedback.reportError("GeoPackage is not an GPKG file!", True)
            return {}
        self.pg_conn_name = parameters["DBNAME"]
        self.pg_schema = parameters["SCHEMA"]
        self.pg_drop_before = parameters["DROPIFEXISTS"]
        dataset_list = self.getDatasets()
        feedback = QgsProcessingMultiStepFeedback(2 + len(dataset_list),
                                                  model_feedback)
        step = 0
        self.create_pk_metadata_table(context, feedback)
        step = 1
        for dataset in dataset_list:
            step += 1
            definition = self.getDatasetDef(dataset)
            if definition is not None:
                try:
                    in_layer = self.get_gpkg_vector_layer(definition[0])
                    if in_layer is not None:
                        feedback.pushInfo("Feature Class: " + definition[0])
                        try:
                            alg_params = {
                                'DATABASE': self.pg_conn_name,
                                'SQL': definition[1]
                            }
                            feedback.pushInfo(
                                "   processing (A) => qgis:postgisexecutesql")
                            processing.run('qgis:postgisexecutesql',
                                           alg_params,
                                           context=context,
                                           feedback=feedback,
                                           is_child_algorithm=True)
                        except Exception as e1:
                            feedback.reportError(
                                "Error creating table definition: \n" +
                                definition[1] + ": " + str(e1), False)
                            break

                        try:
                            # Esporta in PostgreSQL (connessioni disponibili)
                            alg_params = {
                                'ADDFIELDS': False,
                                'APPEND': False,
                                'A_SRS': None,
                                'CLIP': False,
                                'DATABASE': self.pg_conn_name,
                                'DIM': 0,
                                'GEOCOLUMN': 'geom',
                                'GT': '',
                                'GTYPE': definition[4],
                                'INDEX': False,
                                'INPUT':
                                self.get_gpkg_vector_layer(definition[0]),
                                'LAUNDER': False,
                                'OPTIONS': '',
                                'OVERWRITE': True,
                                'PK': '',
                                'PRECISION': True,
                                'PRIMARY_KEY': '',
                                'PROMOTETOMULTI': True,
                                'SCHEMA': self.pg_schema,
                                'SEGMENTIZE': '',
                                'SHAPE_ENCODING': '',
                                'SIMPLIFY': '',
                                'SKIPFAILURES': False,
                                'SPAT': None,
                                'S_SRS': None,
                                'TABLE': definition[0].lower() + '_tmp',
                                'T_SRS': None,
                                'WHERE': ''
                            }
                            feedback.pushInfo(
                                "   processing (B) => qgis:importvectorintopostgisdatabaseavailableconnections"
                            )
                            processing.run(
                                'gdal:importvectorintopostgisdatabaseavailableconnections',
                                alg_params,
                                context=context,
                                feedback=feedback,
                                is_child_algorithm=True)
                        except Exception as e2:
                            feedback.reportError(
                                "Error importing data: \n" + definition[0] +
                                ": " + str(e2), False)
                            break

                        try:
                            #Copy from TMP to FINAL table
                            sql_copy = "INSERT INTO %s.%s(%s) SELECT %s FROM %s.%s_tmp" % (
                                self.pg_schema, definition[0], definition[2],
                                definition[3], self.pg_schema,
                                definition[0]) + ";"
                            sql_drop = "DROP TABLE %s.%s_tmp" % (
                                self.pg_schema, definition[0]) + ";"
                            alg_params = {
                                'DATABASE': self.pg_conn_name,
                                'SQL': sql_copy + sql_drop
                            }
                            feedback.pushInfo(
                                "   processing (C) => qgis:postgisexecutesql")
                            processing.run('qgis:postgisexecutesql',
                                           alg_params,
                                           context=context,
                                           feedback=feedback,
                                           is_child_algorithm=True)
                        except Exception as e3:
                            feedback.reportError(
                                "Error moving data: \n" + sql_copy + sql_drop +
                                ": " + str(e3), False)
                            break
                except Exception as e:
                    feedback.reportError(
                        "Error importing domain " + definition[1] + ": " +
                        str(e), False)
            feedback.setCurrentStep(step)
        results = {}
        outputs = {}
        return results
    def processAlgorithm(self, parameters, context, model_feedback):

        # source: 'export as python script' in processing modeler
        feedback = QgsProcessingMultiStepFeedback(3, model_feedback)

        # pass
        source = self.parameterAsFile(parameters, self.INPUT, context)
        server_name = self.server_name_options[self.parameterAsInt(
            parameters, self.SERVER_NAME, context)]
        graph_name = self.parameterAsString(parameters, self.GRAPH_NAME,
                                            context)
        graph_version = self.graph_version_options[self.parameterAsInt(
            parameters, self.GRAPH_VERSION, context)]
        routing_mode = self.routing_mode_options[self.parameterAsInt(
            parameters, self.ROUTING_MODE, context)]

        if os.path.splitext(source)[-1].lower() == '.json':
            feedback.pushInfo('Load json track file')
            with open(source) as json_data:
                track_data = json.load(json_data)
        elif os.path.splitext(source)[-1].lower() == '.gpx':
            feedback.pushInfo(
                'Convert track file from GPX to JSON format using Graphium:Gpx2JsonConverter'
            )
            try:
                output = processing.run("Graphium:gpx2jsonconverter",
                                        parameters={
                                            'INPUT': source,
                                            'OUTPUT': 'TEMPORARY_OUTPUT'
                                        },
                                        is_child_algorithm=True,
                                        context=context,
                                        feedback=feedback)['OUTPUT']
                with open(output) as json_data:
                    track_data = json.load(json_data)
            except QgsProcessingException as e:
                feedback.reportError(
                    "Could not convert GPX file to JSON: " + str(e), True)
                return {self.OUTPUT_MATCHED_SEGMENTS: None}
        else:
            feedback.reportError(
                "Wrong track file format (" +
                os.path.splitext(source)[-1].lower() + ")", True)
            return {self.OUTPUT_MATCHED_SEGMENTS: None}

        # Connect to Graphium
        feedback.setCurrentStep(2)
        feedback.pushInfo("Connect to Graphium server '" + server_name +
                          "' ...")

        graphium = GraphiumUtilitiesApi(feedback)
        selected_connection = self.connection_manager.select_graphium_server(
            server_name)
        if selected_connection is None:
            feedback.reportError('Cannot select connection to Graphium', True)
            return {self.OUTPUT_MATCHED_SEGMENTS: None}

        if graphium.connect(selected_connection) is False:
            feedback.reportError('Cannot connect to Graphium', True)
            return {self.OUTPUT_MATCHED_SEGMENTS: None}

        feedback.pushInfo("Start Map-Matching task on Graphium server '" +
                          server_name + "' ...")
        response = graphium.do_map_matching(track_data, graph_name,
                                            graph_version, routing_mode)

        # Process map matching result
        if 'segments' in response:
            feedback.pushInfo('Finished map matching task!')
        elif 'error' in response:
            if 'msg' in response['error']:
                if response['error']['msg'] == 'ContentNotFoundError':
                    feedback.reportError(
                        'Graphium server "' + server_name +
                        '" does not support map matching', True)
                else:
                    feedback.reportError(response['error']['msg'], True)
            return {self.OUTPUT_MATCHED_SEGMENTS: None}
        elif 'exception' in response:
            feedback.reportError(response['exception'], True)
            return {self.OUTPUT_MATCHED_SEGMENTS: None}
        else:
            feedback.reportError('Unknown mapmatching error', True)
            return {self.OUTPUT_MATCHED_SEGMENTS: None}

        feedback.setCurrentStep(3)
        feedback.pushInfo("Prepare result vector layer ...")
        vector_layer = self.prepare_vector_layer('matched_track')

        (sink, dest_id) = self.parameterAsSink(parameters,
                                               self.OUTPUT_MATCHED_SEGMENTS,
                                               context, vector_layer.fields(),
                                               QgsWkbTypes.LineString,
                                               vector_layer.sourceCrs())

        total = 100.0 / len(response['segments'])
        for current, segment in enumerate(response['segments']):
            if feedback.isCanceled():
                break
            feature = QgsFeature()
            feature.setGeometry(QgsGeometry.fromWkt(segment['geometry']))
            feature.setFields(vector_layer.fields(), True)
            feature.setAttribute('order', current)
            for attribute_key in segment:
                try:
                    feature.setAttribute(attribute_key, segment[attribute_key])
                except KeyError:
                    pass
            sink.addFeature(feature, QgsFeatureSink.FastInsert)
            feedback.setProgress(int(current * total))

        feedback.pushInfo("Finished preparing vector layer " + dest_id)
        return {
            self.OUTPUT_MATCHED_SEGMENTS:
            dest_id,
            self.OUTPUT_NR_OF_U_TURNS:
            response['nrOfUTurns'],
            self.OUTPUT_NR_OF_SHORTEST_PATH_SEARCHES:
            response['nrOfShortestPathSearches'],
            self.OUTPUT_LENGTH:
            response['length'],
            self.OUTPUT_MATCHED_FACTOR:
            response['matchedFactor'],
            self.OUTPUT_MATCHED_POINTS:
            response['matchedPoints'],
            self.OUTPUT_CERTAIN_PATH_END_SEGMENT_ID:
            response['certainPathEndSegmentId']
        }
Beispiel #7
0
    def processAlgorithm(self, parameters, context, model_feedback):
        feedback = QgsProcessingMultiStepFeedback(2, model_feedback)

        source = self.parameterAsSource(parameters, self.INPUT, context)
        field_segment_id = self.parameterAsString(parameters, self.FIELD_SEGMENT_ID, context)
        segment_attribute_index = self.parameterAsInt(parameters, self.SEGMENT_ATTRIBUTE, context)
        segment_attribute = self.segment_attribute_options[segment_attribute_index]
        target_field = self.parameterAsString(parameters, self.TARGET_FIELD, context)

        server_name = self.connection_options[self.parameterAsInt(parameters, self.SERVER_NAME, context)]
        graph_name = self.parameterAsString(parameters, self.GRAPH_NAME, context)
        graph_version = self.parameterAsString(parameters, self.GRAPH_VERSION, context)

        feedback.pushInfo("Connect to Graphium server '" + server_name + "' ...")

        graphium = GraphiumGraphDataApi(feedback)
        selected_connection = self.connection_manager.select_graphium_server(server_name)

        if selected_connection is None:
            feedback.reportError('Cannot select connection to Graphium', True)
            return {self.OUTPUT: None}

        if graphium.connect(selected_connection) is False:
            feedback.reportError('Cannot connect to [' + server_name + ']', True)
            return {self.OUTPUT: None}

        feedback.pushInfo("Start downloading task on Graphium server '" + server_name + "' ...")

        total = 100.0 / source.featureCount() if source.featureCount() else 0

        # Read segment IDs
        segment_ids = []
        attributes_per_segment = dict()
        for current, feature in enumerate(source.getFeatures()):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            if not feature[field_segment_id]:
                continue

            if not feature[field_segment_id] in segment_ids:
                segment_ids.append(feature[field_segment_id])
            if len(segment_ids) > 50:
                self.get_segment_attributes(feedback, graphium, graph_name, graph_version, segment_attribute,
                                            segment_ids, attributes_per_segment)
            # Update the progress bar
            feedback.setProgress(int(current * total))
        if len(segment_ids) > 0:
            self.get_segment_attributes(feedback, graphium, graph_name, graph_version, segment_attribute,
                                        segment_ids, attributes_per_segment)

        feedback.setCurrentStep(1)
        feedback.pushInfo("Add attributes to features")

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

        for current, feature in enumerate(source.getFeatures()):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            if feature[field_segment_id]:
                if int(feature[field_segment_id]) in attributes_per_segment:
                    feature[target_field] = attributes_per_segment[int(feature[field_segment_id])]
                else:
                    feedback.pushInfo("No attribute for segment " + str(feature[field_segment_id]))

            sink.addFeature(feature, QgsFeatureSink.FastInsert)

            # Update the progress bar
            feedback.setProgress(int(current * total))

        feedback.setProgress(100)

        return {
            self.OUTPUT: dest_id
        }
Beispiel #8
0
    def processAlgorithm(self, parameters, context, model_feedback):      
        # entrées
        profils_l = self.parameterAsVectorLayer(parameters, 'profils', context)
        mnt = self.parameterAsRasterLayer(parameters, 'mnt', context)
        
        # sorties
        output = self.parameterAsOutputLayer(parameters, 'OUTPUT', context)

        # paramètres
        echantillons_nb = parameters['echantillons_nb'] # nombre d'échantillons
        seuil_diff = parameters['seuil_diff']
        seuil_rug = parameters['seuil_rug']

        # variables propres à Processing
        feedback = QgsProcessingMultiStepFeedback(profils_l.featureCount()*2, model_feedback)
        status = 0
        results = {}

        # l'algo SAGA Cross Profiles ajoute à chaque profil un attribut LINE qui permet d'identifier pour chaque profil la ligne dont il est issu
        # permet de traiter les profils de cours d'eau à cours d'eau
        if profils_l.fields().indexOf('LINE')<0:
            feedback.reportError("Les profils en entrée doivent contenir un attribut numérique LINE qui identifie chaque cours d'eau de manière unique !", True)
            return{}

        # préparation de la sortie
        id = 1
        fields = QgsFields()
        fields.append(QgsField("id", QVariant.Int))
        fields.append(QgsField("obstruct", QVariant.Int))
        writer = QgsVectorFileWriter(output, "System", fields, QgsWkbTypes.LineString, QgsCoordinateReferenceSystem(2154), "ESRI Shapefile")

        # on récupère les identifiants uniques de lignes pour traiter les profils par cours d'eau
        lines_ids = profils_l.uniqueValues(profils_l.fields().indexOf('LINE'))
        
        for line_id in lines_ids:

            # variables liées aux traitements
            low = None # dernière valeur d'altitude non-obstruée
            ids = []   # liste des identifiants de profils obstrués
            plist = [] # liste des X derniers profils 
            p2 = None  # id du deuxième profil traité
            count = 0

            # pour chaque cours d'eau
            for profil_f in profils_l.getFeatures("LINE = %s"%line_id):

                # ajout des points sur chaque profil
                profil_g = profil_f.geometry()
                freq = profil_g.length()/(echantillons_nb-1)
                echantillons_g = [QgsGeometry().fromPointXY(profil_g.asMultiPolyline()[0][0])]
                for i in range(1, echantillons_nb-1):
                    echantillons_g.append(profil_g.interpolate(freq*i))
                echantillons_g.append(QgsGeometry().fromPointXY(profil_g.asMultiPolyline()[0][-1]))

                # on affecte aux points la valeur du MNT correspondante
                elevations = []
                for echantillon_g in echantillons_g:                
                    elevation = mnt.dataProvider().sample(echantillon_g.asPoint(), 1)[0]
                    elevations.append(elevation)

                # exécuté pour le tout premier profil qui va déterminer la première valeur d'altitude considérée
                if low == None:
                    low = min(elevations)
                    plist.append(profil_f)
                # détection des obstructions
                else:
                    # seuil utilisé pour détecter les ruptures franches et limiter les "petites détections"
                    if min(elevations) <= low+seuil_diff:
                        # en cas de longue portion souterraine, la condition d'écoulement peut être remplie alors que le CE est toujours souterrain (cas sur le Malvan à Cagnes)
                        # on vérifie alors si le terrain est relativement plat (rugosité faible) et, si c'est le cas, on maintient l'obstruction
                        # pose problème sur les CE peu profonds
                        if not plist and (max(elevations)-min(elevations))<seuil_rug:
                            ids.append(profil_f.id())
                        # il y a écoulement
                        else:
                            low = min(elevations)
                            plist.append(profil_f.id())
                    else: # il n'y a pas écoulement
                        ids.append(profil_f.id()) 
                        # s'il y a un groupe de 5 (ou moins) profils non-obstrués au milieu de profils obstrués, on les ajoute au traitement
                        if len(plist) <= 5:
                            ids += plist
                        del plist[:]
                    # récupération de l'id du deuxième profil
                    if count == 1:
                        p2 = profil_f.id()

                status += 1
                count += 1
                feedback.setCurrentStep(status)
                if feedback.isCanceled():
                    return {}

            # si le second profil est obstrué, on considère qu'il y a erreur et on ne traite pas le CE
            # lorsque le cas arrive, il s'agit souvent d'un CE non-présent sur le MNT et le traitement est erronné
            if p2 in ids:
                del ids[:]

            # post processing pour étendre la détection à x profils amont/aval pour permettre l'interpolation
            # extention modulable par la variable ext ci-dessous
            ext = 2
            prev = []
            count = 0
            for profil_f in profils_l.getFeatures("LINE = %s"%line_id):
                if len(prev) > 0:
                    if count == 0:
                        if profil_f.id() in ids and prev[-1] not in ids:
                            ids += prev
                        if profil_f.id() not in ids and prev[-1] in ids:
                            ids.append(profil_f.id())
                            count += 1
                    else:
                        if count < ext:
                            ids.append(profil_f.id())
                            count += 1
                        else:
                            count = 0
                prev.append(profil_f.id())
                if len(prev) > ext:
                    del prev[0]
            
            # attribution d'un identifiant unique à chaque groupe de profils souterrains
            # ecriture de chaque profil dans la nouvelle couche qui contient deux attributs :
            # id : identifie les profils obstrués contigus pour les traiter en groupe
            # obstruct : à 1 si le profil est obstrué, sinon à 0
            for profil_f in profils_l.getFeatures("LINE = %s"%line_id):
                if profil_f.id() not in ids:
                    profil_f.setAttributes([0,0])
                    id += 1
                else:
                    profil_f.setAttributes([id,1])
                writer.addFeature(profil_f)
                status += 1
                feedback.setCurrentStep(status)
                if feedback.isCanceled():
                    return {}
        
        results['OUTPUT']=output
        return results
    def processAlgorithm(self, parameters, context, model_feedback):

        # source: 'export as python script' in processing modeler
        feedback = QgsProcessingMultiStepFeedback(4, model_feedback)

        start_coordinate = self.parameterAsPoint(
            parameters, self.START_COORDINATE, context,
            QgsCoordinateReferenceSystem(4326))
        end_coordinate = self.parameterAsPoint(
            parameters, self.END_COORDINATE, context,
            QgsCoordinateReferenceSystem(4326))
        routing_mode = self.routing_mode_options[self.parameterAsInt(
            parameters, self.ROUTING_MODE, context)]
        routing_criteria = self.routing_criteria_options[self.parameterAsInt(
            parameters, self.ROUTING_CRITERIA, context)]
        # cut_segments = self.parameterAsBool(parameters, self.CUT_SEGMENTS, context)

        server_name = self.server_name_options[self.parameterAsInt(
            parameters, self.SERVER_NAME, context)]
        graph_name = self.parameterAsString(parameters, self.GRAPH_NAME,
                                            context)
        graph_version = self.parameterAsString(parameters, self.GRAPH_VERSION,
                                               context)

        # Connect to Graphium
        feedback.setCurrentStep(2)
        feedback.pushInfo("Connect to Graphium server '" + server_name +
                          "' ...")

        graphium = GraphiumUtilitiesApi(feedback)
        selected_connection = self.connection_manager.select_graphium_server(
            server_name)
        if selected_connection is None:
            feedback.reportError('Cannot select connection to Graphium', True)
            return {self.OUTPUT: None, self.OUTPUT_PATH: None}

        if graphium.connect(selected_connection) is False:
            feedback.reportError('Cannot connect to Graphium', True)
            return {self.OUTPUT: None, self.OUTPUT_PATH: None}

        feedback.pushInfo("Start Routing task on Graphium server '" +
                          server_name + "' ...")
        response = graphium.do_routing(graph_name, graph_version,
                                       start_coordinate.x(),
                                       start_coordinate.y(),
                                       end_coordinate.x(), end_coordinate.y(),
                                       datetime.today(), None, routing_mode,
                                       routing_criteria)

        # Process routing result
        if 'route' in response:
            if response['route']['length'] == 0:
                feedback.reportError('No route found', False)
                return {self.OUTPUT: None, self.OUTPUT_PATH: None}
        elif 'error' in response:
            if 'msg' in response['error']:
                if response['error']['msg'] == 'ContentNotFoundError':
                    feedback.reportError(
                        'Graphium server "' + server_name +
                        '" does not support routing', True)
                else:
                    feedback.reportError(response['error']['msg'], True)
            return {self.OUTPUT: None, self.OUTPUT_PATH: None}
        else:
            feedback.reportError('Unknown routing error', True)
            feedback.reportError(str(response), True)
            return {self.OUTPUT: None, self.OUTPUT_PATH: None}

        # create feature output
        feedback.setCurrentStep(3)
        vector_layer = self.prepare_vector_layer('route')

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, vector_layer.fields(),
                                               QgsWkbTypes.LineString,
                                               vector_layer.sourceCrs())
        if response['route']['geometry'] is not None:
            feature = QgsFeature()
            feature.setGeometry(
                QgsGeometry.fromWkt(response['route']['geometry']))
            feature.setFields(vector_layer.fields(), True)
            for attribute_key in response['route']:
                if feedback.isCanceled():
                    break
                try:
                    feature.setAttribute(attribute_key,
                                         response['route'][attribute_key])
                except KeyError:
                    pass
            sink.addFeature(feature, QgsFeatureSink.FastInsert)

        # create path output
        feedback.setCurrentStep(4)

        path_layer = self.prepare_path_layer('route_path')

        (sink_path,
         dest_id_path) = self.parameterAsSink(parameters, self.OUTPUT_PATH,
                                              context, path_layer.fields(),
                                              QgsWkbTypes.NoGeometry,
                                              vector_layer.sourceCrs())
        if response['route']['geometry'] is not None:
            total = 100.0 / len(response['route']['segments'])
            for current, path_segment in enumerate(
                    response['route']['segments']):
                if feedback.isCanceled():
                    break
                feature = QgsFeature()
                feature.setFields(path_layer.fields(), True)
                feature.setAttribute('order', current)
                feature.setAttribute('segment_id', path_segment['id'])
                feature.setAttribute('linkDirectionForward',
                                     path_segment['linkDirectionForward'])
                sink_path.addFeature(feature, QgsFeatureSink.FastInsert)
                feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id, self.OUTPUT_PATH: dest_id_path}
    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(25, model_feedback)
        results = {}
        outputs = {}

        nlcd_rast_output = self.parameterAsBool(parameters,
                                                "OutputNLCDLandCoverRaster",
                                                context)
        nlcd_vect_output = self.parameterAsBool(parameters,
                                                "OutputNLCDLandCoverVector",
                                                context)
        nlcd_rast_imp_output = self.parameterAsBool(
            parameters, "OutputNLCDImperviousRaster", context)
        soil_output = self.parameterAsBool(parameters, "OutputSoilLayer",
                                           context)
        curve_number_output = self.parameterAsBool(parameters,
                                                   "OutputCurveNumberLayer",
                                                   context)

        # Assiging Default CN_Lookup Table
        if parameters["cnlookup"] == None:
            csv_uri = ("file:///" + os.path.join(cmd_folder, "CN_Lookup.csv") +
                       "?delimiter=,")
            csv = QgsVectorLayer(csv_uri, "CN_Lookup.csv", "delimitedtext")
            parameters["cnlookup"] = csv

        area_layer = self.parameterAsVectorLayer(parameters, "areaboundary",
                                                 context)

        EPSGCode = area_layer.crs().authid()
        origEPSGCode = EPSGCode  # preserve orignal EPSGCode to project back to it
        # feedback.pushInfo(str(EPSGCode))

        if check_crs_acceptable(EPSGCode):
            pass
        else:
            # Reproject layer to EPSG:5070
            alg_params = {
                "INPUT": parameters["areaboundary"],
                "OPERATION": "",
                "TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:5070"),
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["ReprojectLayer5070"] = processing.run(
                "native:reprojectlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )
            area_layer = context.takeResultLayer(
                outputs["ReprojectLayer5070"]["OUTPUT"])

            EPSGCode = area_layer.crs().authid()

        # Check if area of the extent is less than 100,000 Acres
        d = QgsDistanceArea()
        tr_cont = QgsCoordinateTransformContext()
        d.setSourceCrs(area_layer.crs(), tr_cont)
        # d.setEllipsoid(area_layer.crs().ellipsoidAcronym())
        extent_area = d.measureArea(QgsGeometry().fromRect(
            area_layer.extent()))
        area_acres = d.convertAreaMeasurement(extent_area,
                                              QgsUnitTypes.AreaAcres)

        if area_acres > 500000:
            feedback.reportError(
                f"Area Boundary layer extent area should be less than 500,000 acres.\nArea Boundary layer extent area is {round(area_acres,4):,} acres.\n\nExecution Failed",
                True,
            )
            return results
        elif area_acres > 100000:
            feedback.reportError(
                f"Your Area Boundary layer extent area is {round(area_acres,4):,} acres. The recommended extent area is 100,000 acres or less. If the Algorithm fails, rerun with a smaller input layer.\n",
                False,
            )
        else:
            feedback.pushInfo(
                f"Area Boundary layer extent area is {round(area_acres,4):,} acres\n"
            )

        # Get extent of the area boundary layer
        xmin = area_layer.extent().xMinimum()
        ymin = area_layer.extent().yMinimum()
        xmax = area_layer.extent().xMaximum()
        ymax = area_layer.extent().yMaximum()

        BBOX_width = (xmax - xmin) / 30
        BBOX_height = (ymax - ymin) / 30
        BBOX_width_int = round(BBOX_width)
        BBOX_height_int = round(BBOX_height)

        # NLCD Impervious Raster
        if nlcd_rast_imp_output == True:
            request_URL = f"https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Impervious_L48/ows?version=1.3.0&service=WMS&layers=NLCD_2016_Impervious_L48&styles&crs={str(EPSGCode)}&format=image/geotiff&request=GetMap&width={str(BBOX_width_int)}&height={str(BBOX_height_int)}&BBOX={str(xmin)},{str(ymin)},{str(xmax)},{str(ymax)}&"

            # Download NLCD Impervious Raster
            try:
                ping_URL = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Impervious_L48/ows"
                r = requests.head(ping_URL, verify=False)
                r.raise_for_status()

                alg_params = {
                    "URL": request_URL,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcdImp"] = processing.run(
                    "native:filedownloader",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except (QgsProcessingException,
                    requests.exceptions.HTTPError) as e:
                feedback.reportError(
                    f"Error: {str(e)}\n\nError requesting land use data from 'www.mrlc.gov'. Most probably because either their server is down or there is a certification issue.\nThis should be temporary. Try again later.\n",
                    True,
                )
                return results

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

            # reproject to original crs
            # Warp (reproject)
            if EPSGCode != origEPSGCode:
                alg_params = {
                    "DATA_TYPE": 0,
                    "EXTRA": "",
                    "INPUT": outputs["DownloadNlcdImp"]["OUTPUT"],
                    "MULTITHREADING": False,
                    "NODATA": None,
                    "OPTIONS": "",
                    "RESAMPLING": 0,
                    "SOURCE_CRS": None,
                    "TARGET_CRS":
                    QgsCoordinateReferenceSystem(str(origEPSGCode)),
                    "TARGET_EXTENT": None,
                    "TARGET_EXTENT_CRS": None,
                    "TARGET_RESOLUTION": None,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcdImp"] = processing.run(
                    "gdal:warpreproject",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

            # Set layer style
            alg_params = {
                "INPUT": outputs["DownloadNlcdImp"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "NLCD_Raster_Imp.qml"),
            }

            try:  # for QGIS Version later than 3.12
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForRasterLayer"] = processing.run(
                    "qgis:setstyleforrasterlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

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

        # NLCD Land Cover Data
        if (curve_number_output == True or nlcd_vect_output == True
                or nlcd_rast_output == True):
            request_URL = f"https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Land_Cover_L48/ows?version=1.3.0&service=WMS&layers=NLCD_2016_Land_Cover_L48&styles&crs={str(EPSGCode)}&format=image/geotiff&request=GetMap&width={str(BBOX_width_int)}&height={str(BBOX_height_int)}&BBOX={str(xmin)},{str(ymin)},{str(xmax)},{str(ymax)}&"

            # Download NLCD
            try:
                ping_URL = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Land_Cover_L48/ows"
                r = requests.head(ping_URL, verify=False)
                r.raise_for_status()

                alg_params = {
                    "URL": request_URL,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcd"] = processing.run(
                    "native:filedownloader",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except (QgsProcessingException,
                    requests.exceptions.HTTPError) as e:
                feedback.reportError(
                    f"Error: {str(e)}\n\nError requesting land use data from 'www.mrlc.gov'. Most probably because either their server is down or there is a certification issue.\nThis should be temporary. Try again later.\n",
                    True,
                )
                return results

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

            # reproject to original crs
            # Warp (reproject)
            if EPSGCode != origEPSGCode:
                alg_params = {
                    "DATA_TYPE": 0,
                    "EXTRA": "",
                    "INPUT": outputs["DownloadNlcd"]["OUTPUT"],
                    "MULTITHREADING": False,
                    "NODATA": None,
                    "OPTIONS": "",
                    "RESAMPLING": 0,
                    "SOURCE_CRS": None,
                    "TARGET_CRS":
                    QgsCoordinateReferenceSystem(str(origEPSGCode)),
                    "TARGET_EXTENT": None,
                    "TARGET_EXTENT_CRS": None,
                    "TARGET_RESOLUTION": None,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcd"] = processing.run(
                    "gdal:warpreproject",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

            # Reclassify by table
            alg_params = {
                "DATA_TYPE":
                5,
                "INPUT_RASTER":
                outputs["DownloadNlcd"]["OUTPUT"],
                "NODATA_FOR_MISSING":
                False,
                "NO_DATA":
                -9999,
                "RANGE_BOUNDARIES":
                0,
                "RASTER_BAND":
                1,
                "TABLE":
                QgsExpression(
                    "'0,1,11,1,2,12,2,3,21,3,4,22,4,5,23,5,6,24,6,7,31,7,8,32,8,9,41,9,10,42,10,11,43,11,12,51,12,13,52,13,14,71,14,15,72,15,16,73,16,17,74,17,18,81,18,19,82,19,20,90,20,21,95'"
                ).evaluate(),
                "OUTPUT":
                QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["ReclassifyByTable"] = processing.run(
                "native:reclassifybytable",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Set layer style
            alg_params = {
                "INPUT": outputs["ReclassifyByTable"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "NLCD_Raster.qml"),
            }

            try:  # for QGIS Version later than 3.12
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForRasterLayer"] = processing.run(
                    "qgis:setstyleforrasterlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

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

            if curve_number_output == True or nlcd_vect_output == True:
                # Polygonize (raster to vector)
                alg_params = {
                    "BAND": 1,
                    "EIGHT_CONNECTEDNESS": False,
                    "EXTRA": "",
                    "FIELD": "VALUE",
                    "INPUT": outputs["ReclassifyByTable"]["OUTPUT"],
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["PolygonizeRasterToVector"] = processing.run(
                    "gdal:polygonize",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

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

                # Fix geometries
                alg_params = {
                    "INPUT": outputs["PolygonizeRasterToVector"]["OUTPUT"],
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["FixGeometries"] = processing.run(
                    "native:fixgeometries",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

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

                # Set layer style
                alg_params = {
                    "INPUT": outputs["FixGeometries"]["OUTPUT"],
                    "STYLE": os.path.join(cmd_folder, "NLCD_Vector.qml"),
                }
                try:  # for QGIS Version 3.12 and later
                    outputs["SetLayerStyle"] = processing.run(
                        "native:setlayerstyle",
                        alg_params,
                        context=context,
                        feedback=feedback,
                        is_child_algorithm=True,
                    )
                except:  # for QGIS Version older than 3.12
                    outputs["SetStyleForVectorLayer"] = processing.run(
                        "qgis:setstyleforvectorlayer",
                        alg_params,
                        context=context,
                        feedback=feedback,
                        is_child_algorithm=True,
                    )

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

        # Soil Layer
        if soil_output == True or curve_number_output == True:

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

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

            # Get Area Boundary layer extent in EPSG:4326
            area_layer_reprojected = context.takeResultLayer(
                outputs["ReprojectLayer4326"]["OUTPUT"])

            # Download Soil

            try:  # request using post rest

                # create vector layer structure to store data
                feedback.pushInfo("Creating POST request...")
                uri = "Polygon?crs=epsg:4326"
                soil_layer = QgsVectorLayer(uri, "soil layer", "memory")
                provider = soil_layer.dataProvider()
                attributes = []
                attr_dict = [
                    {
                        "name": "musym",
                        "type": "str"
                    },
                    {
                        "name": "muname",
                        "type": "str"
                    },
                    {
                        "name": "mustatus",
                        "type": "str"
                    },
                    {
                        "name": "slopegraddcp",
                        "type": "str"
                    },
                    {
                        "name": "slopegradwta",
                        "type": "str"
                    },
                    {
                        "name": "brockdepmin",
                        "type": "str"
                    },
                    {
                        "name": "wtdepannmin",
                        "type": "str"
                    },
                    {
                        "name": "wtdepaprjunmin",
                        "type": "str"
                    },
                    {
                        "name": "flodfreqdcd",
                        "type": "str"
                    },
                    {
                        "name": "flodfreqmax",
                        "type": "str"
                    },
                    {
                        "name": "pondfreqprs",
                        "type": "str"
                    },
                    {
                        "name": "aws025wta",
                        "type": "str"
                    },
                    {
                        "name": "aws050wta",
                        "type": "str"
                    },
                    {
                        "name": "aws0100wta",
                        "type": "str"
                    },
                    {
                        "name": "aws0150wta",
                        "type": "str"
                    },
                    {
                        "name": "drclassdcd",
                        "type": "str"
                    },
                    {
                        "name": "drclasswettest",
                        "type": "str"
                    },
                    {
                        "name": "hydgrpdcd",
                        "type": "str"
                    },
                    {
                        "name": "iccdcd",
                        "type": "str"
                    },
                    {
                        "name": "iccdcdpct",
                        "type": "str"
                    },
                    {
                        "name": "niccdcd",
                        "type": "str"
                    },
                    {
                        "name": "niccdcdpct",
                        "type": "str"
                    },
                    {
                        "name": "engdwobdcd",
                        "type": "str"
                    },
                    {
                        "name": "engdwbdcd",
                        "type": "str"
                    },
                    {
                        "name": "engdwbll",
                        "type": "str"
                    },
                    {
                        "name": "engdwbml",
                        "type": "str"
                    },
                    {
                        "name": "engstafdcd",
                        "type": "str"
                    },
                    {
                        "name": "engstafll",
                        "type": "str"
                    },
                    {
                        "name": "engstafml",
                        "type": "str"
                    },
                    {
                        "name": "engsldcd",
                        "type": "str"
                    },
                    {
                        "name": "engsldcp",
                        "type": "str"
                    },
                    {
                        "name": "englrsdcd",
                        "type": "str"
                    },
                    {
                        "name": "engcmssdcd",
                        "type": "str"
                    },
                    {
                        "name": "engcmssmp",
                        "type": "str"
                    },
                    {
                        "name": "urbrecptdcd",
                        "type": "str"
                    },
                    {
                        "name": "urbrecptwta",
                        "type": "str"
                    },
                    {
                        "name": "forpehrtdcp",
                        "type": "str"
                    },
                    {
                        "name": "hydclprs",
                        "type": "str"
                    },
                    {
                        "name": "awmmfpwwta",
                        "type": "str"
                    },
                    {
                        "name": "mukey",
                        "type": "str"
                    },
                    {
                        "name": "mupolygonkey",
                        "type": "str"
                    },
                    {
                        "name": "areasymbol",
                        "type": "str"
                    },
                    {
                        "name": "nationalmusym",
                        "type": "str"
                    },
                ]

                # initialize fields
                for field in attr_dict:
                    attributes.append(QgsField(field["name"], QVariant.String))
                    provider.addAttributes(attributes)
                    soil_layer.updateFields()

                # get area layer extent polygon as WKT in 4326
                aoi_reproj_wkt = area_layer_reprojected.extent().asWktPolygon()

                # send post request
                body = {
                    "format":
                    "JSON",
                    "query":
                    f"select Ma.*, M.mupolygonkey, M.areasymbol, M.nationalmusym, M.mupolygongeo from mupolygon M, muaggatt Ma where M.mupolygonkey in (select * from SDA_Get_Mupolygonkey_from_intersection_with_WktWgs84('{aoi_reproj_wkt.lower()}')) and M.mukey=Ma.mukey",
                }
                url = "https://sdmdataaccess.sc.egov.usda.gov/TABULAR/post.rest"
                soil_response = requests.post(url, json=body).json()

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

                for row in soil_response["Table"]:
                    # None attribute for empty data
                    row = [None if not attr else attr for attr in row]
                    feat = QgsFeature(soil_layer.fields())
                    # populate data
                    for index, col in enumerate(row):
                        if index != len(attr_dict):
                            feat.setAttribute(attr_dict[index]["name"], col)
                        else:
                            feat.setGeometry(QgsGeometry.fromWkt(col))
                    provider.addFeatures([feat])

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

            except:  # try wfs request
                feedback.reportError(
                    "Error getting soil data through post request. Your input layer maybe too large. Trying WFS download now.\nIf the Algorithm get stuck during download. Terminate the Algorithm and rerun with a smaller input layer.",
                    False,
                )
                xmin_reprojected = area_layer_reprojected.extent().xMinimum()
                ymin_reprojected = area_layer_reprojected.extent().yMinimum()
                xmax_reprojected = area_layer_reprojected.extent().xMaximum()
                ymax_reprojected = area_layer_reprojected.extent().yMaximum()

                request_URL_soil = f"https://sdmdataaccess.sc.egov.usda.gov/Spatial/SDMWGS84GEOGRAPHIC.wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=mapunitpolyextended&SRSNAME=EPSG:4326&BBOX={str(xmin_reprojected)},{str(ymin_reprojected)},{str(xmax_reprojected)},{str(ymax_reprojected)}"

                alg_params = {
                    "URL": request_URL_soil,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadSoil"] = processing.run(
                    "native:filedownloader",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

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

                # Swap X and Y coordinates
                alg_params = {
                    "INPUT": outputs["DownloadSoil"]["OUTPUT"],
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["SwapXAndYCoordinates"] = processing.run(
                    "native:swapxy",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

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

                soil_layer = outputs["SwapXAndYCoordinates"]["OUTPUT"]

            # Fix soil layer geometries
            alg_params = {
                "INPUT": soil_layer,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
            }
            outputs["FixGeometries2"] = processing.run(
                "native:fixgeometries",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Clip Soil Layer
            alg_params = {
                "INPUT": outputs["FixGeometries2"]["OUTPUT"],
                "OVERLAY": parameters["areaboundary"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["Clip"] = processing.run(
                "native:clip",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Reproject Soil
            alg_params = {
                "INPUT": outputs["Clip"]["OUTPUT"],
                "OPERATION": "",
                "TARGET_CRS": QgsCoordinateReferenceSystem(origEPSGCode),
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["ReprojectSoil"] = processing.run(
                "native:reprojectlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Fix soil layer geometries second time
            alg_params = {
                "INPUT": outputs["ReprojectSoil"]["OUTPUT"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["FixGeometries3"] = processing.run(
                "native:fixgeometries",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Set layer style
            alg_params = {
                "INPUT": outputs["FixGeometries3"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "Soil_Layer.qml"),
            }
            try:  # for QGIS Version 3.12 and later
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForVectorLayer"] = processing.run(
                    "qgis:setstyleforvectorlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

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

        # Curve Number Calculations
        if curve_number_output == True:

            feedback.pushInfo(
                "Generating Curve Number Layer. This may take a while. Do not cancel."
            )
            # Intersection
            alg_params = {
                "INPUT": outputs["FixGeometries3"]["OUTPUT"],
                "INPUT_FIELDS": ["MUSYM", "HYDGRPDCD", "MUNAME"],
                "OVERLAY": outputs["FixGeometries"]["OUTPUT"],
                "OVERLAY_FIELDS": ["VALUE"],
                "OVERLAY_FIELDS_PREFIX": "",
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["Intersection"] = processing.run(
                "native:intersection",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Create GDCodeTemp
            alg_params = {
                "FIELD_LENGTH": 5,
                "FIELD_NAME": "GDCodeTemp",
                "FIELD_PRECISION": 3,
                "FIELD_TYPE": 2,
                "FORMULA":
                'IF ("HYDGRPDCD" IS NOT NULL, "Value" || "HYDGRPDCD", IF (("MUSYM" = \'W\' OR lower("MUSYM") = \'water\' OR lower("MUNAME") = \'water\' OR "MUNAME" = \'W\'), 11, "VALUE"))',
                "INPUT": outputs["Intersection"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateGdcodetemp"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Create GDCode
            alg_params = {
                "FIELD_LENGTH": 5,
                "FIELD_NAME": "GDCode",
                "FIELD_PRECISION": 3,
                "FIELD_TYPE": 2,
                "FORMULA":
                "if( var('drainedsoilsleaveuncheckedifnotsure') = True,replace(\"GDCodeTemp\", '/D', ''),replace(\"GDCodeTemp\", map('A/', '', 'B/', '', 'C/', '')))",
                "INPUT": outputs["CreateGdcodetemp"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateGdcode"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Create NLCD_LU
            alg_params = {
                "FIELD_LENGTH": 2,
                "FIELD_NAME": "NLCD_LU",
                "FIELD_PRECISION": 3,
                "FIELD_TYPE": 1,
                "FORMULA": '"Value"',
                "INPUT": outputs["CreateGdcode"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateNlcd_lu"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Join with CNLookup
            alg_params = {
                "DISCARD_NONMATCHING": False,
                "FIELD": "GDCode",
                "FIELDS_TO_COPY": ["CN_Join"],
                "FIELD_2": "GDCode",
                "INPUT": outputs["CreateNlcd_lu"]["OUTPUT"],
                "INPUT_2": parameters["cnlookup"],
                "METHOD": 1,
                "PREFIX": "",
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["JoinWithCnlookup"] = processing.run(
                "native:joinattributestable",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Create Integer CN
            alg_params = {
                "FIELD_LENGTH": 3,
                "FIELD_NAME": "CN",
                "FIELD_PRECISION": 0,
                "FIELD_TYPE": 1,
                "FORMULA": "CN_Join  * 1",
                "INPUT": outputs["JoinWithCnlookup"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateIntegerCn"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Drop field(s)
            alg_params = {
                "COLUMN": ["VALUE", "GDCodeTemp", "CN_Join"],
                "INPUT": outputs["CreateIntegerCn"]["OUTPUT"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["DropFields"] = processing.run(
                "qgis:deletecolumn",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

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

            # Set layer style
            alg_params = {
                "INPUT": outputs["DropFields"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "CN_Grid.qml"),
            }
            try:  # for QGIS Version 3.12 and later
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForVectorLayer"] = processing.run(
                    "qgis:setstyleforvectorlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

        if nlcd_rast_output:
            # Load NLCD Raster into project
            alg_params = {
                "INPUT": outputs["ReclassifyByTable"]["OUTPUT"],
                "NAME": "NLCD Land Cover Raster",
            }
            outputs["LoadLayerIntoProject1"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if nlcd_vect_output:
            # Load NLCD Vector Layer into project
            alg_params = {
                "INPUT": outputs["FixGeometries"]["OUTPUT"],
                "NAME": "NLCD Land Cover Vector",
            }
            outputs["LoadLayerIntoProject2"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if nlcd_rast_imp_output:
            # Load NLCD Impervious Raster into project
            alg_params = {
                "INPUT": outputs["DownloadNlcdImp"]["OUTPUT"],
                "NAME": "NLCD Impervious Raster",
            }
            outputs["LoadLayerIntoProject3"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if soil_output:
            # Load Soil Layer into project
            alg_params = {
                "INPUT": outputs["FixGeometries3"]["OUTPUT"],
                "NAME": "SSURGO Soil Layer",
            }
            outputs["LoadLayerIntoProject4"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if curve_number_output:
            # Load Curve Number Layer into project
            alg_params = {
                "INPUT": outputs["DropFields"]["OUTPUT"],
                "NAME": "Curve Number Layer",
            }
            outputs["LoadLayerIntoProject5"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        # log usage
        with open(os.path.join(cmd_folder, "usage_counter.log"), "r+") as f:
            counter = int(f.readline())
            f.seek(0)
            f.write(str(counter + 1))

        # check if counter is milestone
        if (counter + 1) % 25 == 0:
            appeal_file = NamedTemporaryFile("w", suffix=".html", delete=False)
            self.createHTML(appeal_file.name, counter + 1)
            results["Message"] = appeal_file.name

        return results
Beispiel #11
0
    def processAlgorithm(self, parameters, context, model_feedback):
        feedback = QgsProcessingMultiStepFeedback(2, model_feedback)

        server_name = self.connection_options[self.parameterAsInt(parameters, self.SERVER_NAME, context)]
        graph_name = self.parameterAsString(parameters, self.GRAPH_NAME, context)
        graph_version = self.parameterAsString(parameters, self.GRAPH_VERSION, context)
        save_json_file = self.parameterAsBoolean(parameters, self.SAVE_JSON_FILE, context)
        json_file = self.parameterAsFileOutput(parameters, self.OUTPUT_JSON, context)

        # Connect to Graphium
        feedback.pushInfo("Connect to Graphium server '" + server_name + "' ...")

        graphium_data = GraphiumGraphDataApi(feedback)
        graphium_management = GraphiumGraphManagementApi(feedback)
        selected_connection = self.connection_manager.select_graphium_server(server_name)

        if selected_connection is None:
            feedback.reportError('Cannot select connection to Graphium', True)
            return {self.OUTPUT_SEGMENTS: None}

        if graphium_management.connect(selected_connection) is False:
            feedback.reportError('Cannot connect to Graphium', True)
            return {self.OUTPUT_SEGMENTS: None}
        if graphium_data.connect(selected_connection) is False:
            feedback.reportError('Cannot connect to Graphium', True)
            return {self.OUTPUT_SEGMENTS: None}

        metadata = graphium_management.get_graph_version_metadata(graph_name, graph_version)

        if not metadata['type']:
            feedback.reportError('Cannot correctly retrieve graph metadata', True)
            return {self.OUTPUT_SEGMENTS: None}

        feedback.pushInfo("Start downloading task on Graphium server '" + server_name + "' ...")
        response = graphium_data.export_graph(graph_name, graph_version, metadata.get('type') == 'hdwaysegment')

        if save_json_file:
            feedback.pushInfo("Write graph to JSON file...")
            with open(json_file, 'w') as output_file:
                output_file.write(json.dumps(response))

        feedback.setCurrentStep(1)
        if 'graphVersionMetadata' in response:
            # if response['graphVersionMetadata']['segmentsCount'] == 0:
            #     feedback.reportError('No segments available', False)
            #     return {self.OUTPUT_SEGMENT_COUNT: 0}
            # elif response['graphVersionMetadata']['state'] == 'DELETED':
            if response['graphVersionMetadata']['state'] == 'DELETED':
                feedback.reportError('Graph version has been deleted', False)
                return {self.OUTPUT_SEGMENT_COUNT: 0}
        elif 'error' in response:
            if 'msg' in response['error']:
                feedback.reportError(response['error']['msg'], True)
            return {self.OUTPUT_SEGMENT_COUNT: 0}
        else:
            feedback.reportError('Unknown error', True)
            return {self.OUTPUT_SEGMENT_COUNT: 0}

        feedback.pushInfo("Prepare result vector layer ...")
        vector_layer = self.prepare_vector_layer('segments_' + graph_name + '_' + graph_version,
                                                 response['graphVersionMetadata']['type'])

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_SEGMENTS, context, vector_layer.fields(),
                                               QgsWkbTypes.LineString, vector_layer.sourceCrs())

        total = 100.0 / len(response[response['graphVersionMetadata']['type']])
        for current, segment in enumerate(response[response['graphVersionMetadata']['type']]):
            if feedback.isCanceled():
                break
            feature = QgsFeature()
            feature.setGeometry(QgsGeometry.fromWkt(segment['geometry']))
            feature.setFields(vector_layer.fields(), True)
            for attribute_key in segment:
                try:
                    if attribute_key == 'tags' or attribute_key == 'connection':
                        feature.setAttribute(attribute_key, json.dumps(segment[attribute_key]))
                    else:
                        feature.setAttribute(attribute_key, segment[attribute_key])
                except KeyError:
                    pass
            sink.addFeature(feature, QgsFeatureSink.FastInsert)
            feedback.setProgress(int(current * total))

        feedback.pushInfo("Finished preparing vector layer " + dest_id)
        return {self.OUTPUT_SEGMENTS: dest_id,
                self.OUTPUT_JSON: json_file if save_json_file else None,
                self.OUTPUT_SEGMENT_COUNT: response['graphVersionMetadata']['segmentsCount']
                }
Beispiel #12
0
    def processAlgorithm(self, parameters, context, model_feedback):
        feedback = QgsProcessingMultiStepFeedback(2, model_feedback)
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        group_field_name = self.parameterAsString(parameters, self.GROUP_FIELD,
                                                  context)
        order_field_name = self.parameterAsString(parameters, self.ORDER_FIELD,
                                                  context)
        date_format = self.parameterAsString(parameters, self.DATE_FORMAT,
                                             context)
        group_field_index = source.fields().lookupField(group_field_name)
        order_field_index = source.fields().lookupField(order_field_name)
        field_names = self.parameterAsFields(parameters, self.FIELDS, context)

        if group_field_index >= 0:
            group_field_def = source.fields().at(group_field_index)
        else:
            group_field_def = None
        order_field_def = source.fields().at(order_field_index)

        #Create output for lines
        fields = QgsFields()
        self.addFields(source, fields, 'start_order', field_names,
                       order_field_def)
        self.addFields(source, fields, 'end_order', field_names,
                       order_field_def)
        fields.append(QgsField('COUNT', QVariant.LongLong))

        output_wkb = QgsWkbTypes.LineString
        if QgsWkbTypes.hasM(source.wkbType()):
            output_wkb = QgsWkbTypes.addM(output_wkb)
        if QgsWkbTypes.hasZ(source.wkbType()):
            output_wkb = QgsWkbTypes.addZ(output_wkb)

        (self.sink,
         self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LINE,
                                              context, fields, output_wkb,
                                              source.sourceCrs())
        if self.sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT_LINE))

        #Create output for Points
        fields_point = QgsFields()
        fields_point.append(QgsField("fid", QVariant.Int, "int", 9, 0))
        self.addFields(source, fields_point, 'end_order', field_names)
        fields_point.append(QgsField("COUNT", QVariant.LongLong))
        fields_point.append(QgsField("COUNT_IN", QVariant.LongLong))
        fields_point.append(QgsField("COUNT_OUT", QVariant.LongLong))

        (self.sink_point, self.dest_id_point) = self.parameterAsSink(
            parameters, self.OUTPUT_POINT, context, fields_point,
            source.wkbType(), source.sourceCrs(),
            QgsFeatureSink.RegeneratePrimaryKey)
        if self.sink_point is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT_POINT))

        #Compute the lines
        points = dict()
        features = source.getFeatures(
            QgsFeatureRequest(),
            QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled(): return {}

            if not f.hasGeometry(): continue

            point = f.geometry().asPoint()
            if group_field_index >= 0:
                group = f[group_field_index]
            else:
                group = 1
            order = f[order_field_index]
            if date_format != '':
                if isinstance(order, QDateTime):
                    pass
                else:
                    try:
                        order = datetime.strptime(str(order), date_format)
                    except ValueError as ve:
                        feedback.reportError(
                            self.tr('Invalid format {}').format(ve))
                        return {}
            data = [order, point]
            for indice in self.field_indices:
                data.append(f[indice])
            if group in points:
                points[group].append(data)
            else:
                points[group] = [data]

            feedback.setProgress(int(current * total))
        feedback.setCurrentStep(1)

        #Create the features
        current = 0
        total = 100.0 / len(points) if points else 1
        edge_ids = {}
        end_points = {}
        self.point_id = 0
        for group, vertices in points.items():
            if feedback.isCanceled(): return {}
            feedback.setProgress(int(current * total))
            # log("attrs: {}".format(vertices))
            vertices.sort(key=lambda x: (x[0] is None, x[0]))
            for i in range(len(vertices) - 1):
                if feedback.isCanceled(): return {}
                start_id = vertices[i][1].asWkt()
                end_id = vertices[i + 1][1].asWkt()
                edge_id = start_id + "-" + end_id

                if edge_id in edge_ids:
                    f = edge_ids[edge_id]
                    count_index = 2 * (len(self.field_indices) + 1)
                    count = f.attribute(count_index) + 1
                    f.setAttribute(count_index, count)
                else:
                    f = QgsFeature()
                    attrs = []
                    attrs.append(vertices[i][0])  #Add Order begin
                    for j in range(len(
                            self.field_indices)):  #Add Attrs for begin
                        attrs.append(
                            vertices[i][j + 2])  # +2 ignore order and point
                    attrs.append(vertices[i + 1][0])  #Add Order end
                    for j in range(0, len(
                            self.field_indices)):  #Add Attrs for end
                        attrs.append(
                            vertices[i + 1][j +
                                            2])  # +2 ignore order and point
                    attrs.append(1)  #Count = 1
                    f.setAttributes(attrs)
                    line = [vertices[i][1], vertices[i + 1][1]]
                    geom = QgsGeometry()
                    f.setGeometry(QgsGeometry(geom.fromPolylineXY(line)))
                    edge_ids[edge_id] = f

                self.updatePoints(start_id, end_points, vertices[i], 'start')
                self.updatePoints(end_id, end_points, vertices[i + 1], 'end')

                current += 1
                feedback.setProgress(int(current * total))
        for id in edge_ids:
            if feedback.isCanceled(): return {}
            self.sink.addFeature(edge_ids[id], QgsFeatureSink.FastInsert)
        for id in end_points:
            if feedback.isCanceled(): return {}
            self.sink_point.addFeature(end_points[id],
                                       QgsFeatureSink.FastInsert)

        return {self.OUTPUT_LINE: self.dest_id}
Beispiel #13
0
    def processAlgorithm(self, parameters, context, model_feedback):
        feedback = QgsProcessingMultiStepFeedback(2, model_feedback)
        source = self.parameterAsSource(parameters, self.INPUT, context)
        field_segment_id = self.parameterAsString(parameters,
                                                  self.FIELD_SEGMENT_ID,
                                                  context)

        server_name = self.connection_options[self.parameterAsInt(
            parameters, self.SERVER_NAME, context)]
        graph_name = self.parameterAsString(parameters, self.GRAPH_NAME,
                                            context)
        graph_version = self.parameterAsString(parameters, self.GRAPH_VERSION,
                                               context)

        # Connect to Graphium
        feedback.pushInfo("Connect to Graphium server '" + server_name +
                          "' ...")

        graphium = GraphiumGraphDataApi(feedback)
        selected_connection = self.connection_manager.select_graphium_server(
            server_name)

        if selected_connection is None:
            feedback.reportError('Cannot select connection to Graphium', True)
            return {self.OUTPUT_SEGMENTS: None}

        if graphium.connect(selected_connection) is False:
            feedback.reportError('Cannot connect to Graphium', True)
            return {self.OUTPUT_SEGMENTS: None}

        feedback.pushInfo("Start downloading task on Graphium server '" +
                          server_name + "' ...")

        total = 100.0 / source.featureCount() if source.featureCount() else 0

        segments_with_geometry = 0

        # Read segment IDs
        segment_ids = []
        segment_geometries = dict()
        for current, feature in enumerate(source.getFeatures()):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            segment_ids.append(feature[field_segment_id])
            if len(segment_ids) > 50:
                self.get_segment_geometries(feedback, graphium, graph_name,
                                            graph_version, segment_ids,
                                            segment_geometries)
            # Update the progress bar
            feedback.setProgress(int(current * total))
        if len(segment_ids) > 0:
            self.get_segment_geometries(feedback, graphium, graph_name,
                                        graph_version, segment_ids,
                                        segment_geometries)

        feedback.setCurrentStep(1)
        feedback.pushInfo("Add geometries to segments")

        (sink, dest_id) = self.parameterAsSink(
            parameters, self.OUTPUT_SEGMENTS, context, source.fields(),
            QgsWkbTypes.LineString, QgsCoordinateReferenceSystem('EPSG:4326'))

        for current, feature in enumerate(source.getFeatures()):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            if int(feature[field_segment_id]) in segment_geometries:
                feature.setGeometry(segment_geometries[int(
                    feature[field_segment_id])])
                segments_with_geometry += 1
            else:
                pass
                # feedback.pushInfo("No geometry for segment " + str(feature[field_segment_id]))

            sink.addFeature(feature, QgsFeatureSink.FastInsert)

            # Update the progress bar
            feedback.setProgress(int(current * total))

        feedback.setProgress(100)

        return {
            self.OUTPUT_SEGMENTS:
            dest_id,
            self.OUTPUT_SEGMENT_COUNT:
            source.featureCount() if source.featureCount() else 0,
            self.OUTPUT_SEGMENT_WITH_GEOMETRY_COUNT:
            segments_with_geometry
        }
Beispiel #14
0
    def processAlgorithm(self, parameters, context, model_feedback):
        # variables propres à Processing
        feedback = QgsProcessingMultiStepFeedback(2, model_feedback)
        results = {}

        # entrées
        ponts = self.parameterAsVectorLayer(parameters, 'ponts', context)
        berges = self.parameterAsVectorLayer(parameters, 'berges', context)

        # sorties
        lines = []
        output = self.parameterAsOutputLayer(parameters, 'OUTPUT', context)

        # paramètres
        extension = parameters['extension']
        distance = parameters['distance']

        # traitement
        # on étend le pont original d'une valeur définie pour assurer son itnersection avec les berges
        # on récupère les deux points d'intersection et on les décale en amont et en aval de X m sur la ligne de berge
        # puis on reconstruit la ligne à partir des deux points décalés
        pont = 0
        for pont_f in ponts.getFeatures():
            pont_g = pont_f.geometry().extendLine(
                extension, extension
            )  # extension du pont pour assurer son intersection avec les berges
            l0 = []  # liste qui contiendra les deux points du profil amont
            l1 = []  # liste qui contiendra les deux points du pont
            l2 = []  # liste qui contiendra les deux points du profil aval
            bcount = 0
            for berge_f in berges.getFeatures(
            ):  # pour chaque pont on traite une berge, puis l'autre
                berge_g = berge_f.geometry()
                if berge_g.intersects(
                        pont_g
                ):  # on vérifie l'intersection, si absence d'intersection on renvoie une erreur et on stoppe l'algorithme
                    point_g = berge_g.intersection(pont_g)
                    line_distance = berge_g.lineLocatePoint(
                        point_g
                    )  # on détermine à quelle distance on se situe sur la ligne de berge pour permettre le décalage
                    p1, p2 = berge_g.interpolate(
                        line_distance + distance), berge_g.interpolate(
                            line_distance - distance
                        )  # on décale le point original en amont et en aval
                    l1.append(point_g.asPoint())
                    if (bcount == 1 and (QgsGeometry.fromPolylineXY([
                            l0[0], p1.asPoint()
                    ]).length() > QgsGeometry.fromPolylineXY(
                        [l0[0], p2.asPoint()]).length())):
                        l0.append(p2.asPoint())
                        l2.append(p1.asPoint())
                    else:
                        l0.append(p1.asPoint())
                        l2.append(p2.asPoint())
                    bcount += 1
            if bcount != 2:
                feedback.reportError(
                    "Le pont %s n'intersecte pas avec les berges !" %
                    pont_f.id(), True)
                return {}
            lines.append((QgsGeometry.fromPolylineXY(l0), [0, pont, 'Profil']))
            lines.append((QgsGeometry.fromPolylineXY(l1), [1, pont, 'Pont']))
            lines.append((QgsGeometry.fromPolylineXY(l2), [2, pont, 'Profil']))
            pont += 1

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

        # écriture des données en sortie
        fields = QgsFields()
        fields.append(QgsField("id", QVariant.Int))
        fields.append(QgsField("pont", QVariant.Int))
        fields.append(QgsField("type", QVariant.String))
        writer = QgsVectorFileWriter(output, "System", fields,
                                     QgsWkbTypes.LineString,
                                     QgsCoordinateReferenceSystem(2154),
                                     "ESRI Shapefile")
        for line, attributes in lines:
            f = QgsFeature()
            f.setGeometry(line)
            f.setAttributes(attributes)
            writer.addFeature(f)

        feedback.setCurrentStep(2)

        results['OUTPUT'] = output
        return results
Beispiel #15
0
    def processAlgorithm(self, parameters, context, model_feedback):
        """
        Process algorithm.
        """
        feedback = QgsProcessingMultiStepFeedback(13, model_feedback)
        results = {}
        outputs = {}
        project = QgsProject.instance()

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

        # Points:
        #  origin:      user origin point in proj crs
        #  wgs84_origin: origin point in wgs84 crs, used for choosing utm zone
        #  utm_origin:  origin point in utm crs
        #  fire_origin: user fire point in proj crs
        #  wgs84_fire_origin: fire point in wgs84 crs
        #  utm_fire_origin: fire point in utm crs

        # CRSs:
        #  project_crs: project crs
        #  wgs84_crs:  wgs84 crs
        #  utm_crs:  utm crs, calculated from wgs84_origin
        #  dem_crs:  dem crs, used for grid alignment

        # Extents:
        #  extent:      user terrain extent in any crs
        #  mesh_extent: extent to utm crs, used for FDS MESH
        #               as it is always contained in the terrain
        #  dem_extent:  mesh_extent to dem crs, used for grid
        #               alignment with dem raster data
        #  tex_extent:  dem_extent to utm crs, used for texture,
        #               that should be oriented as utm and perfectly
        #               correspond to dem

        # Get some of the parameters
        chid = self.parameterAsString(parameters, "chid", context)
        project.writeEntry("qgis2fds", "chid", parameters["chid"])
        path = self.parameterAsFile(parameters, "path", context)
        project.writeEntry("qgis2fds", "path", parameters["path"])
        landuse_type = self.parameterAsEnum(parameters, "landuse_type",
                                            context)
        project.writeEntry("qgis2fds", "landuse_type",
                           parameters["landuse_type"])
        dem_sampling = self.parameterAsInt(parameters, "dem_sampling", context)
        project.writeEntry("qgis2fds", "dem_sampling",
                           parameters["dem_sampling"])
        extent = self.parameterAsExtent(parameters, "extent",
                                        context)  # FIXME crs?
        project.writeEntry("qgis2fds", "extent", parameters["extent"])

        # Get layers in their respective crs: dem_layer, landuse_layer, tex_layer
        dem_layer = self.parameterAsRasterLayer(parameters, "dem_layer",
                                                context)
        project.writeEntry("qgis2fds", "dem_layer", parameters["dem_layer"])

        if not parameters["landuse_layer"]:  # it is optional
            landuse_layer = None
        else:
            landuse_layer = self.parameterAsRasterLayer(
                parameters, "landuse_layer", context)
        project.writeEntry("qgis2fds", "landuse_layer",
                           parameters["landuse_layer"])

        if not parameters["tex_layer"]:  # it is optional
            tex_layer = None
        else:
            tex_layer = self.parameterAsRasterLayer(parameters, "tex_layer",
                                                    context)
        project.writeEntry("qgis2fds", "tex_layer", parameters["tex_layer"])

        # Get tex_pixel_size
        tex_pixel_size = self.parameterAsDouble(parameters, "tex_pixel_size",
                                                context)
        project.writeEntryDouble("qgis2fds", "tex_pixel_size",
                                 parameters["tex_pixel_size"])

        # Prepare CRSs and check their validity
        project_crs = QgsProject.instance().crs()
        project.writeEntry("qgis2fds", "project_crs",
                           project_crs.description())
        feedback.pushInfo(f"Project CRS: <{project_crs.description()}>")
        if not project_crs.isValid():
            raise QgsProcessingException(
                "Project CRS is not usable, cannot proceed.")
        wgs84_crs = QgsCoordinateReferenceSystem("EPSG:4326")

        dem_crs = dem_layer.crs()
        feedback.pushInfo(f"DEM layer CRS: <{dem_crs.description()}>")
        if not dem_crs.isValid():
            raise QgsProcessingException(
                "DEM layer CRS is not usable, cannot proceed.")

        if landuse_layer:
            landuse_crs = landuse_layer.crs()
            feedback.pushInfo(
                f"Landuse layer CRS: <{landuse_crs.description()}>")
            if not landuse_crs.isValid():
                raise QgsProcessingException(
                    "Landuse layer CRS is not usable, cannot proceed.")

        if tex_layer:
            tex_crs = tex_layer.crs()
            feedback.pushInfo(f"Texture layer CRS: <{tex_crs.description()}>")
            if not tex_crs.isValid():
                raise QgsProcessingException(
                    "Texture layer CRS is not usable, cannot proceed.")

        # Get extent in WGS84 CRS
        wgs84_extent = self.parameterAsExtent(parameters,
                                              "extent",
                                              context,
                                              crs=wgs84_crs)

        # Get origin in WGS84 CRS
        project_to_wgs84_tr = QgsCoordinateTransform(project_crs, wgs84_crs,
                                                     QgsProject.instance())
        if parameters["origin"] is not None:
            wgs84_origin = self.parameterAsPoint(parameters,
                                                 "origin",
                                                 context,
                                                 crs=wgs84_crs)
            feedback.pushInfo("Using user origin.")
            project.writeEntry("qgis2fds", "origin", parameters["origin"])
        else:  # no origin
            wgs84_origin = wgs84_extent.center()
            feedback.pushInfo(f"Using terrain extent center as origin.")

        # Get fire origin in WGS84 CRS
        if parameters["fire_origin"] is not None:
            wgs84_fire_origin = self.parameterAsPoint(parameters,
                                                      "fire_origin",
                                                      context,
                                                      crs=wgs84_crs)
            feedback.pushInfo("Using user fire origin.")
        else:
            wgs84_fire_origin = QgsPoint(wgs84_origin.x(), wgs84_origin.y())
            feedback.pushInfo("Using origin as fire origin.")
        project.writeEntry("qgis2fds", "fire_origin",
                           parameters["fire_origin"])

        # Calc UTM CRS from wgs84_origin
        utm_epsg = utils.lonlat_to_epsg(lon=wgs84_origin.x(),
                                        lat=wgs84_origin.y())
        utm_crs = QgsCoordinateReferenceSystem(utm_epsg)
        feedback.pushInfo(f"Using UTM CRS: <{utm_crs.description()}>")

        # Get origin in UTM CRS
        wgs84_to_utm_tr = QgsCoordinateTransform(wgs84_crs, utm_crs,
                                                 QgsProject.instance())
        utm_origin = QgsPoint(wgs84_origin.x(), wgs84_origin.y())
        utm_origin.transform(wgs84_to_utm_tr)

        # Check for QGIS bug
        if utm_origin == wgs84_origin:
            raise QgsProcessingException(
                f"[QGIS bug] UTM Origin <{utm_origin}> and WGS84 Origin <{wgs84_origin}> are identical, cannot proceed."
            )

        # Get fire origin in UTM CRS
        utm_fire_origin = QgsPoint(wgs84_fire_origin.x(),
                                   wgs84_fire_origin.y())
        utm_fire_origin.transform(wgs84_to_utm_tr)

        # Get FDS MESH extent in UTM CRS, then obtain dem extent in DEM CRS
        mesh_extent = self.parameterAsExtent(
            parameters,
            "extent",
            context,
            crs=utm_crs,
        )
        utm_to_dem_tr = QgsCoordinateTransform(utm_crs, dem_crs,
                                               QgsProject.instance())
        dem_extent = utm_to_dem_tr.transformBoundingBox(mesh_extent)

        # Check DEM contains dem_extent
        if not dem_layer.extent().contains(dem_extent):
            feedback.reportError(
                "Terrain extent is larger than available DEM data.")

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

        # QGIS geographic transformations
        # Creating sampling grid in DEM crs

        feedback.pushInfo("Creating sampling grid layer from DEM...")

        xspacing = dem_layer.rasterUnitsPerPixelX()
        yspacing = dem_layer.rasterUnitsPerPixelY()
        x0, y0, x1, y1 = (  # terrain extent in DEM CRS
            dem_extent.xMinimum(),
            dem_extent.yMinimum(),
            dem_extent.xMaximum(),
            dem_extent.yMaximum(),
        )
        xd0, yd1 = (  # DEM extent in DEM CRS
            dem_layer.extent().xMinimum(),
            dem_layer.extent().yMaximum(),
        )
        # align terrain extent to DEM grid (gridding starts from top left corner)
        x0 = xd0 + round((x0 - xd0) / xspacing) * xspacing + xspacing / 2.0
        y1 = yd1 + round((y1 - yd1) / yspacing) * yspacing - yspacing / 2.0
        dem_extent = QgsRectangle(x0, y0, x1, y1)  # terrain extent in DEM CRS
        npoints = int(
            (x1 - x0) / xspacing * (y1 - y0) / yspacing / dem_sampling**2)
        if npoints < 9:
            raise QgsProcessingException(
                f"Too few sampling points, cannot proceed. (npoints: {npoints})"
            )
        feedback.pushInfo(
            f"Sampling points: {npoints} (xspacing: {xspacing}, yspacing: {yspacing})"
        )
        alg_params = {
            "CRS": dem_crs,
            "EXTENT": dem_extent,
            "HOVERLAY": 0,
            "HSPACING":
            xspacing * dem_sampling,  # reduce sampling, if requested
            "TYPE": 0,  # Points
            "VOVERLAY": 0,
            "VSPACING":
            yspacing * dem_sampling,  # reduce sampling, if requested
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["CreateGrid"] = processing.run(
            "native:creategrid",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # Save texture

        dem_to_utm_tr = QgsCoordinateTransform(dem_crs, utm_crs,
                                               QgsProject.instance())
        tex_extent = dem_to_utm_tr.transformBoundingBox(dem_extent)

        utils.write_image(
            feedback=feedback,
            tex_layer=tex_layer,
            tex_pixel_size=tex_pixel_size,  # pixel size in meters
            destination_crs=
            utm_crs,  # using UTM crs, texture aligned to axis in Smokeview
            destination_extent=tex_extent,
            filepath=f"{path}/{chid}_tex.png",
            imagetype="png",
        )

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

        # QGIS geographic transformations
        # Draping Z values to sampling grid in DEM crs

        feedback.pushInfo("Draping Z values from DEM...")
        alg_params = {
            "BAND": 1,
            "INPUT": outputs["CreateGrid"]["OUTPUT"],
            "NODATA": 0,
            "RASTER": dem_layer,
            "SCALE": 1,
            "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
        }
        outputs["DrapeSetZValueFromRaster"] = processing.run(
            "native:setzfromraster",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # QGIS geographic transformations
        # Reprojecting sampling grid to UTM CRS

        feedback.pushInfo("Reprojecting sampling grid layer to UTM CRS...")
        alg_params = {
            "INPUT":
            outputs["DrapeSetZValueFromRaster"]["OUTPUT"],
            "TARGET_CRS":
            utm_crs,
            "OUTPUT":
            landuse_layer and QgsProcessing.TEMPORARY_OUTPUT
            or parameters["sampling_layer"],
        }
        outputs["ReprojectLayer"] = processing.run(
            "native:reprojectlayer",
            alg_params,
            context=context,
            feedback=feedback,
            is_child_algorithm=True,
        )

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

        # QGIS geographic transformations
        # Sampling landuse layer with sampling grid in UTM CRS

        if landuse_layer:
            feedback.pushInfo("Sampling landuse...")
            alg_params = {
                "COLUMN_PREFIX": "landuse",
                "INPUT": outputs["ReprojectLayer"]["OUTPUT"],
                "RASTERCOPY": parameters["landuse_layer"],
                "OUTPUT": parameters["sampling_layer"],
            }
            outputs["sampling_layer"] = processing.run(
                "qgis:rastersampling",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            results["sampling_layer"] = outputs["sampling_layer"]["OUTPUT"]
            point_layer = context.getMapLayer(results["sampling_layer"])
        else:
            feedback.pushInfo("No landuse layer provided, no sampling.")
            results["sampling_layer"] = outputs["ReprojectLayer"]["OUTPUT"]
            point_layer = context.getMapLayer(results["sampling_layer"])
            # add fake landuse
            point_layer.dataProvider().addAttributes(
                (QgsField("landuse", QVariant.Int), ))
            point_layer.updateFields()

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

        # Prepare geometry

        verts, faces, landuses = geometry.get_geometry(
            feedback=feedback,
            layer=point_layer,
            utm_origin=utm_origin,
        )

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

        # Write the FDS case file

        fds.write_case(
            feedback=feedback,
            dem_layer=dem_layer,
            landuse_layer=landuse_layer,
            path=path,
            chid=chid,
            wgs84_origin=wgs84_origin,
            utm_origin=utm_origin,
            wgs84_fire_origin=wgs84_fire_origin,
            utm_fire_origin=utm_fire_origin,
            utm_crs=utm_crs,
            verts=verts,
            faces=faces,
            landuses=landuses,
            landuse_type=landuse_type,
            mesh_extent=mesh_extent,
        )

        return results