Ejemplo n.º 1
1
    def execute(self, feedback=None, model=None):
        """The method to use to call a processing algorithm.

        Although the body of the algorithm is in processAlgorithm(),
        it should be called using this method, since it performs
        some additional operations.

        Raises a GeoAlgorithmExecutionException in case anything goes
        wrong.
        """

        if feedback is None:
            feedback = QgsProcessingFeedback()

        self.model = model
        try:
            self.setOutputCRS()
            self.resolveOutputs()
            self.evaluateParameterValues()
            self.runPreExecutionScript(feedback)
            self.processAlgorithm(feedback)
            feedback.setProgress(100)
            self.convertUnsupportedFormats(feedback)
            self.runPostExecutionScript(feedback)
        except GeoAlgorithmExecutionException as gaee:
            lines = [self.tr("Error while executing algorithm")]
            lines.append(traceback.format_exc())
            ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, gaee.msg)
            raise GeoAlgorithmExecutionException(gaee.msg, lines, gaee)
        except Exception as e:
            # If something goes wrong and is not caught in the
            # algorithm, we catch it here and wrap it
            lines = [self.tr("Uncaught error while executing algorithm")]
            lines.append(traceback.format_exc())
            ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, lines)
            raise GeoAlgorithmExecutionException(str(e) + self.tr("\nSee log for more details"), lines, e)
Ejemplo n.º 2
0
def executeAlgorithm(alg, parameters, context=None, feedback=None, model=None):
    """The method to use to call a processing algorithm.

    Although the body of the algorithm is in processAlgorithm(),
    it should be called using this method, since it performs
    some additional operations.

    Raises a QgsProcessingException in case anything goes
    wrong.
    :param parameters:
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext(feedback)

    #self.model = model

    #self.setOutputCRS()
    #self.resolveOutputs()
    #self.evaluateParameterValues()
    #self.runPreExecutionScript(feedback)
    result, ok = alg.run(parameters, context, feedback)
    #self.processAlgorithm(parameters, context, feedback)
    feedback.setProgress(100)
    return result, ok
Ejemplo n.º 3
0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
    i = 0

    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))

        try:
            layer = QgsProcessingUtils.mapLayerFromString(l, context, typeHint=details.layerTypeHint)
            if layer is not None:
                set_layer_name(layer, details)

                style = None
                if details.outputName:
                    style = RenderingStyles.getStyle(alg.id(), details.outputName)
                if style is None:
                    if layer.type() == QgsMapLayer.RasterLayer:
                        style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE)
                    else:
                        if layer.geometryType() == QgsWkbTypes.PointGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE)
                        elif layer.geometryType() == QgsWkbTypes.LineGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE)
                        else:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE)
                if style:
                    layer.loadNamedStyle(style)

                details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))

                if details.postProcessor():
                    details.postProcessor().postProcessLayer(layer, context, feedback)

            else:
                wrongLayers.append(str(l))
        except Exception:
            QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical)
            wrongLayers.append(str(l))
        i += 1

    feedback.setProgress(100)

    if wrongLayers:
        msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.")
        msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.")
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Ejemplo n.º 4
0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
    i = 0
    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))

        try:
            layer = QgsProcessingUtils.mapLayerFromString(l, context)
            if layer is not None:
                if not ProcessingConfig.getSetting(ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
                    layer.setName(details.name)

                style = None
                if details.outputName:
                    style = RenderingStyles.getStyle(alg.id(), details.outputName)
                if style is None:
                    if layer.type() == QgsMapLayer.RasterLayer:
                        style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE)
                    else:
                        if layer.geometryType() == QgsWkbTypes.PointGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE)
                        elif layer.geometryType() == QgsWkbTypes.LineGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE)
                        else:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE)
                if style:
                    layer.loadNamedStyle(style)
                details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
            else:
                wrongLayers.append(str(l))
        except Exception:
            QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical)
            wrongLayers.append(str(l))
        i += 1

    feedback.setProgress(100)

    if wrongLayers:
        msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.")
        msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.")
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Ejemplo n.º 5
0
def handleAlgorithmResults(alg, feedback=None, showResults=True):
    wrongLayers = []
    htmlResults = False
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(
        QCoreApplication.translate('Postprocessing',
                                   'Loading resulting layers'))
    i = 0
    for out in alg.outputs:
        feedback.setProgress(100 * i / float(len(alg.outputs)))
        if out.hidden or not out.open:
            continue
        if isinstance(out, (OutputRaster, OutputVector, OutputTable)):
            try:
                if hasattr(out, "layer") and out.layer is not None:
                    out.layer.setName(out.description)
                    QgsProject.instance().addMapLayers([out.layer])
                else:
                    if ProcessingConfig.getSetting(
                            ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
                        name = os.path.basename(out.value)
                    else:
                        name = out.description
                    dataobjects.load(
                        out.value, name, alg.crs,
                        RenderingStyles.getStyle(alg.commandLineName(),
                                                 out.name))
            except Exception:
                ProcessingLog.addToLog(
                    ProcessingLog.LOG_ERROR,
                    "Error loading result layer:\n" + traceback.format_exc())
                wrongLayers.append(out.description)
        elif isinstance(out, OutputHTML):
            ProcessingResults.addResult(out.description, out.value)
            htmlResults = True
        i += 1

    QApplication.restoreOverrideCursor()
    if wrongLayers:
        msg = "The following layers were not correctly generated.<ul>"
        msg += "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += "You can check the log messages to find more information about the execution of the algorithm"
        feedback.reportError(msg)

    if showResults and htmlResults and not wrongLayers:
        dlg = ResultsDialog()
        dlg.exec_()

    return len(wrongLayers) == 0
Ejemplo n.º 6
0
def executeAlgorithm(alg, parameters, context=None, feedback=None, model=None):
    """The method to use to call a processing algorithm.

    Although the body of the algorithm is in processAlgorithm(),
    it should be called using this method, since it performs
    some additional operations.

    Raises a GeoAlgorithmExecutionException in case anything goes
    wrong.
    :param parameters:
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext()

    #self.model = model
    try:
        #self.setOutputCRS()
        #self.resolveOutputs()
        #self.evaluateParameterValues()
        #self.runPreExecutionScript(feedback)
        result = alg.run(parameters, context, feedback)
        #self.processAlgorithm(parameters, context, feedback)
        feedback.setProgress(100)
        return result
        #self.convertUnsupportedFormats(context, feedback)
        #self.runPostExecutionScript(feedback)
    except GeoAlgorithmExecutionException as gaee:
        lines = [self.tr('Error while executing algorithm')]
        lines = []
        lines.append(traceback.format_exc())
        feedback.reportError(gaee.msg)
        QgsMessageLog.logMessage(gaee.msg, self.tr('Processing'),
                                 QgsMessageLog.CRITICAL)
        raise GeoAlgorithmExecutionException(gaee.msg, lines, gaee)
    #except Exception as e:
    # If something goes wrong and is not caught in the
    # algorithm, we catch it here and wrap it
    #lines = [self.tr('Uncaught error while executing algorithm')]
    #   lines = []
    #  lines.append(traceback.format_exc())
    #QgsMessageLog.logMessage('\n'.join(lines), self.tr('Processing'), QgsMessageLog.CRITICAL)
    #raise GeoAlgorithmExecutionException(str(e) + self.tr('\nSee log for more details'), lines, e)

    def helpUrl(self):
        return QgsHelp.helpUrl("processing_algs/{}/{}".format(
            self.provider().id(), self.id())).toString()
