def _get_route_dict(source, source_field, sort_start, destination,
                        destination_field, sort_end):
        """
        Compute route_dict from input layer.

        :param source: Input from layer
        :type source: QgsProcessingParameterFeatureSource

        :param source_field: ID field from layer.
        :type source_field: QgsField

        :param destination: Input to layer.
        :type destination: QgsProcessingParameterFeatureSource

        :param destination_field: ID field to layer.
        :type destination_field: QgsField

        :returns: route_dict with coordinates and ID values
        :rtype: dict
        """
        route_dict = dict()
        source_feats = sorted(list(source.getFeatures()), key=sort_start)
        x_former_source = transform.transformToWGS(source.sourceCrs())
        route_dict['start'] = dict(
            geometries=[
                x_former_source.transform(feat.geometry().asPoint())
                for feat in source_feats
            ],
            values=[
                feat.attribute(source_field.name())
                if source_field else feat.id() for feat in source_feats
            ],
        )

        destination_feats = sorted(list(destination.getFeatures()),
                                   key=sort_end)
        x_former_destination = transform.transformToWGS(
            destination.sourceCrs())
        route_dict['end'] = dict(
            geometries=[
                x_former_destination.transform(feat.geometry().asPoint())
                for feat in destination_feats
            ],
            values=[
                feat.attribute(destination_field.name())
                if destination_field else feat.id()
                for feat in destination_feats
            ],
        )

        return route_dict
Пример #2
0
    def _get_sorted_lines(layer, field_name):
        """
        Generator to yield geometry and ID value sorted by feature ID. Careful: feat.id() is not necessarily
        permanent

        :param layer: source input layer
        :type layer: QgsProcessingParameterFeatureSource

        :param field_name: name of ID field
        :type field_name: str
        """
        # First get coordinate transformer
        xformer = transform.transformToWGS(layer.sourceCrs())

        for feat in sorted(layer.getFeatures(), key=lambda f: f.id()):
            line = None
            field_value = feat[field_name]
            # for
            if layer.wkbType() == QgsWkbTypes.MultiLineString:
                # TODO: only takes the first polyline geometry from the multiline geometry currently
                # Loop over all polyline geometries
                line = [
                    xformer.transform(QgsPointXY(point))
                    for point in feat.geometry().asMultiPolyline()[0]
                ]

            elif layer.wkbType() == QgsWkbTypes.LineString:
                line = [
                    xformer.transform(QgsPointXY(point))
                    for point in feat.geometry().asPolyline()
                ]

            yield line, field_value
def _get_avoid_polygons(layer):
    """
    Extract polygon geometries from the selected polygon layer.

    :param layer: The polygon layer
    :type layer: QgsMapLayer
    :returns: GeoJSON object
    :rtype: dict
    """
    polygons = None
    transformer = transform.transformToWGS(layer.sourceCrs())
    features = layer.getFeatures()

    # iterate over all other features
    for feature in features:
        if feature.hasGeometry():
            geom = feature.geometry()
            geom.transform(transformer)
            if polygons is None:
                polygons = geom
            else:
                polygons = polygons.combine(geom)

    if not polygons:
        return json.loads("{}")
    else:
        return json.loads(polygons.asJson())
Пример #4
0
    def get_parameters(self):
        """
        Builds parameters across directions functionalities.

        :returns: All parameter mappings except for coordinates.
        :rtype: dict
        """

        if self.dlg.optimization_group.isChecked():
            return self._get_optimize_parameters()

        # API parameters
        route_mode = self.dlg.routing_travel_combo.currentText()
        route_pref = self.dlg.routing_preference_combo.currentText()

        params = {
            'preference': route_pref,
            'geometry': 'true',
            'instructions': 'false',
            'elevation': True,
            'id': 1,
        }

        # Get Advanced parameters
        if self.dlg.routing_avoid_tags_group.isChecked():
            avoid_boxes = self.dlg.routing_avoid_tags_group.findChildren(
                QCheckBox)
            if any(box.isChecked() for box in avoid_boxes):
                self.options['avoid_features'] = self._get_avoid_options(
                    avoid_boxes)

        if self.dlg.routing_avoid_countries_group.isChecked():
            countries_text = self.dlg.countries_text.value()
            if countries_text:
                countries = countries_text.split(',')
                if all(map(lambda x: x.isdigit(), countries)):
                    countries = [int(x) for x in countries]
                self.options['avoid_countries'] = countries

        if self.dlg.avoidpolygon_group.isChecked():
            layer = self.dlg.avoidpolygon_dropdown.currentLayer()
            if layer:
                transformer = transform.transformToWGS(layer.sourceCrs())
                # FIXME: bug in GPKG driver I think:
                # https://github.com/GIScience/orstools-qgis-plugin/issues/114
                # Wrote to dev mailing list
                geom = layer.getGeometry(0) or layer.getGeometry(1)
                geom.transform(transformer)
                self.options['avoid_polygons'] = json.loads(geom.asJson())

        if self.options:
            params['options'] = self.options

        return params
    def _on_linetool_map_click(self, point, idx):
        """Adds an item to QgsListWidget and annotates the point in the map canvas"""

        transformer = transform.transformToWGS(self.map_crs)
        point_wgs = transformer.transform(point)
        self.routing_fromline_list.addItem(
            f"Point {idx}: {point_wgs.x():.6f}, {point_wgs.y():.6f}")

        annotation = self._linetool_annotate_point(point, idx)
        self.annotations.append(annotation)
        self.project.annotationManager().addAnnotation(annotation)
Пример #6
0
    def canvasReleaseEvent(self, event):
        #Get the click and emit a transformed point

        # mapSettings() was only introduced in QGIS 2.4, keep compatibility
        crsSrc = self.canvas.mapSettings().destinationCrs()

        point_oldcrs = event.mapPoint()

        xform = transform.transformToWGS(crsSrc)
        point_newcrs = xform.transform(point_oldcrs)

        QApplication.restoreOverrideCursor()

        self.canvasClicked.emit(point_newcrs, self.button)
    def get_sorted_feature_parameters(self, layer):
        """
        Generator to yield geometry and id of features sorted by feature ID. Careful: feat.id() is not necessarily
        permanent

        :param layer: source input layer.
        :type layer: QgsProcessingParameterFeatureSource
        """
        # First get coordinate transformer
        xformer = transform.transformToWGS(layer.sourceCrs())

        for feat in sorted(layer.getFeatures(), key=lambda f: f.id()):
            x_point = xformer.transform(feat.geometry().asPoint())

            yield (convert.build_coords([x_point.x(), x_point.y()]), feat)
Пример #8
0
    def get_sorted_feature_parameters(layer: QgsProcessingParameterFeatureSource, id_field_name: str):
        """
        Generator to yield geometry and id of features sorted by feature ID. Careful: feat.id() is not necessarily
        permanent

        :param layer: source input layer.
        :param id_field_name: layer field containing id values
        """
        # First get coordinate transformer
        x_former = transform.transformToWGS(layer.sourceCrs())

        for feat in sorted(layer.getFeatures(), key=lambda f: f.id()):
            x_point = x_former.transform(feat.geometry().asPoint())
            id_value = feat[id_field_name] if id_field_name else None

            yield [[round(x_point.x(), 6), round(x_point.y(), 6)]], id_value
Пример #9
0
    def __init__(self, canvas):
        """
        :param canvas: current map canvas
        :type canvas: QgsMapCanvas
        """
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rubberBand = QgsRubberBand(self.canvas, False)
        self.rubberBand.setStrokeColor(QColor(DEFAULT_COLOR))
        self.rubberBand.setWidth(3)

        crsSrc = self.canvas.mapSettings().destinationCrs()
        self.transformer = transform.transformToWGS(crsSrc)
        self.previous_point = None
        self.points = []
        self.markers = []
        self.reset()