Ejemplo n.º 7
0
def executeAlgorithm(alg, parameters, context=None, feedback=None, model=None):
    """The method to use to call a processing algorithm.

    Although the body of the algorithm is in processAlgorithm(),
    it should be called using this method, since it performs
    some additional operations.

    Raises a GeoAlgorithmExecutionException in case anything goes
    wrong.
    :param parameters:
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext()

    #self.model = model
    try:
        #self.setOutputCRS()
        #self.resolveOutputs()
        #self.evaluateParameterValues()
        #self.runPreExecutionScript(feedback)
        result = alg.run(parameters, context, feedback)
        #self.processAlgorithm(parameters, context, feedback)
        feedback.setProgress(100)
        return result
        #self.convertUnsupportedFormats(context, feedback)
        #self.runPostExecutionScript(feedback)
    except GeoAlgorithmExecutionException as gaee:
        lines = [self.tr('Error while executing algorithm')]
        lines = []
        lines.append(traceback.format_exc())
        feedback.reportError(gaee.msg)
        QgsMessageLog.logMessage(gaee.msg, self.tr('Processing'), QgsMessageLog.CRITICAL)
        raise GeoAlgorithmExecutionException(gaee.msg, lines, gaee)
    #except Exception as e:
        # If something goes wrong and is not caught in the
        # algorithm, we catch it here and wrap it
        #lines = [self.tr('Uncaught error while executing algorithm')]
     #   lines = []
      #  lines.append(traceback.format_exc())
        #QgsMessageLog.logMessage('\n'.join(lines), self.tr('Processing'), QgsMessageLog.CRITICAL)
        #raise GeoAlgorithmExecutionException(str(e) + self.tr('\nSee log for more details'), lines, e)

    def helpUrl(self):
        return QgsHelp.helpUrl("processing_algs/{}/{}".format(
            self.provider().id(), self.id())).toString()
Ejemplo n.º 8
0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(
        QCoreApplication.translate('Postprocessing',
                                   'Loading resulting layers'))
    i = 0
    for out in alg.outputs:
        feedback.setProgress(100 * i / float(len(alg.outputs)))
        if out.hidden or not out.open:
            continue
        if isinstance(out, (OutputRaster, OutputVector, OutputTable)):
            try:
                layer = QgsProcessingUtils.mapLayerFromString(
                    out.value, context)
                if layer:
                    layer.setName(out.description)
                    QgsProject.instance().addMapLayer(
                        context.temporaryLayerStore().takeMapLayer(layer))
                else:
                    if ProcessingConfig.getSetting(
                            ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
                        name = os.path.basename(out.value)
                    else:
                        name = out.description

                    isRaster = True if isinstance(out, OutputRaster) else False
                    dataobjects.load(
                        out.value, name, alg.crs,
                        RenderingStyles.getStyle(alg.id(), out.name), isRaster)
            except Exception:
                QgsMessageLog.logMessage(
                    "Error loading result layer:\n" + traceback.format_exc(),
                    'Processing', QgsMessageLog.CRITICAL)
                wrongLayers.append(out.description)
        elif isinstance(out, OutputHTML):
            resultsList.addResult(alg.icon(), out.description, out.value)
        i += 1

    QApplication.restoreOverrideCursor()
    if wrongLayers:
        msg = "The following layers were not correctly generated.<ul>"
        msg += "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += "You can check the log messages to find more information about the execution of the algorithm"
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Ejemplo n.º 9
0
def handleAlgorithmResults(alg, feedback=None, showResults=True):
    wrongLayers = []
    htmlResults = False
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
    i = 0
    for out in alg.outputs:
        feedback.setProgress(100 * i / float(len(alg.outputs)))
        if out.hidden or not out.open:
            continue
        if isinstance(out, (OutputRaster, OutputVector, OutputTable)):
            try:
                if hasattr(out, "layer") and out.layer is not None:
                    out.layer.setName(out.description)
                    QgsProject.instance().addMapLayers([out.layer])
                else:
                    if ProcessingConfig.getSetting(
                            ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
                        name = os.path.basename(out.value)
                    else:
                        name = out.description
                    dataobjects.load(out.value, name, alg.crs,
                                     RenderingStyles.getStyle(alg.commandLineName(),
                                                              out.name))
            except Exception:
                ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
                                       "Error loading result layer:\n" + traceback.format_exc())
                wrongLayers.append(out.description)
        elif isinstance(out, OutputHTML):
            ProcessingResults.addResult(out.description, out.value)
            htmlResults = True
        i += 1

    QApplication.restoreOverrideCursor()
    if wrongLayers:
        msg = "The following layers were not correctly generated.<ul>"
        msg += "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += "You can check the log messages to find more information about the execution of the algorithm"
        feedback.reportError(msg)

    if showResults and htmlResults and not wrongLayers:
        dlg = ResultsDialog()
        dlg.exec_()

    return len(wrongLayers) == 0
Ejemplo n.º 10
0
    def execute(self, parameters, context=None, feedback=None, model=None):
        """The method to use to call a processing algorithm.

        Although the body of the algorithm is in processAlgorithm(),
        it should be called using this method, since it performs
        some additional operations.

        Raises a QgsProcessingException in case anything goes
        wrong.
        :param parameters:
        """

        if feedback is None:
            feedback = QgsProcessingFeedback()
        if context is None:
            context = dataobjects.createContext(feedback)

        self.model = model
        try:
            self.setOutputCRS()
            self.resolveOutputs()
            self.runPreExecutionScript(feedback)
            self.processAlgorithm(parameters, context, feedback)
            feedback.setProgress(100)
            self.convertUnsupportedFormats(context, feedback)
            self.runPostExecutionScript(feedback)
        except QgsProcessingException as gaee:
            lines = [self.tr('Error while executing algorithm')]
            lines.append(traceback.format_exc())
            QgsMessageLog.logMessage(gaee.msg, self.tr('Processing'),
                                     QgsMessageLog.CRITICAL)
            raise QgsProcessingException(gaee.msg, lines, gaee)
        except Exception as e:
            # If something goes wrong and is not caught in the
            # algorithm, we catch it here and wrap it
            lines = [self.tr('Uncaught error while executing algorithm')]
            lines.append(traceback.format_exc())
            QgsMessageLog.logMessage('\n'.join(lines), self.tr('Processing'),
                                     QgsMessageLog.CRITICAL)
            raise QgsProcessingException(
                str(e) + self.tr('\nSee log for more details'), lines, e)
Ejemplo n.º 11
0
    def execute(self, parameters, context=None, feedback=None, model=None):
        """The method to use to call a processing algorithm.

        Although the body of the algorithm is in processAlgorithm(),
        it should be called using this method, since it performs
        some additional operations.

        Raises a QgsProcessingException in case anything goes
        wrong.
        :param parameters:
        """

        if feedback is None:
            feedback = QgsProcessingFeedback()
        if context is None:
            context = dataobjects.createContext(feedback)

        self.model = model
        try:
            self.setOutputCRS()
            self.resolveOutputs()
            self.runPreExecutionScript(feedback)
            self.processAlgorithm(parameters, context, feedback)
            feedback.setProgress(100)
            self.convertUnsupportedFormats(context, feedback)
            self.runPostExecutionScript(feedback)
        except QgsProcessingException as gaee:
            lines = [self.tr('Error while executing algorithm')]
            lines.append(traceback.format_exc())
            QgsMessageLog.logMessage(gaee.msg, self.tr('Processing'), QgsMessageLog.CRITICAL)
            raise QgsProcessingException(gaee.msg, lines, gaee)
        except Exception as e:
            # If something goes wrong and is not caught in the
            # algorithm, we catch it here and wrap it
            lines = [self.tr('Uncaught error while executing algorithm')]
            lines.append(traceback.format_exc())
            QgsMessageLog.logMessage('\n'.join(lines), self.tr('Processing'), QgsMessageLog.CRITICAL)
            raise QgsProcessingException(str(e) + self.tr('\nSee log for more details'), lines, e)
Ejemplo n.º 12
0
    def execute(self, feedback=None, model=None):
        """The method to use to call a processing algorithm.

        Although the body of the algorithm is in processAlgorithm(),
        it should be called using this method, since it performs
        some additional operations.

        Raises a GeoAlgorithmExecutionException in case anything goes
        wrong.
        """

        if feedback is None:
            feedback = QgsProcessingFeedback()

        self.model = model
        try:
            self.setOutputCRS()
            self.resolveOutputs()
            self.evaluateParameterValues()
            self.runPreExecutionScript(feedback)
            self.processAlgorithm(feedback)
            feedback.setProgress(100)
            self.convertUnsupportedFormats(feedback)
            self.runPostExecutionScript(feedback)
        except GeoAlgorithmExecutionException as gaee:
            lines = [self.tr('Error while executing algorithm')]
            lines.append(traceback.format_exc())
            ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, gaee.msg)
            raise GeoAlgorithmExecutionException(gaee.msg, lines, gaee)
        except Exception as e:
            # If something goes wrong and is not caught in the
            # algorithm, we catch it here and wrap it
            lines = [self.tr('Uncaught error while executing algorithm')]
            lines.append(traceback.format_exc())
            ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, lines)
            raise GeoAlgorithmExecutionException(
                str(e) + self.tr('\nSee log for more details'), lines, e)
Ejemplo n.º 13
0
    def processAlgorithm(self, parameters, context: QgsProcessingContext,
                         feedback: QgsProcessingFeedback):
        """Here is where the processing itself takes place."""

        feedback.setProgress(0)

        # init params
        database = self.parameterAsString(parameters, self.DATABASE, context)
        template_inp_file = self.parameterAsFile(parameters,
                                                 self.TEMPLATE_INP_FILE,
                                                 context)
        inp_file = self.parameterAsFileOutput(parameters, self.INP_FILE,
                                              context)

        # Connect to QGEP database and perform translation
        qs = QgepSwmm(datetime.datetime.today().isoformat(), database,
                      inp_file, template_inp_file, None, None, None, None)
        qs.write_input()

        if qs.feedbacks is not None:
            for i in range(len(qs.feedbacks)):
                feedback.reportError(qs.feedbacks[i])

        return {self.INP_FILE: inp_file}
Ejemplo n.º 14
0
    def processAlgorithm(self, parameters, context: QgsProcessingContext,
                         feedback: QgsProcessingFeedback):
        """Here is where the processing itself takes place."""

        # init params
        log_file = self.parameterAsFile(parameters, self.LOG_FILE, context)
        output_file = self.parameterAsFile(parameters, self.OUT_FILE, context)
        inp_file = self.parameterAsFileOutput(parameters, self.INP_FILE,
                                              context)
        swmm_cli = os.path.abspath(ProcessingConfig.getSetting('SWMM_PATH'))
        if not swmm_cli:
            # raise GeoAlgorithmExecutionException(
            # 'Swmm command line toom is not configured.\n\
            # Please configure it before running Swmm algorithms.')
            raise QgsProcessingException(
                self.tr('Swmm command line tool is not configured.\n\
                    Please configure it before running Swmm algorithms.'))

        qs = QgepSwmm(None, None, inp_file, None, output_file, log_file,
                      swmm_cli, None)
        prompt = qs.execute_swmm()
        if qs.feedbacks is not None:
            for i in range(len(qs.feedbacks)):
                feedback.reportError(qs.feedbacks[i])

        feedback.pushInfo(prompt)

        if re.search('There are errors', prompt):
            feedback.reportError(prompt)
            feedback.reportError(
                'There were errors, look into logs for details: {log_file}'.
                format(log_file=log_file))

        feedback.setProgress(100)

        return {}
Ejemplo n.º 15
0
    def processAlgorithm(self, parameters, context,
                         feedback: QgsProcessingFeedback):
        # Input / Output
        source = self.parameterAsSource(parameters, self.INPUT, context)
        (sink, dest_id) = self.create_output_sink(parameters, context,
                                                  source.sourceCrs())

        # Filter parameters
        observation_type_indices = self.parameterAsEnums(
            parameters, self.OBSERVATION_TYPE, context)
        observation_types = list(
            map(lambda i: self.OBSERVATION_TYPES[i][0],
                observation_type_indices))

        from_date = None
        from_date_string = self.parameterAsString(parameters, self.FROM_DATE,
                                                  context)
        if from_date_string:
            from_date = datetime.fromisoformat(from_date_string)

        to_date = None
        to_date_string = self.parameterAsString(parameters, self.TO_DATE,
                                                context)
        if to_date_string:
            to_date = datetime.fromisoformat(to_date_string)

        fire_connection_string = self.settings.value("fire_connection_string")
        fireDb = FireDb(fire_connection_string, debug=True)

        features = list(source.getFeatures())
        total_num_features = len(features)
        total_num_features_processed = 0
        # for current, feature in enumerate(features):
        for feature in features:
            if feedback.isCanceled():
                return {}
            wkt = feature.geometry().asWkt().upper()
            geometry = Geometry(wkt)
            observations = fireDb.hent_observationer_naer_geometri(
                geometri=geometry, afstand=0, tidfra=from_date, tidtil=to_date)

            pid_list = self.get_pids_from_observations(observations)
            geometriobjekter = self.get_geometriobjekter_from_pids(
                fireDb, pid_list)
            idents = self.get_idents_from_pids(fireDb, pid_list)

            feedback.setProgressText(
                "Fandt {antal} observationer".format(antal=len(observations)))
            feedback.setProgressText("Fandt {antal} geometriobjekter".format(
                antal=len(geometriobjekter)))
            feedback.setProgressText(
                "Fandt {antal} idents".format(antal=len(idents)))
            for current, observation in enumerate(observations):
                observation_type_id = observation.observationstypeid
                if observation_type_id in observation_types:
                    feature = self.create_feature_from_observation(
                        observation, geometriobjekter, idents, feedback)
                    if feature:
                        sink.addFeature(feature, QgsFeatureSink.FastInsert)
            total_num_features_processed = total_num_features_processed + 1
            feedback.setProgress(total_num_features_processed /
                                 total_num_features)
            if feedback.isCanceled():
                return {}

        apply_theme = self.parameterAsBool(parameters, self.APPLY_THEME,
                                           context)
        if apply_theme:
            style_file = os.path.join(os.path.dirname(__file__), "..",
                                      "styles", "observation.qml")
            alg_params = {"INPUT": dest_id, "STYLE": style_file}
            processing.run(
                "qgis:setstyleforvectorlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        return {self.OUTPUT: dest_id}
Ejemplo n.º 16
0
def execute_in_place_run(alg,
                         parameters,
                         context=None,
                         feedback=None,
                         raise_exceptions=False):
    """Executes an algorithm modifying features in-place in the input layer.

    :param alg: algorithm to run
    :type alg: QgsProcessingAlgorithm
    :param parameters: parameters of the algorithm
    :type parameters: dict
    :param context: context, defaults to None
    :type context: QgsProcessingContext, optional
    :param feedback: feedback, defaults to None
    :type feedback: QgsProcessingFeedback, optional
    :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback
    :type raise_exceptions: boo, default to False
    :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable
    :return: a tuple with true if success and results
    :rtype: tuple
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext(feedback)

    # Only feature based algs have sourceFlags
    try:
        if alg.sourceFlags(
        ) & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks:
            context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)
    except AttributeError:
        pass

    active_layer = parameters['INPUT']

    # Run some checks and prepare the layer for in-place execution by:
    # - getting the active layer and checking that it is a vector
    # - making the layer editable if it was not already
    # - selecting all features if none was selected
    # - checking in-place support for the active layer/alg/parameters
    # If one of the check fails and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        if active_layer is None:
            raise QgsProcessingException(tr("There is not active layer."))

        if not isinstance(active_layer, QgsVectorLayer):
            raise QgsProcessingException(
                tr("Active layer is not a vector layer."))

        if not active_layer.isEditable():
            if not active_layer.startEditing():
                raise QgsProcessingException(
                    tr("Active layer is not editable (and editing could not be turned on)."
                       ))

        if not alg.supportInPlaceEdit(active_layer):
            raise QgsProcessingException(
                tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."
                   ))
    except QgsProcessingException as e:
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing',
                                 Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)
        return False, {}

    if not active_layer.selectedFeatureIds():
        active_layer.selectAll()

    # Make sure we are working on selected features only
    parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(
        active_layer.id(), True)
    parameters['OUTPUT'] = 'memory:'

    req = QgsFeatureRequest(QgsExpression(r"$id < 0"))
    req.setFlags(QgsFeatureRequest.NoGeometry)
    req.setSubsetOfAttributes([])

    # Start the execution
    # If anything goes wrong and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        new_feature_ids = []

        active_layer.beginEditCommand(alg.displayName())

        # Checks whether the algorithm has a processFeature method
        if hasattr(alg, 'processFeature'):  # in-place feature editing
            # Make a clone or it will crash the second time the dialog
            # is opened and run
            alg = alg.create()
            if not alg.prepare(parameters, context, feedback):
                raise QgsProcessingException(
                    tr("Could not prepare selected algorithm."))
            # Check again for compatibility after prepare
            if not alg.supportInPlaceEdit(active_layer):
                raise QgsProcessingException(
                    tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."
                       ))
            field_idxs = range(len(active_layer.fields()))
            iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds())
            iterator_req.setInvalidGeometryCheck(
                context.invalidGeometryCheck())
            feature_iterator = active_layer.getFeatures(iterator_req)
            step = 100 / len(active_layer.selectedFeatureIds()
                             ) if active_layer.selectedFeatureIds() else 1
            for current, f in enumerate(feature_iterator):
                feedback.setProgress(current * step)
                if feedback.isCanceled():
                    break

                # need a deep copy, because python processFeature implementations may return
                # a shallow copy from processFeature
                input_feature = QgsFeature(f)
                new_features = alg.processFeature(input_feature, context,
                                                  feedback)
                new_features = QgsVectorLayerUtils.makeFeaturesCompatible(
                    new_features, active_layer)

                if len(new_features) == 0:
                    active_layer.deleteFeature(f.id())
                elif len(new_features) == 1:
                    new_f = new_features[0]
                    if not f.geometry().equals(new_f.geometry()):
                        active_layer.changeGeometry(f.id(), new_f.geometry())
                    if f.attributes() != new_f.attributes():
                        active_layer.changeAttributeValues(
                            f.id(), dict(zip(field_idxs, new_f.attributes())),
                            dict(zip(field_idxs, f.attributes())))
                    new_feature_ids.append(f.id())
                else:
                    active_layer.deleteFeature(f.id())
                    # Get the new ids
                    old_ids = set(
                        [f.id() for f in active_layer.getFeatures(req)])
                    if not active_layer.addFeatures(new_features):
                        raise QgsProcessingException(
                            tr("Error adding processed features back into the layer."
                               ))
                    new_ids = set(
                        [f.id() for f in active_layer.getFeatures(req)])
                    new_feature_ids += list(new_ids - old_ids)

            results, ok = {}, True

        else:  # Traditional 'run' with delete and add features cycle

            # There is no way to know if some features have been skipped
            # due to invalid geometries
            if context.invalidGeometryCheck(
            ) == QgsFeatureRequest.GeometrySkipInvalid:
                selected_ids = active_layer.selectedFeatureIds()
            else:
                selected_ids = []

            results, ok = alg.run(parameters, context, feedback)

            if ok:
                result_layer = QgsProcessingUtils.mapLayerFromString(
                    results['OUTPUT'], context)
                # TODO: check if features have changed before delete/add cycle

                new_features = []

                # Check if there are any skipped features
                if context.invalidGeometryCheck(
                ) == QgsFeatureRequest.GeometrySkipInvalid:
                    missing_ids = list(
                        set(selected_ids) - set(result_layer.allFeatureIds()))
                    if missing_ids:
                        for f in active_layer.getFeatures(
                                QgsFeatureRequest(missing_ids)):
                            if not f.geometry().isGeosValid():
                                new_features.append(f)

                active_layer.deleteFeatures(active_layer.selectedFeatureIds())

                for f in result_layer.getFeatures():
                    new_features.extend(
                        QgsVectorLayerUtils.makeFeaturesCompatible(
                            [f], active_layer))

                # Get the new ids
                old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                if not active_layer.addFeatures(new_features):
                    raise QgsProcessingException(
                        tr("Error adding processed features back into the layer."
                           ))
                new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                new_feature_ids += list(new_ids - old_ids)

        active_layer.endEditCommand()

        if ok and new_feature_ids:
            active_layer.selectByIds(new_feature_ids)
        elif not ok:
            active_layer.rollBack()

        return ok, results

    except QgsProcessingException as e:
        active_layer.endEditCommand()
        active_layer.rollBack()
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing',
                                 Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)

    return False, {}