Пример #10
0
    def processAlgorithm(self, parameters, context, feedback):

        # Init ORS client
        providers = configmanager.read_config()['providers']
        provider = providers[self.parameterAsEnum(parameters, self.IN_PROVIDER, context)]
        clnt = client.Client(provider)
        clnt.overQueryLimit.connect(lambda: feedback.reportError("OverQueryLimit: Retrying"))

        params = dict()

        # Get profile value
        profile = PROFILES[self.parameterAsEnum(
            parameters,
            self.IN_PROFILE,
            context
        )]

        # Get parameter values
        source = self.parameterAsSource(
            parameters,
            self.IN_START,
            context
        )
        source_field_name = self.parameterAsString(
            parameters,
            self.IN_START_FIELD,
            context
        )
        destination = self.parameterAsSource(
            parameters,
            self.IN_END,
            context
        )
        destination_field_name = self.parameterAsString(
            parameters,
            self.IN_END_FIELD,
            context
        )

        # Get fields from field name
        source_field_id = source.fields().lookupField(source_field_name)
        source_field = source.fields().field(source_field_id)

        destination_field_id = destination.fields().lookupField(destination_field_name)
        destination_field = destination.fields().field(destination_field_id)

        # Abort when MultiPoint type
        if (source.wkbType() or destination.wkbType()) == 4:
            raise QgsProcessingException("TypeError: Multipoint Layers are not accepted. Please convert to single geometry layer.")

        # Get source and destination features
        sources_features = list(source.getFeatures())
        destination_features = list(destination.getFeatures())
        # Get feature amounts/counts
        sources_amount = source.featureCount()
        destinations_amount = destination.featureCount()

        # Allow for 50 features in source if source == destination
        source_equals_destination = parameters['INPUT_START_LAYER'] == parameters['INPUT_END_LAYER']
        if source_equals_destination:
            features = sources_features
            xformer = transform.transformToWGS(source.sourceCrs())
            features_points = [xformer.transform(feat.geometry().asPoint()) for feat in features]
        else:
            xformer = transform.transformToWGS(source.sourceCrs())
            sources_features_xformed = [xformer.transform(feat.geometry().asPoint()) for feat in sources_features]

            xformer = transform.transformToWGS(destination.sourceCrs())
            destination_features_xformed = [xformer.transform(feat.geometry().asPoint()) for feat in destination_features]

            features_points = sources_features_xformed + destination_features_xformed

        # Get IDs
        sources_ids = list(range(sources_amount)) if source_equals_destination else list(range(sources_amount))
        destination_ids = list(range(sources_amount)) if source_equals_destination else list(range(sources_amount, sources_amount + destinations_amount))

        # Populate parameters further
        params.update({
            'locations': [[point.x(), point.y()] for point in features_points],
            'sources': sources_ids,
            'destinations': destination_ids,
            'metrics': ["duration", "distance"],
            'id': 'Matrix'
        })

        # Make request and catch ApiError
        try:
            response = clnt.request('/v2/matrix/' + profile, {}, post_json=params)

        except (exceptions.ApiError,
                exceptions.InvalidKey,
                exceptions.GenericServerError) as e:
            msg = "{}: {}".format(
                e.__class__.__name__,
                str(e))
            feedback.reportError(msg)
            logger.log(msg)

        (sink, dest_id) = self.parameterAsSink(
            parameters,
            self.OUT,
            context,
            self.get_fields(
                source_field.type(),
                destination_field.type()
            ),
            QgsWkbTypes.NoGeometry
        )

        sources_attributes = [feat.attribute(source_field_name) for feat in sources_features]
        destinations_attributes = [feat.attribute(destination_field_name) for feat in destination_features]

        for s, source in enumerate(sources_attributes):
            for d, destination in enumerate(destinations_attributes):
                duration = response['durations'][s][d]
                distance = response['distances'][s][d]
                feat = QgsFeature()
                feat.setAttributes([
                    source,
                    destination,
                    duration / 3600 if duration is not None else None,
                    distance / 1000 if distance is not None else None
                ])

                sink.addFeature(feat)

        return {self.OUT: dest_id}