Ejemplo n.º 17
0
    def processAlgorithm(self, parameters, context: QgsProcessingContext,
                         feedback: QgsProcessingFeedback):
        """Here is where the processing itself takes place."""

        feedback.setProgress(0)
        na = qgis_utils.plugins["qgepplugin"].network_analyzer

        # init params
        reach_layer = self.parameterAsVectorLayer(parameters, self.REACH_LAYER,
                                                  context)
        flow_layer = self.parameterAsVectorLayer(parameters,
                                                 self.FLOWTIMES_LAYER, context)
        fk_reach_field = self.parameterAsFields(parameters,
                                                self.FK_REACH_FIELD,
                                                context)[0]
        flow_time_field = self.parameterAsFields(parameters,
                                                 self.FLOWTIMES_FIELD,
                                                 context)[0]

        # create feature sink
        fields = QgsFields()
        fields.append(QgsField('flow_time', QVariant.Double))
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields,
                                               QgsWkbTypes.LineString,
                                               reach_layer.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        # get selected reach
        iterator = reach_layer.getSelectedFeatures()
        feature_count = reach_layer.selectedFeatureCount()
        if feature_count != 1:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.REACH_LAYER))
        reach_feature = QgsFeature()
        iterator.nextFeature(reach_feature)
        assert reach_feature.isValid()
        qgep_reach_obj_id = reach_feature.attribute('obj_id')

        # get top node
        reach_features = na.getFeaturesByAttr(na.getEdgeLayer(), 'obj_id',
                                              [qgep_reach_obj_id]).asDict()
        assert len(reach_features) > 0
        from_pos = 1
        top_node = None
        for fid, reach_feature in reach_features.items():
            if from_pos > reach_feature.attribute('from_pos'):
                top_node = reach_feature.attribute('from_obj_id_interpolate')
                from_pos = reach_feature.attribute('from_pos')
        assert top_node is not None
        nodes = na.getFeaturesByAttr(na.getNodeLayer(), 'obj_id',
                                     [top_node]).asDict()
        assert len(nodes) == 1
        top_node_id = next(iter(nodes.values())).id()

        # create graph
        _, edges = na.getTree(top_node_id)
        feedback.setProgress(50)
        cache_edge_features = na.getFeaturesById(
            na.getEdgeLayer(),
            [edge[2]['feature'] for edge in edges]).asDict()

        # join and accumulate flow times
        i = -1
        flow_time = 0.0
        while True:
            i += 1
            feedback.setProgress(50 + i / len(edges) * 50)
            if i >= len(edges):
                break

            edge = edges[i]
            edge_feature = cache_edge_features[edge[2]['feature']]
            # TODO: if top_pos != 1 => merge
            if edge_feature.attribute('type') != 'reach':
                continue
            rate = edge_feature.attribute('to_pos') - edge_feature.attribute(
                'from_pos')
            assert 0 < rate <= 1

            expression = QgsExpression("{fk_reach} = '{obj_id}'".format(
                fk_reach=fk_reach_field, obj_id=edge_feature['obj_id']))
            print(expression.expression())
            request = QgsFeatureRequest(expression)
            flow_time_feature = next(flow_layer.getFeatures(request))

            if not flow_time_feature.isValid():
                break

            flow_time += rate * flow_time_feature.attribute(flow_time_field)

            sf = QgsFeature()
            sf.setFields(fields)
            sf.setAttribute('flow_time', flow_time)
            sf.setGeometry(edge_feature.geometry())
            sink.addFeature(sf, QgsFeatureSink.FastInsert)

        #f.setAttributes(attrs)
        #sink.addFeature(f, QgsFeatureSink.FastInsert)
        #feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 18
0
def generate_od_routes(
    network_layer: QgsVectorLayer,
    origin_layer: QgsVectorLayer,
    poi_layer: QgsVectorLayer,
    size_field: str,
    class_field: str,
    work_layer: QgsVectorLayer = None,
    work_size_field: str = None,
    school_layer: QgsVectorLayer = None,
    school_size_field: str = None,
    origin_weight_field: str = None,
    socio_data=None,
    health_data=None,
    diversity_data=None,
    join_on: str = None,
    max_distance: int = 25000,
    return_layer: bool = True,
    return_raw: bool = False,
    feedback: QgsProcessingFeedback = None,
) -> QgsVectorLayer:
    """
    Shortest path algorithm based on Dijkstra's algorithm

    :param network_layer: road network
    :param points_layer: combined from to points
    :param relations_data: tabular from to id data
    :param origin_field: name of from field
    :param destination_field: name of to field
    :param max_distance: maximum distance/cost
    :param crs: output layer crs
    """

    if not network_layer.wkbType() & QgsWkbTypes.LineString:
        raise Exception('Network layer must be of type LineString')
    crs = network_layer.crs()

    ## prepare graph
    director = QgsVectorLayerDirector(
        source=network_layer,
        directionFieldId=-1,
        directDirectionValue='',
        reverseDirectionValue='',
        bothDirectionValue='',
        defaultDirection=QgsVectorLayerDirector.DirectionBoth,
    )
    # First strategy is for actual shortest distance calculation
    director.addStrategy(QgsNetworkDistanceStrategy())  # 0
    # Second strategy is a hack to be able to recover the edge id
    director.addStrategy(SaveFidStrategy())  # 1
    builder = QgsGraphBuilder(crs)

    ## spatial index

    poi_sidx = QgsSpatialIndex(poi_layer)
    work_sidx = QgsSpatialIndex(work_layer)
    school_sidx = QgsSpatialIndex(school_layer)

    ## prepare points
    orig_n = len(origin_layer)
    poi_n = len(poi_layer)
    work_n = len(work_layer) if work_layer else 0
    school_n = len(school_layer) if school_layer else 0
    dest_n = poi_n + work_n + school_n

    orig_points = [None] * orig_n
    orig_sizes = np.zeros(orig_n, dtype=float)
    if socio_data:
        orig_socio = np.zeros(orig_n, dtype=float)
    dest_points = [None] * dest_n
    dest_sizes = [None] * dest_n
    dest_fids = [None] * dest_n
    dest_cats = [None] * dest_n

    orig_id_field = 'deso'
    for i, feat in enumerate(origin_layer.getFeatures()):
        orig_points[i] = feat.geometry().asPoint()
        orig_sizes[i] = feat[size_field] * (
            feat[origin_weight_field] if origin_weight_field else 1
        )

        if socio_data:
            orig_socio[i] = socio_data[
                feat[orig_id_field]
            ]  # FIXME: check if all origins have data

    if socio_data:
        orig_socio = orig_socio / np.mean(orig_socio)
        orig_sizes *= orig_socio

    for i, feat in enumerate(poi_layer.getFeatures()):
        dest_points[i] = feat.geometry().asPoint()
        dest_fids[i] = feat.id()
        dest_sizes[i] = 1  # TODO: dest size
        dest_cats[i] = poi_class_map.get(feat[class_field])

    if work_layer:
        for i, feat in enumerate(work_layer.getFeatures(), start=poi_n):
            dest_points[i] = feat.geometry().asPoint()
            dest_fids[i] = feat.id()
            dest_sizes[i] = feat[work_size_field]  # TODO: dest size
            dest_cats[i] = 'work'

    if school_layer:
        for i, feat in enumerate(school_layer.getFeatures(), start=(poi_n + work_n)):
            dest_points[i] = feat.geometry().asPoint()
            dest_fids[i] = feat.id()
            dest_sizes[i] = feat[school_size_field]  # TODO: dest size
            dest_cats[i] = 'school'

    # points = [origin.point for origin in origins_data] + [
    #    dest.point for dest in dests_data
    # ]

    if feedback is None:
        feedback = QgsProcessingFeedback()

        def progress(p):
            if int(10 * p % 100) == 0:
                print(f'{int(p):#3d}%')

        feedback.progressChanged.connect(progress)

    with timing('build network graph'):
        tied_points = director.makeGraph(
            builder, orig_points + dest_points, feedback=feedback
        )
        graph = builder.graph()

    orig_tied_points = tied_points[:orig_n]
    dest_tied_points = tied_points[orig_n:]

    poi_tied_points = dest_tied_points[:poi_n]
    work_tied_points = dest_tied_points[poi_n : poi_n + work_n]
    school_tied_points = dest_tied_points[poi_n + work_n :]

    dest_fid_to_tied_points = dict(zip(dest_fids, enumerate(dest_tied_points)))

    poi_fid_to_tied_points = dict(zip(dest_fids[:poi_n], enumerate(poi_tied_points)))
    work_fid_to_tied_points = dict(
        zip(dest_fids[poi_n : poi_n + work_n], enumerate(work_tied_points, start=poi_n))
    )
    school_fid_to_tied_points = dict(
        zip(
            dest_fids[poi_n + work_n :],
            enumerate(school_tied_points, start=poi_n + work_n),
        )
    )

    orig_dests = [None] * orig_n
    for i, point in enumerate(orig_points):
        orig_dests[i] = (
            [
                poi_fid_to_tied_points[fid]
                for fid in poi_sidx.nearestNeighbor(
                    point, neighbors=MAX_NEIGHBORS, maxDistance=max_distance
                )
            ]
            + [
                work_fid_to_tied_points[fid]
                for fid in work_sidx.nearestNeighbor(
                    point, neighbors=MAX_NEIGHBORS, maxDistance=max_distance
                )
            ]
            + [
                school_fid_to_tied_points[fid]
                for fid in school_sidx.nearestNeighbor(
                    point, neighbors=MAX_NEIGHBORS, maxDistance=max_distance
                )
            ]
        )

    step = 100.0 / orig_n
    time_dijkstra = 0.0
    time_find = 0.0
    time_route = 0.0
    with timing('calculate connecting routes'):
        routes = []
        # for i, (origin_fid, dest_fids) in enumerate(od_data):
        for i, (orig_point, dests) in enumerate(zip(orig_tied_points, orig_dests)):
            origin_vertex_id = graph.findVertex(orig_point)

            # Calculate the tree and cost using the distance strategy (#0)
            ts = time()
            (tree, cost) = QgsGraphAnalyzer.dijkstra(graph, origin_vertex_id, 0)
            time_dijkstra += time() - ts

            for j, dest_point in dests:
                if feedback.isCanceled():
                    return
                if dest_sizes[j] <= 0:
                    continue
                category = dest_cats[j]
                if category is None:
                    continue
                ts = time()
                dest_vertex_id = graph.findVertex(dest_point)
                time_find += time() - ts
                if tree[dest_vertex_id] != -1 and (
                    cost[dest_vertex_id] <= MAX_DISTANCE_M
                    or MAX_DISTANCE_M <= 0  # TODO: enable skipping max distance
                ):
                    route_distance = cost[dest_vertex_id]
                    # route_points = [graph.vertex(dest_vertex_id).point()]
                    cur_vertex_id = dest_vertex_id
                    route_fids = []
                    # Iterate the graph from dest to origin saving the edges
                    ts = time()
                    while cur_vertex_id != origin_vertex_id:
                        cur_edge = graph.edge(tree[cur_vertex_id])
                        # Here we recover the edge id through strategy #1
                        route_fids.append(cur_edge.cost(1))
                        cur_vertex_id = cur_edge.fromVertex()
                        # route_points.append(graph.vertex(cur_vertex_id).point())
                    time_route += time() - ts

                    # route_points.reverse()
                    # route_geom = QgsGeometry.fromPolylineXY(route_points))

                    # Hack to remove duplicate fids
                    route_fids = list(
                        dict.fromkeys(route_fids)
                    )  # NOTE: requires python >= 3.7 for ordered dict FIXME: add python version check
                    route_fids.reverse()

                    # Calc
                    # TODO: Move to matrix and vectorize calculation using numpy
                    gravity_value = poi_gravity_values[category]
                    bike_params = mode_params_bike[category]
                    ebike_params = mode_params_ebike[category]

                    # NOTE: we include dest size in decay here
                    decay = dest_sizes[j] * math.exp(
                        gravity_value * route_distance / 1000.0
                    )
                    p_bike = sigmoid(*bike_params, route_distance)
                    p_ebike = sigmoid(*ebike_params, route_distance)

                    # TODO: use namedtuple or dataclass
                    routes.append(
                        Route(
                            i,
                            j,
                            category,
                            route_distance,
                            decay,
                            p_bike,
                            p_ebike,
                            route_fids,
                        )
                    )
            feedback.setProgress(i * step)

        print(f'dijkstra took: {time_dijkstra:#1.2f} sec')
        print(f'find vertex took: {time_find:#1.2f} sec')
        print(f'route took: {time_route:#1.2f} sec')

    with timing('post process routes'):
        alpha_bike = 0.8
        alpha_ebke = 0.2

        decay_sums = {cat: defaultdict(float) for cat in poi_categories}
        bike_values = {cat: defaultdict(float) for cat in poi_categories}
        ebike_values = {cat: defaultdict(float) for cat in poi_categories}

        for route in routes:
            # NOTE: dest size is included in decay
            decay_sums[route.cat][route.i] += route.decay
        for route in routes:
            decay_sum = decay_sums[route.cat][route.i]
            # TODO: add T_p and alpha_m
            T_p = trip_generation[route.cat]
            bike_value = (
                T_p
                * alpha_bike
                * orig_sizes[route.i]
                * route.p_bike
                * route.decay
                / decay_sum
            )
            ebike_value = (
                T_p
                * alpha_ebke
                * orig_sizes[route.i]
                * route.p_ebike
                * route.decay
                / decay_sum
            )
            for fid in route.net_fids:
                bike_values[route.cat][fid] += float(bike_value)
                ebike_values[route.cat][fid] += float(ebike_value)

    # FIXME: Un-kludge this
    with timing('create result features'):
        fields = get_fields()

        segments = []
        for feature in network_layer.getFeatures():
            fid = feature.id()
            segment = QgsFeature(fields)
            segment.setGeometry(QgsGeometry(feature.geometry()))

            segment['network_fid'] = fid
            flow = 0.0
            for cat in poi_categories:
                bike_field = f'{cat}_bike_value'
                ebike_field = f'{cat}_ebike_value'

                flow_bike = segment[bike_field] = bike_values[cat].get(fid)
                flow_ebke = segment[ebike_field] = ebike_values[cat].get(fid)

                if flow_bike is not None:
                    flow += flow_bike
                if flow_ebke is not None:
                    flow += flow_ebke

            segment['flow'] = flow
            segment['lts'] = feature['lts']
            segment['vgu'] = feature['vgu']
            segment['R'] = flow * feature['ratio']
            segments.append(segment)

    if not return_layer:
        if return_raw:
            return segments, bike_values, ebike_values
        return segments

    with timing('create result layer'):
        output_layer = QgsVectorLayer(
            f'LineString?crs={crs.toWkt()}', 'segments', 'memory'
        )
        with edit(output_layer):
            for field in fields:
                output_layer.addAttribute(field)
            output_layer.addFeatures(segments, flags=QgsFeatureSink.FastInsert)

    return output_layer