Пример #11
0
    def processAlgorithm(self, parameters, context, feedback):
        ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)

        # Get profile value
        profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]

        # TODO: enable once core matrix is available
        # options = self.parseOptions(parameters, context)

        # Get parameter values
        source = self.parameterAsSource(
            parameters,
            self.IN_START,
            context
        )

        source_field_name = parameters[self.IN_START_FIELD]
        source_field = source.fields().field(source_field_name) if source_field_name else None

        destination = self.parameterAsSource(
            parameters,
            self.IN_END,
            context
        )
        destination_field_name = parameters[self.IN_END_FIELD]
        destination_field = destination.fields().field(destination_field_name) if destination_field_name else None

        # Abort when MultiPoint type
        if (QgsWkbTypes.flatType(source.wkbType()) or QgsWkbTypes.flatType(destination.wkbType()))\
                == QgsWkbTypes.MultiPoint:
            raise QgsProcessingException(
                "TypeError: Multipoint Layers are not accepted. Please convert to single geometry layer.")

        # Get source and destination features
        sources_features = list(source.getFeatures())
        destination_features = list(destination.getFeatures())
        # Get feature amounts/counts
        sources_amount = source.featureCount()
        destinations_amount = destination.featureCount()

        # Allow for 50 features in source if source == destination
        source_equals_destination = parameters['INPUT_START_LAYER'] == parameters['INPUT_END_LAYER']
        if source_equals_destination:
            features = sources_features
            x_former = transform.transformToWGS(source.sourceCrs())
            features_points = [x_former.transform(feat.geometry().asPoint()) for feat in features]
        else:
            x_former = transform.transformToWGS(source.sourceCrs())
            sources_features_x_formed = [x_former.transform(feat.geometry().asPoint()) for feat in sources_features]

            x_former = transform.transformToWGS(destination.sourceCrs())
            destination_features_x_formed = [x_former.transform(feat.geometry().asPoint()) for feat in
                                             destination_features]

            features_points = sources_features_x_formed + destination_features_x_formed

        # Get IDs
        sources_ids = list(range(sources_amount)) if source_equals_destination else list(range(sources_amount))
        destination_ids = list(range(sources_amount)) if source_equals_destination else list(
            range(sources_amount, sources_amount + destinations_amount))

        params = {
            'locations': [[point.x(), point.y()] for point in features_points],
            'sources': sources_ids,
            'destinations': destination_ids,
            'metrics': ["duration", "distance"],
            'id': 'Matrix'
            # 'options': options
        }

        # get types of set ID fields
        field_types = dict()
        if source_field:
            field_types.update({"source_type": source_field.type()})
        if destination_field:
            field_types.update({"destination_type": destination_field.type()})

        sink_fields = self.get_fields(**field_types)

        # Make request and catch ApiError
        try:
            response = ors_client.request('/v2/matrix/' + profile, {}, post_json=params)

        except (exceptions.ApiError,
                exceptions.InvalidKey,
                exceptions.GenericServerError) as e:
            msg = f"{e.__class__.__name__}: {str(e)}"
            feedback.reportError(msg)
            logger.log(msg)

        (sink, dest_id) = self.parameterAsSink(
            parameters,
            self.OUT,
            context,
            sink_fields,
            QgsWkbTypes.NoGeometry
        )

        sources_attributes = [feat.attribute(source_field_name) if source_field_name else feat.id() for feat in
                              sources_features]
        destinations_attributes = [feat.attribute(destination_field_name) if destination_field_name else feat.id() for
                                   feat in destination_features]

        for s, source in enumerate(sources_attributes):
            for d, destination in enumerate(destinations_attributes):
                duration = response['durations'][s][d]
                distance = response['distances'][s][d]
                feat = QgsFeature()
                feat.setAttributes([
                    source,
                    destination,
                    duration / 3600 if duration is not None else None,
                    distance / 1000 if distance is not None else None
                ])

                sink.addFeature(feat)

        return {self.OUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        # Init ORS client

        providers = configmanager.read_config()['providers']
        provider = providers[self.parameterAsEnum(parameters, self.IN_PROVIDER,
                                                  context)]
        clnt = client.Client(provider)
        clnt.overQueryLimit.connect(
            lambda: feedback.reportError("OverQueryLimit: Retrying..."))

        profile = PROFILES[self.parameterAsEnum(parameters, self.IN_PROFILE,
                                                context)]

        preference = PREFERENCES[self.parameterAsEnum(parameters,
                                                      self.IN_PREFERENCE,
                                                      context)]

        optimize = self.parameterAsBool(parameters, self.IN_OPTIMIZE, context)

        # Get parameter values
        source = self.parameterAsSource(parameters, self.IN_POINTS, context)

        source_field_idx = self.parameterAsEnum(parameters, self.IN_FIELD,
                                                context)

        source_field_name = self.parameterAsString(parameters, self.IN_FIELD,
                                                   context)

        (sink, dest_id) = self.parameterAsSink(
            parameters, self.OUT, context,
            directions_core.get_fields(
                from_type=source.fields().field(source_field_name).type(),
                from_name=source_field_name,
                line=True), QgsWkbTypes.LineString,
            QgsCoordinateReferenceSystem(4326))
        count = source.featureCount()

        input_points = list()
        from_values = list()
        xformer = transform.transformToWGS(source.sourceCrs())

        if source.wkbType() == QgsWkbTypes.Point:
            points = list()
            for feat in sorted(source.getFeatures(), key=lambda f: f.id()):
                points.append(
                    xformer.transform(QgsPointXY(feat.geometry().asPoint())))
            input_points.append(points)
            from_values.append('')
        elif source.wkbType() == QgsWkbTypes.MultiPoint:
            # loop through multipoint features
            for feat in sorted(source.getFeatures(), key=lambda f: f.id()):
                points = list()
                for point in feat.geometry().asMultiPoint():
                    points.append(xformer.transform(QgsPointXY(point)))
                input_points.append(points)
                from_values.append(feat[source_field_name])

        for num, (points,
                  from_value) in enumerate(zip(input_points, from_values)):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            try:
                if optimize:
                    params = self._get_params_optimize(points, profile)
                    response = clnt.request('/optimization', {},
                                            post_json=params)

                    sink.addFeature(
                        directions_core.get_output_features_optimization(
                            response, profile, from_value=from_value))
                else:
                    params = self._get_params_directions(
                        points, profile, preference)
                    response = clnt.request('/v2/directions/' + profile +
                                            '/geojson', {},
                                            post_json=params)

                    sink.addFeature(
                        directions_core.get_output_feature_directions(
                            response,
                            profile,
                            preference,
                            from_value=from_value))
            except (exceptions.ApiError, exceptions.InvalidKey,
                    exceptions.GenericServerError) as e:
                msg = "Feature ID {} caused a {}:\n{}".format(
                    from_value, e.__class__.__name__, str(e))
                feedback.reportError(msg)
                logger.log(msg)
                continue

            feedback.setProgress(int(100.0 / count * num))

        return {self.OUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        ors_client = self._get_ors_client_from_provider(
            parameters[self.IN_PROVIDER], feedback)

        profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]

        preference = dict(
            enumerate(PREFERENCES))[parameters[self.IN_PREFERENCE]]

        optimization_mode = parameters[self.IN_OPTIMIZE]

        options = self.parseOptions(parameters, context)

        # Get parameter values
        source = self.parameterAsSource(parameters, self.IN_POINTS, context)

        source_field_name = parameters[self.IN_FIELD]
        get_fields_options = dict()
        if source_field_name:
            get_fields_options.update(
                from_type=source.fields().field(source_field_name).type(),
                from_name=source_field_name)

        sink_fields = directions_core.get_fields(**get_fields_options,
                                                 line=True)

        (sink, dest_id) = self.parameterAsSink(
            parameters, self.OUT, context, sink_fields, QgsWkbTypes.LineString,
            QgsCoordinateReferenceSystem.fromEpsgId(4326))

        sort_by = parameters[self.IN_SORTBY]

        if sort_by:

            def sort(f):
                return f.attribute(sort_by)
        else:

            def sort(f):
                return f.id()

        count = source.featureCount()

        input_points = list()
        from_values = list()
        x_former = transform.transformToWGS(source.sourceCrs())

        if QgsWkbTypes.flatType(source.wkbType()) == QgsWkbTypes.Point:
            points = list()
            for feat in sorted(source.getFeatures(), key=sort):
                points.append(
                    x_former.transform(QgsPointXY(feat.geometry().asPoint())))
            input_points.append(points)
            from_values.append(None)
        elif QgsWkbTypes.flatType(source.wkbType()) == QgsWkbTypes.MultiPoint:
            # loop through multipoint features
            for feat in sorted(source.getFeatures(), key=sort):
                points = list()
                for point in feat.geometry().asMultiPoint():
                    points.append(x_former.transform(QgsPointXY(point)))
                input_points.append(points)
                from_values.append(
                    feat[source_field_name] if source_field_name else None)

        for num, (points,
                  from_value) in enumerate(zip(input_points, from_values)):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            try:
                if optimization_mode is not None:
                    params = get_params_optimize(points, profile,
                                                 optimization_mode)
                    response = ors_client.request('/optimization', {},
                                                  post_json=params)

                    sink.addFeature(
                        directions_core.get_output_features_optimization(
                            response, profile, from_value=from_value))
                else:
                    params = directions_core.build_default_parameters(
                        preference, point_list=points, options=options)
                    response = ors_client.request('/v2/directions/' + profile +
                                                  '/geojson', {},
                                                  post_json=params)

                    sink.addFeature(
                        directions_core.get_output_feature_directions(
                            response,
                            profile,
                            preference,
                            from_value=from_value))
            except (exceptions.ApiError, exceptions.InvalidKey,
                    exceptions.GenericServerError) as e:
                msg = f"Feature ID {from_value} caused a {e.__class__.__name__}:\n{str(e)}"
                feedback.reportError(msg)
                logger.log(msg)
                continue

            feedback.setProgress(int(100.0 / count * num))

        return {self.OUT: dest_id}