Ejemplo n.º 19
0
def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=None, raise_exceptions=False):
    """Executes an algorithm modifying features in-place in the input layer.

    The input layer must be editable or an exception is raised.

    :param alg: algorithm to run
    :type alg: QgsProcessingAlgorithm
    :param active_layer: the editable layer
    :type active_layer: QgsVectoLayer
    :param parameters: parameters of the algorithm
    :type parameters: dict
    :param context: context, defaults to None
    :param context: QgsProcessingContext, optional
    :param feedback: feedback, defaults to None
    :param feedback: QgsProcessingFeedback, optional
    :raises QgsProcessingException: raised when the layer is not editable or the layer cannot be found in the current project
    :return: a tuple with true if success and results
    :rtype: tuple
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext(feedback)

    if active_layer is None or not active_layer.isEditable():
        raise QgsProcessingException(tr("Layer is not editable or layer is None."))

    if not alg.supportInPlaceEdit(active_layer):
        raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))

    parameters['OUTPUT'] = 'memory:'

    try:
        new_feature_ids = []

        active_layer.beginEditCommand(alg.displayName())

        req = QgsFeatureRequest(QgsExpression(r"$id < 0"))
        req.setFlags(QgsFeatureRequest.NoGeometry)
        req.setSubsetOfAttributes([])

        # Checks whether the algorithm has a processFeature method
        if hasattr(alg, 'processFeature'):  # in-place feature editing
            # Make a clone or it will crash the second time the dialog
            # is opened and run
            alg = alg.create()
            if not alg.prepare(parameters, context, feedback):
                raise QgsProcessingException(tr("Could not prepare selected algorithm."))
            # Check again for compatibility after prepare
            if not alg.supportInPlaceEdit(active_layer):
                raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
            field_idxs = range(len(active_layer.fields()))
            feature_iterator = active_layer.getFeatures(QgsFeatureRequest(active_layer.selectedFeatureIds())) if parameters['INPUT'].selectedFeaturesOnly else active_layer.getFeatures()
            step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1
            for current, f in enumerate(feature_iterator):
                feedback.setProgress(current * step)
                if feedback.isCanceled():
                    break

                # need a deep copy, because python processFeature implementations may return
                # a shallow copy from processFeature
                input_feature = QgsFeature(f)
                new_features = alg.processFeature(input_feature, context, feedback)
                new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer)
                if len(new_features) == 0:
                    active_layer.deleteFeature(f.id())
                elif len(new_features) == 1:
                    new_f = new_features[0]
                    if not f.geometry().equals(new_f.geometry()):
                        active_layer.changeGeometry(f.id(), new_f.geometry())
                    if f.attributes() != new_f.attributes():
                        active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes())))
                    new_feature_ids.append(f.id())
                else:
                    active_layer.deleteFeature(f.id())
                    # Get the new ids
                    old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    if not active_layer.addFeatures(new_features):
                        raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                    new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    new_feature_ids += list(new_ids - old_ids)

            results, ok = {}, True

        else:  # Traditional 'run' with delete and add features cycle
            results, ok = alg.run(parameters, context, feedback)

            if ok:
                result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context)
                # TODO: check if features have changed before delete/add cycle
                active_layer.deleteFeatures(active_layer.selectedFeatureIds())
                new_features = []
                for f in result_layer.getFeatures():
                    new_features.extend(QgsVectorLayerUtils.
                                        makeFeaturesCompatible([f], active_layer))

                # Get the new ids
                old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                if not active_layer.addFeatures(new_features):
                    raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                new_feature_ids += list(new_ids - old_ids)

        active_layer.endEditCommand()

        if ok and new_feature_ids:
            active_layer.selectByIds(new_feature_ids)
        elif not ok:
            active_layer.rollBack()

        return ok, results

    except QgsProcessingException as e:
        active_layer.endEditCommand()
        active_layer.rollBack()
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)))

    return False, {}
Ejemplo n.º 20
0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parameters={}):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
    i = 0

    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))
        try:
            layer = QgsProcessingUtils.mapLayerFromString(l, context, typeHint=details.layerTypeHint)
            if layer is not None:
                set_layer_name(layer, details)

                '''If running a model, the execution will arrive here when an algorithm that is part of
                that model is executed. We check if its output is a final otuput of the model, and
                adapt the output name accordingly'''
                outputName = details.outputName
                expcontext = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                expcontext.appendScope(scope)
                for out in alg.outputDefinitions():
                    if out.name() not in parameters:
                        continue
                    outValue = parameters[out.name()]
                    if hasattr(outValue, "sink"):
                        outValue = outValue.sink.valueAsString(expcontext)[0]
                    else:
                        outValue = str(outValue)
                    if outValue == l:
                        outputName = out.name()
                        break
                style = None
                if outputName:
                    style = RenderingStyles.getStyle(alg.id(), outputName)
                if style is None:
                    if layer.type() == QgsMapLayer.RasterLayer:
                        style = ProcessingConfig.getSetting(ProcessingConfig.RASTER_STYLE)
                    else:
                        if layer.geometryType() == QgsWkbTypes.PointGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POINT_STYLE)
                        elif layer.geometryType() == QgsWkbTypes.LineGeometry:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_LINE_STYLE)
                        else:
                            style = ProcessingConfig.getSetting(ProcessingConfig.VECTOR_POLYGON_STYLE)
                if style:
                    layer.loadNamedStyle(style)

                details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))

                if details.postProcessor():
                    details.postProcessor().postProcessLayer(layer, context, feedback)

            else:
                wrongLayers.append(str(l))
        except Exception:
            QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical)
            wrongLayers.append(str(l))
        i += 1

    feedback.setProgress(100)

    if wrongLayers:
        msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.")
        msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.")
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Ejemplo n.º 21
0
    def processAlgorithm(self, parameters, context: QgsProcessingContext,
                         feedback: QgsProcessingFeedback):
        """Here is where the processing itself takes place."""

        feedback.setProgress(0)

        # init params
        out_file = self.parameterAsFileOutput(parameters, self.OUT_FILE,
                                              context)

        # create feature sink for node summary
        fields = QgsFields()

        fields.append(QgsField('id', QVariant.String))
        fields.append(QgsField('type', QVariant.String))
        fields.append(QgsField('average_depth', QVariant.Double))
        fields.append(QgsField('maximum_depth', QVariant.Double))
        fields.append(QgsField('maximum_hgl', QVariant.Double))
        fields.append(QgsField('time_max_day', QVariant.Int))
        fields.append(QgsField('time_max_time', QVariant.Double))
        fields.append(QgsField('reported_max_depth', QVariant.Double))
        (sink_node, dest_id) = self.parameterAsSink(parameters,
                                                    self.NODE_SUMMARY, context,
                                                    fields)
        if sink_node is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.NODE_SUMMARY))

        # Get node summary from output file
        qs = QgepSwmm(None, None, None, None, out_file, None, None, None)
        if qs.feedbacks is not None:
            for i in range(len(qs.feedbacks)):
                feedback.reportError(qs.feedbacks[i])
        node_summary = qs.extract_node_depth_summary()

        # Fill node summary with data
        for ns in node_summary:
            sf = QgsFeature()
            sf.setFields(fields)
            for k in ns.keys():
                index = fields.indexOf(k)
                if index != -1:
                    sf.setAttribute(k, ns[k])
            sink_node.addFeature(sf, QgsFeatureSink.FastInsert)
        feedback.setProgress(50)

        # create feature sink for link summary
        fields = QgsFields()
        fields.append(QgsField('id', QVariant.String))
        fields.append(QgsField('type', QVariant.String))
        fields.append(QgsField('maximum_flow', QVariant.Double))
        fields.append(QgsField('time_max_day', QVariant.Int))
        fields.append(QgsField('time_max_time', QVariant.String))
        fields.append(QgsField('maximum_velocity', QVariant.Double))
        fields.append(QgsField('max_over_full_flow', QVariant.Double))
        fields.append(QgsField('max_over_full_depth', QVariant.Double))
        (sink_link, dest_id) = self.parameterAsSink(parameters,
                                                    self.LINK_SUMMARY, context,
                                                    fields)
        if sink_link is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.LINK_SUMMARY))

        # Get link summary from output file
        link_summary = qs.extract_link_flow_summary()

        # Fill node summary with data
        for ns in link_summary:
            sf = QgsFeature()
            sf.setFields(fields)
            for k in ns.keys():
                index = fields.indexOf(k)
                if index != -1:
                    sf.setAttribute(k, ns[k])
            sink_link.addFeature(sf, QgsFeatureSink.FastInsert)
        feedback.setProgress(100)

        return {self.NODE_SUMMARY: sink_node, self.LINK_SUMMARY: sink_link}
Ejemplo n.º 22
0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
    i = 0
    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))

        try:
            layer = QgsProcessingUtils.mapLayerFromString(l, context)
            if layer is not None:
                layer.setName(details.name)
                details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
            else:
                name = details.name
                if ProcessingConfig.getSetting(
                        ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
                    name = os.path.basename(l)
                dataobjects.load(l, name, alg.crs,
                                 RenderingStyles.getStyle(alg.id(), l))
        except Exception:
            QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL)
            #wrongLayers.append(out.description())
            wrongLayers.append(l)
    # for out in alg.outputs:
    #     feedback.setProgress(100 * i / float(len(alg.outputs)))
    #     if out.flags() & QgsProcessingParameterDefinition.FlagHidden or not out.open:
    #         continue
    #     if isinstance(out, (OutputRaster, OutputVector, OutputTable)):
    #         try:
    #             layer = QgsProcessingUtils.mapLayerFromString(out.value, context)
    #             if layer:
    #                 layer.setName(out.description)
    #                 QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
    #             else:
    #                 if ProcessingConfig.getSetting(
    #                         ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
    #                     name = os.path.basename(out.value)
    #                 else:
    #                     name = out.description
    #
    #                 isRaster = True if isinstance(out, OutputRaster) else False
    #                 dataobjects.load(out.value, name, alg.crs,
    #                                  RenderingStyles.getStyle(alg.id(), out.name),
    #                                  isRaster)
    #         except Exception:
    #             QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL)
    #             wrongLayers.append(out.description)
    #     elif isinstance(out, OutputHTML):
    #         resultsList.addResult(alg.icon(), out.description, out.value)
        i += 1

    QApplication.restoreOverrideCursor()
    if wrongLayers:
        msg = "The following layers were not correctly generated.<ul>"
        msg += "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += "You can check the log messages to find more information about the execution of the algorithm"
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Ejemplo n.º 23
0
def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(
        QCoreApplication.translate('Postprocessing',
                                   'Loading resulting layers'))
    i = 0
    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(
                100 * i / float(len(context.layersToLoadOnCompletion())))

        try:
            layer = QgsProcessingUtils.mapLayerFromString(l, context)
            if layer is not None:
                layer.setName(details.name)
                details.project.addMapLayer(
                    context.temporaryLayerStore().takeMapLayer(layer))
            else:
                name = details.name
                if ProcessingConfig.getSetting(
                        ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
                    name = os.path.basename(l)
                dataobjects.load(l, name, alg.crs,
                                 RenderingStyles.getStyle(alg.id(), l))
        except Exception:
            QgsMessageLog.logMessage(
                "Error loading result layer:\n" + traceback.format_exc(),
                'Processing', QgsMessageLog.CRITICAL)
            #wrongLayers.append(out.description())
            wrongLayers.append(l)
    # for out in alg.outputs:
    #     feedback.setProgress(100 * i / float(len(alg.outputs)))
    #     if out.flags() & QgsProcessingParameterDefinition.FlagHidden or not out.open:
    #         continue
    #     if isinstance(out, (OutputRaster, OutputVector, OutputTable)):
    #         try:
    #             layer = QgsProcessingUtils.mapLayerFromString(out.value, context)
    #             if layer:
    #                 layer.setName(out.description)
    #                 QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
    #             else:
    #                 if ProcessingConfig.getSetting(
    #                         ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
    #                     name = os.path.basename(out.value)
    #                 else:
    #                     name = out.description
    #
    #                 isRaster = True if isinstance(out, OutputRaster) else False
    #                 dataobjects.load(out.value, name, alg.crs,
    #                                  RenderingStyles.getStyle(alg.id(), out.name),
    #                                  isRaster)
    #         except Exception:
    #             QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL)
    #             wrongLayers.append(out.description)
    #     elif isinstance(out, OutputHTML):
    #         resultsList.addResult(alg.icon(), out.description, out.value)
        i += 1

    QApplication.restoreOverrideCursor()
    if wrongLayers:
        msg = "The following layers were not correctly generated.<ul>"
        msg += "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
        msg += "You can check the log messages to find more information about the execution of the algorithm"
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Ejemplo n.º 24
0
    def mergeDataSources2Vrt(self, dataSources, outFile, union=False, relative=False,
                             schema=False, feedback=None):
        '''Function to do the work of merging datasources in a single vrt format

        @param data_sources: Array of path strings
        @param outfile: the output vrt file to generate
        @param relative: Write relative flag. DOES NOT relativise paths. They have to be already relative
        @param schema: Schema flag
        @return: vrt in string format
        '''
        if feedback is None:
            feedback = QgsProcessingFeedback()

        vrt = '<OGRVRTDataSource>'
        if union:
            vrt += '<OGRVRTUnionLayer name="UnionedLayer">'

        total = 100.0 / len(dataSources) if dataSources else 1
        for current, layer in enumerate(dataSources):
            if feedback.isCanceled():
                break

            feedback.setProgress(int(current * total))

            inFile = layer.source()
            srcDS = ogr.Open(inFile, 0)
            if srcDS is None:
                raise QgsProcessingException(
                    self.tr('Invalid datasource: {}'.format(inFile)))

            if schema:
                inFile = '@dummy@'

            for layer in srcDS:
                layerDef = layer.GetLayerDefn()
                layerName = layerDef.GetName()

                vrt += '<OGRVRTLayer name="{}">'.format(self.XmlEsc(layerName))
                vrt += '<SrcDataSource relativeToVRT="{}" shared="{}">{}</SrcDataSource>'.format(1 if relative else 0, not schema, self.XmlEsc(inFile))
                if schema:
                    vrt += '<SrcLayer>@dummy@</SrcLayer>'
                else:
                    vrt += '<SrcLayer>{}</SrcLayer>'.format(self.XmlEsc(layerName))

                vrt += '<GeometryType>{}</GeometryType>'.format(self.GeomType2Name(layerDef.GetGeomType()))

                crs = layer.GetSpatialRef()
                if crs is not None:
                    vrt += '<LayerSRS>{}</LayerSRS>'.format(self.XmlEsc(crs.ExportToWkt()))

                # Process all the fields.
                for fieldIdx in range(layerDef.GetFieldCount()):
                    fieldDef = layerDef.GetFieldDefn(fieldIdx)
                    vrt += '<Field name="{}" type="{}"'.format(self.XmlEsc(fieldDef.GetName()), self.fieldType2Name(fieldDef.GetType()))
                    if not schema:
                        vrt += ' src="{}"'.format(self.XmlEsc(fieldDef.GetName()))
                    if fieldDef.GetWidth() > 0:
                        vrt += ' width="{}"'.format(fieldDef.GetWidth())
                    if fieldDef.GetPrecision() > 0:
                        vrt += ' precision="{}"'.format(fieldDef.GetPrecision())
                    vrt += '/>'

                vrt += '</OGRVRTLayer>'

            srcDS.Destroy()

        if union:
            vrt += '</OGRVRTUnionLayer>'

        vrt += '</OGRVRTDataSource>'

        #TODO: pretty-print XML

        if outFile is not None:
            with codecs.open(outFile, 'w') as f:
                f.write(vrt)

        return vrt
Ejemplo n.º 25
0
def handleAlgorithmResults(alg,
                           context,
                           feedback=None,
                           showResults=True,
                           parameters={}):
    wrongLayers = []
    if feedback is None:
        feedback = QgsProcessingFeedback()
    feedback.setProgressText(
        QCoreApplication.translate('Postprocessing',
                                   'Loading resulting layers'))
    i = 0

    for l, details in context.layersToLoadOnCompletion().items():
        if feedback.isCanceled():
            return False

        if len(context.layersToLoadOnCompletion()) > 2:
            # only show progress feedback if we're loading a bunch of layers
            feedback.setProgress(
                100 * i / float(len(context.layersToLoadOnCompletion())))
        try:
            layer = QgsProcessingUtils.mapLayerFromString(
                l, context, typeHint=details.layerTypeHint)
            if layer is not None:
                set_layer_name(layer, details)
                '''If running a model, the execution will arrive here when an algorithm that is part of
                that model is executed. We check if its output is a final otuput of the model, and
                adapt the output name accordingly'''
                outputName = details.outputName
                expcontext = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                expcontext.appendScope(scope)
                for out in alg.outputDefinitions():
                    if out.name() not in parameters:
                        continue
                    outValue = parameters[out.name()]
                    if hasattr(outValue, "sink"):
                        outValue = outValue.sink.valueAsString(expcontext)[0]
                    else:
                        outValue = str(outValue)
                    if outValue == l:
                        outputName = out.name()
                        break
                style = None
                if outputName:
                    style = RenderingStyles.getStyle(alg.id(), outputName)
                if style is None:
                    if layer.type() == QgsMapLayerType.RasterLayer:
                        style = ProcessingConfig.getSetting(
                            ProcessingConfig.RASTER_STYLE)
                    else:
                        if layer.geometryType() == QgsWkbTypes.PointGeometry:
                            style = ProcessingConfig.getSetting(
                                ProcessingConfig.VECTOR_POINT_STYLE)
                        elif layer.geometryType() == QgsWkbTypes.LineGeometry:
                            style = ProcessingConfig.getSetting(
                                ProcessingConfig.VECTOR_LINE_STYLE)
                        else:
                            style = ProcessingConfig.getSetting(
                                ProcessingConfig.VECTOR_POLYGON_STYLE)
                if style:
                    layer.loadNamedStyle(style)

                details.project.addMapLayer(
                    context.temporaryLayerStore().takeMapLayer(layer))

                if details.postProcessor():
                    details.postProcessor().postProcessLayer(
                        layer, context, feedback)

            else:
                wrongLayers.append(str(l))
        except Exception:
            QgsMessageLog.logMessage(
                QCoreApplication.translate(
                    'Postprocessing', "Error loading result layer:") + "\n" +
                traceback.format_exc(), 'Processing', Qgis.Critical)
            wrongLayers.append(str(l))
        i += 1

    feedback.setProgress(100)

    if wrongLayers:
        msg = QCoreApplication.translate(
            'Postprocessing',
            "The following layers were not correctly generated.")
        msg += "<ul>" + "".join(["<li>%s</li>" % lay
                                 for lay in wrongLayers]) + "</ul>"
        msg += QCoreApplication.translate(
            'Postprocessing',
            "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm."
        )
        feedback.reportError(msg)

    return len(wrongLayers) == 0
Ejemplo n.º 26
0
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False):
    """Executes an algorithm modifying features in-place in the input layer.

    :param alg: algorithm to run
    :type alg: QgsProcessingAlgorithm
    :param parameters: parameters of the algorithm
    :type parameters: dict
    :param context: context, defaults to None
    :type context: QgsProcessingContext, optional
    :param feedback: feedback, defaults to None
    :type feedback: QgsProcessingFeedback, optional
    :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback
    :type raise_exceptions: boo, default to False
    :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable
    :return: a tuple with true if success and results
    :rtype: tuple
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext(feedback)

    # Only feature based algs have sourceFlags
    try:
        if alg.sourceFlags() & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks:
            context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)
    except AttributeError:
        pass

    active_layer = parameters['INPUT']

    # Run some checks and prepare the layer for in-place execution by:
    # - getting the active layer and checking that it is a vector
    # - making the layer editable if it was not already
    # - selecting all features if none was selected
    # - checking in-place support for the active layer/alg/parameters
    # If one of the check fails and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        if active_layer is None:
            raise QgsProcessingException(tr("There is not active layer."))

        if not isinstance(active_layer, QgsVectorLayer):
            raise QgsProcessingException(tr("Active layer is not a vector layer."))

        if not active_layer.isEditable():
            if not active_layer.startEditing():
                raise QgsProcessingException(tr("Active layer is not editable (and editing could not be turned on)."))

        if not alg.supportInPlaceEdit(active_layer):
            raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
    except QgsProcessingException as e:
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)
        return False, {}

    if not active_layer.selectedFeatureIds():
        active_layer.selectAll()

    # Make sure we are working on selected features only
    parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(active_layer.id(), True)
    parameters['OUTPUT'] = 'memory:'

    req = QgsFeatureRequest(QgsExpression(r"$id < 0"))
    req.setFlags(QgsFeatureRequest.NoGeometry)
    req.setSubsetOfAttributes([])

    # Start the execution
    # If anything goes wrong and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        new_feature_ids = []

        active_layer.beginEditCommand(alg.displayName())

        # Checks whether the algorithm has a processFeature method
        if hasattr(alg, 'processFeature'):  # in-place feature editing
            # Make a clone or it will crash the second time the dialog
            # is opened and run
            alg = alg.create()
            if not alg.prepare(parameters, context, feedback):
                raise QgsProcessingException(tr("Could not prepare selected algorithm."))
            # Check again for compatibility after prepare
            if not alg.supportInPlaceEdit(active_layer):
                raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
            field_idxs = range(len(active_layer.fields()))
            iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds())
            iterator_req.setInvalidGeometryCheck(context.invalidGeometryCheck())
            feature_iterator = active_layer.getFeatures(iterator_req)
            step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1
            for current, f in enumerate(feature_iterator):
                feedback.setProgress(current * step)
                if feedback.isCanceled():
                    break

                # need a deep copy, because python processFeature implementations may return
                # a shallow copy from processFeature
                input_feature = QgsFeature(f)
                new_features = alg.processFeature(input_feature, context, feedback)
                new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer)

                if len(new_features) == 0:
                    active_layer.deleteFeature(f.id())
                elif len(new_features) == 1:
                    new_f = new_features[0]
                    if not f.geometry().equals(new_f.geometry()):
                        active_layer.changeGeometry(f.id(), new_f.geometry())
                    if f.attributes() != new_f.attributes():
                        active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes())))
                    new_feature_ids.append(f.id())
                else:
                    active_layer.deleteFeature(f.id())
                    # Get the new ids
                    old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    if not active_layer.addFeatures(new_features):
                        raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                    new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    new_feature_ids += list(new_ids - old_ids)

            results, ok = {}, True

        else:  # Traditional 'run' with delete and add features cycle

            # There is no way to know if some features have been skipped
            # due to invalid geometries
            if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid:
                selected_ids = active_layer.selectedFeatureIds()
            else:
                selected_ids = []

            results, ok = alg.run(parameters, context, feedback)

            if ok:
                result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context)
                # TODO: check if features have changed before delete/add cycle

                new_features = []

                # Check if there are any skipped features
                if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid:
                    missing_ids = list(set(selected_ids) - set(result_layer.allFeatureIds()))
                    if missing_ids:
                        for f in active_layer.getFeatures(QgsFeatureRequest(missing_ids)):
                            if not f.geometry().isGeosValid():
                                new_features.append(f)

                active_layer.deleteFeatures(active_layer.selectedFeatureIds())

                for f in result_layer.getFeatures():
                    new_features.extend(QgsVectorLayerUtils.
                                        makeFeaturesCompatible([f], active_layer))

                # Get the new ids
                old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                if not active_layer.addFeatures(new_features):
                    raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                new_feature_ids += list(new_ids - old_ids)

        active_layer.endEditCommand()

        if ok and new_feature_ids:
            active_layer.selectByIds(new_feature_ids)
        elif not ok:
            active_layer.rollBack()

        return ok, results

    except QgsProcessingException as e:
        active_layer.endEditCommand()
        active_layer.rollBack()
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)

    return False, {}
Ejemplo n.º 27
0
class AddCurrentStationsLayerAlgorithm(QgsProcessingAlgorithm):
    PrmCurrentStationsLayer = 'CurrentStationsLayer'
    PrmCurrentPredictionsLayer = 'CurrentPredictionsLayer'

    # boilerplate methods
    def name(self):
        return 'addcurrentstationslayer'

    def displayName(self):
        return tr('Add Current Stations Layer')

    def helpUrl(self):
        return ''

    def createInstance(self):
        return AddCurrentStationsLayerAlgorithm()

    # Set up this algorithm
    def initAlgorithm(self, config):
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.PrmCurrentStationsLayer, tr('Current stations layer'),
                QgsProcessing.TypeVectorPoint,
                os.path.join(layerStoragePath(), 'current_stations.gpkg')))
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.PrmCurrentPredictionsLayer,
                tr('Current predictions layer'), QgsProcessing.TypeVectorPoint,
                os.path.join(layerStoragePath(), 'current_predictions.gpkg')))
        self.feedback = QgsProcessingFeedback()

    def processAlgorithm(self, parameters, context, feedback):
        self.context = context
        self.feedback = feedback
        self.parameters = parameters

        try:
            os.mkdir(layerStoragePath())
        except FileExistsError:
            pass

        current_dest_id = self.getCurrentStations()
        predictions_dest_id = self.getCurrentPredictions()

        return {
            self.PrmCurrentStationsLayer: current_dest_id,
            self.PrmCurrentPredictionsLayer: predictions_dest_id
        }

    def baseStationFields(self):
        fields = QgsFields()
        fields.append(QgsField("station", QVariant.String, '', 16))
        fields.append(QgsField("id", QVariant.String, '', 12))
        fields.append(QgsField("name", QVariant.String))
        fields.append(QgsField("type", QVariant.String, '', 1))
        fields.append(QgsField("timeZoneId", QVariant.String, '', 32))
        fields.append(QgsField("timeZoneUTC", QVariant.String, '', 32))
        fields.append(QgsField("refStation", QVariant.String, '', 12))
        return fields

    def basePredictionFields(self):
        fields = QgsFields()
        fields.append(QgsField("station", QVariant.String, '', 16))
        fields.append(QgsField("depth", QVariant.Double))
        fields.append(QgsField("time", QVariant.DateTime))
        fields.append(QgsField("value", QVariant.Double)
                      )  # signed value, on flood/ebb dimension for current
        fields.append(QgsField("type", QVariant.String))
        return fields

    def currentPredictionFields(self):
        fields = self.basePredictionFields()
        fields.append(QgsField("dir", QVariant.Double))
        fields.append(QgsField(
            "magnitude", QVariant.Double))  # value along direction if known
        return fields

    def getCurrentPredictions(self):
        if currentPredictionsLayer() != None:
            raise QgsProcessingException(
                tr('Existing current layers must be removed before creating new ones.'
                   ))

        (predictionsSink, predictions_dest_id) = self.parameterAsSink(
            self.parameters, self.PrmCurrentPredictionsLayer, self.context,
            self.currentPredictionFields(), QgsWkbTypes.Point, epsg4326)

        if self.context.willLoadLayerOnCompletion(predictions_dest_id):
            proc = CurrentPredictionsStylePostProcessor.create(
                tr('Current Predictions'), 'current_predictions.qml',
                CurrentPredictionsLayerType)
            self.context.layerToLoadOnCompletionDetails(
                predictions_dest_id).setPostProcessor(proc)

        return predictions_dest_id

    def getCurrentStations(self):
        if currentStationsLayer() != None:
            raise QgsProcessingException(
                tr('Existing current layers must be removed before creating new ones.'
                   ))

        self.feedback.pushInfo(
            "Requesting metadata for NOAA current stations...")
        url = 'https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations.xml?type=currentpredictions&expand=currentpredictionoffsets'
        r = requests.get(url, timeout=30)
        if r.status_code != 200:
            raise QgsProcessingException(
                tr('Request failed with status {}').format(r.status_code))

        content = r.text
        if len(content) == 0:
            return

        self.feedback.pushInfo('Read {} bytes'.format(len(content)))

        # obtain our current stations output sink
        fields = self.baseStationFields()
        fields.append(QgsField("bin", QVariant.String, '', 4))
        fields.append(QgsField("depth", QVariant.Double))
        fields.append(QgsField("depthType", QVariant.String, '', 1))
        fields.append(QgsField("surface", QVariant.Int))
        fields.append(QgsField("meanFloodDir", QVariant.Double))
        fields.append(QgsField("meanEbbDir", QVariant.Double))
        fields.append(QgsField("mfcTimeAdjMin", QVariant.Double))
        fields.append(QgsField("sbeTimeAdjMin", QVariant.Double))
        fields.append(QgsField("mecTimeAdjMin", QVariant.Double))
        fields.append(QgsField("sbfTimeAdjMin", QVariant.Double))
        fields.append(QgsField("mfcAmpAdj", QVariant.Double))
        fields.append(QgsField("mecAmpAdj", QVariant.Double))

        (currentSink, current_dest_id) = self.parameterAsSink(
            self.parameters, self.PrmCurrentStationsLayer, self.context,
            fields, QgsWkbTypes.Point, epsg4326)

        # This script converts a stations XML result obtained from this URL:
        # https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations.xml?type=currentpredictions&expand=currentpredictionoffsets
        # by including only the bin of minimum depth for each station and eliminating weak/variable stations.
        # The flood and ebb directions are captured from the metadata also.
        self.feedback.pushInfo("Parsing metadata...")

        # Parse the XML file into a DOM up front
        xmlparse = ET.fromstring(content)

        # Get the list of stations
        root = xmlparse
        stations = root.findall('Station')

        # Get a map that will track the metadata for the least-depth bin for each station.
        # Also maintain a map by station key.
        surfaceMap = {}
        stationMap = {}

        # Loop over all stations and index them. If filtering for shallowest at each location,
        # maintain the minimum depth station for each id in surfaceMap.
        for s in stations:
            stationId = s.find('id').text
            stationBin = s.find('currbin').text

            # Skip weak/variable type stations
            if s.find('type').text == 'W':
                continue

            stationMap[stationId + '_' + stationBin] = s

            # If we find that we already saw this station, check its depth and only update the station map
            # if the newly found bin is shallower
            lastStation = surfaceMap.get(stationId)
            if lastStation and lastStation.find('depth').text and s.find(
                    'depth').text:
                lastDepth = float(lastStation.find('depth').text)
                newDepth = float(s.find('depth').text)
                if newDepth >= lastDepth:
                    continue

            surfaceMap[stationId] = s

        if self.context.willLoadLayerOnCompletion(current_dest_id):
            proc = CurrentStationsStylePostProcessor.create(
                tr('Current Stations'), 'current_stations.qml',
                CurrentStationsLayerType)
            self.context.layerToLoadOnCompletionDetails(
                current_dest_id).setPostProcessor(proc)

        # Now build the features for all stations in the map.

        progress_count = 0

        tzl = TimeZoneLookup()

        for key, s in stationMap.items():
            cpo = s.find('currentpredictionoffsets')

            f = QgsFeature(fields)

            lng = float(s.find('lng').text)
            lat = float(s.find('lat').text)
            geom = QgsGeometry(QgsPoint(lng, lat))
            f.setGeometry(geom)

            f['station'] = key
            f['id'] = s.find('id').text
            f['bin'] = s.find('currbin').text
            f['name'] = s.find('name').text
            f['type'] = s.find('type').text
            f['depth'] = parseFloatNullable(s.find('depth').text)
            f['depthType'] = s.find('depthType').text

            f['surface'] = 1 if surfaceMap.get(f['id']) == s else 0

            refStationId = cpo.find('refStationId').text
            if refStationId:
                f['refStation'] = refStationId + '_' + cpo.find(
                    'refStationBin').text

            (f['timeZoneId'],
             f['timeZoneUTC']) = tzl.getZoneByCoordinates(lat, lng)

            f['meanFloodDir'] = parseFloatNullable(
                cpo.find('meanFloodDir').text)
            f['meanEbbDir'] = parseFloatNullable(cpo.find('meanEbbDir').text)
            f['mfcTimeAdjMin'] = parseFloatNullable(
                cpo.find('mfcTimeAdjMin').text)
            f['sbeTimeAdjMin'] = parseFloatNullable(
                cpo.find('sbeTimeAdjMin').text)
            f['mecTimeAdjMin'] = parseFloatNullable(
                cpo.find('mecTimeAdjMin').text)
            f['sbfTimeAdjMin'] = parseFloatNullable(
                cpo.find('sbfTimeAdjMin').text)
            f['mfcAmpAdj'] = parseFloatNullable(cpo.find('mfcAmpAdj').text)
            f['mecAmpAdj'] = parseFloatNullable(cpo.find('mecAmpAdj').text)

            currentSink.addFeature(f)

            progress_count += 1
            self.feedback.setProgress(100 * progress_count / len(stationMap))

        return current_dest_id
Ejemplo n.º 28
0
    def mergeDataSources2Vrt(self,
                             dataSources,
                             outFile,
                             union=False,
                             relative=False,
                             schema=False,
                             feedback=None):
        '''Function to do the work of merging datasources in a single vrt format

        @param data_sources: Array of path strings
        @param outfile: the output vrt file to generate
        @param relative: Write relative flag. DOES NOT relativise paths. They have to be already relative
        @param schema: Schema flag
        @return: vrt in string format
        '''
        if feedback is None:
            feedback = QgsProcessingFeedback()

        vrt = '<OGRVRTDataSource>'
        if union:
            vrt += '<OGRVRTUnionLayer name="UnionedLayer">'

        total = 100.0 / len(dataSources) if dataSources else 1
        for current, layer in enumerate(dataSources):
            if feedback.isCanceled():
                break

            feedback.setProgress(int(current * total))

            inFile = layer.source()
            srcDS = ogr.Open(inFile, 0)
            if srcDS is None:
                raise QgsProcessingException(
                    self.tr('Invalid datasource: {}'.format(inFile)))

            if schema:
                inFile = '@dummy@'

            for layer in srcDS:
                layerDef = layer.GetLayerDefn()
                layerName = layerDef.GetName()

                vrt += '<OGRVRTLayer name="{}">'.format(self.XmlEsc(layerName))
                vrt += '<SrcDataSource relativeToVRT="{}" shared="{}">{}</SrcDataSource>'.format(
                    1 if relative else 0, not schema, self.XmlEsc(inFile))
                if schema:
                    vrt += '<SrcLayer>@dummy@</SrcLayer>'
                else:
                    vrt += '<SrcLayer>{}</SrcLayer>'.format(
                        self.XmlEsc(layerName))

                vrt += '<GeometryType>{}</GeometryType>'.format(
                    self.GeomType2Name(layerDef.GetGeomType()))

                crs = layer.GetSpatialRef()
                if crs is not None:
                    vrt += '<LayerSRS>{}</LayerSRS>'.format(
                        self.XmlEsc(crs.ExportToWkt()))

                # Process all the fields.
                for fieldIdx in range(layerDef.GetFieldCount()):
                    fieldDef = layerDef.GetFieldDefn(fieldIdx)
                    vrt += '<Field name="{}" type="{}"'.format(
                        self.XmlEsc(fieldDef.GetName()),
                        self.fieldType2Name(fieldDef.GetType()))
                    if not schema:
                        vrt += ' src="{}"'.format(
                            self.XmlEsc(fieldDef.GetName()))
                    if fieldDef.GetWidth() > 0:
                        vrt += ' width="{}"'.format(fieldDef.GetWidth())
                    if fieldDef.GetPrecision() > 0:
                        vrt += ' precision="{}"'.format(
                            fieldDef.GetPrecision())
                    vrt += '/>'

                vrt += '</OGRVRTLayer>'

            srcDS.Destroy()

        if union:
            vrt += '</OGRVRTUnionLayer>'

        vrt += '</OGRVRTDataSource>'

        #TODO: pretty-print XML

        if outFile is not None:
            with codecs.open(outFile, 'w') as f:
                f.write(vrt)

        return vrt
Ejemplo n.º 29
0
    def processAlgorithm(self, parameters, context: QgsProcessingContext,
                         feedback: QgsProcessingFeedback):
        """Here is where the processing itself takes place."""

        feedback.setProgress(0)

        # init params
        reach_layer = self.parameterAsVectorLayer(parameters, self.REACH_LAYER,
                                                  context)
        wastewater_node_layer = self.parameterAsVectorLayer(
            parameters, self.WASTEWATER_NODE_LAYER, context)
        value_expression = self.parameterAsExpression(parameters,
                                                      self.VALUE_EXPRESSION,
                                                      context)
        reach_pk_name = self.parameterAsFields(parameters, self.REACH_PK_NAME,
                                               context)[0]
        node_pk_name = self.parameterAsFields(parameters, self.NODE_PK_NAME,
                                              context)[0]
        node_from_fk_name = self.parameterAsFields(parameters,
                                                   self.NODE_FROM_FK_NAME,
                                                   context)[0]
        node_to_fk_name = self.parameterAsFields(parameters,
                                                 self.NODE_TO_FK_NAME,
                                                 context)[0]
        branch_behavior = self.parameterAsEnum(parameters,
                                               self.BRANCH_BEHAVIOR, context)
        create_loop_layer = self.parameterAsBool(parameters,
                                                 self.CREATE_LOOP_LAYER,
                                                 context)

        if branch_behavior == 0:
            aggregate_method = lambda values: min(values) if values else 0
        elif branch_behavior == 1:
            aggregate_method = lambda values: max(values) if values else 0
        elif branch_behavior == 2:
            aggregate_method = lambda values: statistics.mean(
                values) if values else 0
        else:
            aggregate_method = lambda values: feedback.pushError(
                'Aggregate method not implemented')

        # create feature sink
        fields = wastewater_node_layer.fields()
        fields.append(QgsField('value', QVariant.Double))
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields,
                                               QgsWkbTypes.Point,
                                               reach_layer.sourceCrs())

        loop_sink = None
        loop_dest_id = None
        if create_loop_layer:
            (loop_sink,
             loop_dest_id) = self.parameterAsSink(parameters, self.LOOP_OUTPUT,
                                                  context, fields,
                                                  QgsWkbTypes.Point,
                                                  reach_layer.sourceCrs())

        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        feature_count = reach_layer.featureCount()

        reaches_by_from_node = dict()
        reaches_by_id = dict()

        expression = QgsExpression(value_expression)
        context = QgsExpressionContext(
            QgsExpressionContextUtils.globalProjectLayerScopes(reach_layer))
        expression.prepare(context)

        progress = 0
        feedback.setProgressText(self.tr('Indexing reaches'))
        for reach in reach_layer.getFeatures(QgsFeatureRequest()):
            if reach[node_from_fk_name] == NULL:
                continue

            context.setFeature(reach)
            value = expression.evaluate(context)
            reach_obj = Reach(reach[node_from_fk_name], reach[node_to_fk_name],
                              value, reach.geometry())
            reaches_by_from_node.setdefault(reach_obj.from_id,
                                            []).append(reach_obj)
            reaches_by_id[reach[reach_pk_name]] = reach_obj

            feedback.setProgress(progress / feature_count * 10)
            progress += 1

        loop_nodes = []
        current_feature = 0
        calculated_values = {}

        feedback.setProgressText(self.tr('Analyzing network'))
        for node in wastewater_node_layer.getFeatures():

            from_node_id = node[node_pk_name]

            processed_nodes = []
            times = []
            if from_node_id in reaches_by_from_node.keys():
                for reach in reaches_by_from_node[from_node_id]:
                    times.append(
                        self.calculate_branch(reach, reaches_by_from_node,
                                              reaches_by_id,
                                              list(processed_nodes),
                                              calculated_values,
                                              aggregate_method, loop_nodes,
                                              feedback))

            if times:
                time = aggregate_method(times)
            else:
                time = 0

            current_feature += 1

            calculated_values[node[node_pk_name]] = time
            new_node = QgsFeature(node)
            new_node.setFields(fields)
            new_node.setAttributes(node.attributes() + [time])

            sink.addFeature(new_node, QgsFeatureSink.FastInsert)

            if create_loop_layer and from_node_id in loop_nodes:
                loop_sink.addFeature(node, QgsFeatureSink.FastInsert)

            feedback.setProgress(10 + current_feature / feature_count * 90)

        result = {self.OUTPUT: dest_id}
        if create_loop_layer:
            result[self.LOOP_OUTPUT] = loop_dest_id

        return result