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_locations(avoid_layer):
    """
    Get the avoid locations parameter value.

    :param avoid_layer: The point layer to be avoided
    :type avoid_layer: QgsProcessingFeatureSource

    :returns: Valhalla formatted locations list
    :rtype: list of dict
    """

    locations = []
    xformer_avoid = transform.transformToWGS(avoid_layer.sourceCrs())
    if avoid_layer.wkbType() != QgsWkbTypes.MultiPoint:
        points = []
        for feat in avoid_layer.getFeatures():
            points.append(
                xformer_avoid.transform(QgsPointXY(feat.geometry().asPoint())))

        for point in points:
            locations.append({
                "lon": round(point.x(), 6),
                "lat": round(point.y(), 6)
            })

    return locations
Пример #3
0
    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._mapCanvas.mapSettings().destinationCrs())
        point_wgs = transformer.transform(point)
        self.routing_fromline_list.addItem(
            "Point {0}: {1:.6f}, {2:.6f}".format(idx, point_wgs.x(),
                                                 point_wgs.y()))

        annotation = self._linetool_annotate_point(point, idx)
        self.annotations.append(annotation)
        self.project.annotationManager().addAnnotation(annotation)
    def _get_route_dict(self, source, source_field, destination, destination_field):
        """
        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 = list(source.getFeatures())
        xformer_source = transform.transformToWGS(source.sourceCrs())
        route_dict['start'] = dict(
            geometries=[xformer_source.transform(feat.geometry().asPoint()) for feat in source_feats],
            values= [feat.attribute(source_field.name()) for feat in source_feats],
        )

        destination_feats = list(destination.getFeatures())
        xformer_destination = transform.transformToWGS(destination.sourceCrs())
        route_dict['end'] = dict(
            geometries=[xformer_destination.transform(feat.geometry().asPoint()) for feat in destination_feats],
            values= [feat.attribute(destination_field.name()) for feat in destination_feats],
        )

        return route_dict
    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 ([x_point], feat)
Пример #6
0
    def get_parameters(self):
        """
        Builds parameters across directions functionalities.

        :returns: All parameter mappings.
        :rtype: dict
        """

        # API parameters
        profile = self.dlg.routing_travel_combo.currentText()
        mode = self.dlg.routing_mode_combo.currentText()

        params = {
            'costing': profile,
            'narrative': False,
            'id': 1,
        }

        params['locations'] = get_locations(self.dlg.routing_fromline_list)

        # Get Advanced parameters
        if self.dlg.routing_costing_options_group.isChecked(
        ) or mode == 'shortest':
            params['costing_options'] = dict()
            self.costing_options = params['costing_options'][
                profile] = get_costing_options(
                    self.dlg.routing_costing_options_group, profile)
            if mode == 'shortest':
                params['costing_options'][profile]['shortest'] = True

        if self.dlg.avoidlocation_group.isChecked():
            point_layer = self.dlg.avoidlocation_dropdown.currentLayer()
            if point_layer:
                locations = list()
                transformer = transform.transformToWGS(point_layer.sourceCrs())
                for feat in point_layer.getFeatures():
                    geom = feat.geometry()
                    geom.transform(transformer)
                    point = geom.asPoint()

                    locations.append({
                        'lon': round(point.x(), 6),
                        'lat': round(point.y(), 6)
                    })
                params['avoid_locations'] = locations

        return params
Пример #7
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"))

        mode = self.MODE_TYPES[self.parameterAsEnum(parameters, self.IN_MODE,
                                                    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)
        avoid_layer = self.parameterAsSource(parameters, self.IN_AVOID,
                                             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)

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

        # 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 feature amounts/counts
        sources_amount = source.featureCount()
        destinations_amount = destination.featureCount()
        if (sources_amount or destinations_amount) > 10000:
            raise QgsProcessingException(
                "ProcessingError: Too large input, please decimate.")

        sources_features = list(source.getFeatures())
        destinations_features = list(destination.getFeatures())

        # Get source and destination features
        xformer_source = transform.transformToWGS(source.sourceCrs())
        sources_points = [
            xformer_source.transform(feat.geometry().asPoint())
            for feat in sources_features
        ]
        xformer_destination = transform.transformToWGS(destination.sourceCrs())
        destination_points = [
            xformer_destination.transform(feat.geometry().asPoint())
            for feat in destinations_features
        ]

        # Build params
        params = dict(costing=self.PROFILE)

        # Sets all advanced parameters as attributes of self.costing_options
        self.costing_options.set_costing_options(self, parameters, context)

        costing_params = get_costing_options(self.costing_options,
                                             self.PROFILE, mode)
        if costing_params:
            params['costing_options'] = costing_params

        if avoid_layer:
            params['avoid_locations'] = get_avoid_locations(avoid_layer)

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

        source_attr_iter = self._chunks(sources_attributes, 50)
        for sources in self._chunks(sources_points, 50):
            params["sources"] = get_locations(sources)
            source_attributes = next(source_attr_iter)

            destination_attr_iter = self._chunks(destinations_attributes, 50)
            for destinations in self._chunks(destination_points, 50):
                params["targets"] = get_locations(destinations)
                params["id"] = "matrix"
                destination_attributes = next(destination_attr_iter)

                # Make request and catch ApiError
                try:
                    response = clnt.request('/sources_to_targets',
                                            post_json=params)
                except (exceptions.ApiError) as e:
                    msg = "{}: {}".format(e.__class__.__name__, str(e))
                    feedback.reportError(msg)
                    logger.log(msg)
                    continue
                except (exceptions.InvalidKey,
                        exceptions.GenericServerError) as e:
                    msg = "{}:\n{}".format(e.__class__.__name__, str(e))
                    logger.log(msg)
                    raise

                feats = matrix_core.get_output_features_matrix(
                    response, self.PROFILE, costing_params, source_attributes,
                    destination_attributes)

                for feat in feats:
                    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..."))

        mode = self.MODE_TYPES[self.parameterAsEnum(parameters, self.IN_MODE, context)]

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

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

        avoid_layer = self.parameterAsLayer(
            parameters,
            self.IN_AVOID,
            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))
        input_points = list()
        from_values = list()
        xformer_source = 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_source.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_source.transform(QgsPointXY(point)))
                input_points.append(points)
                from_values.append(feat[source_field_name])

        count = source.featureCount()

        params = dict()
        if avoid_layer:
            params['avoid_locations'] = get_avoid_locations(avoid_layer)

        # Sets all advanced parameters as attributes of self.costing_options
        self.costing_options.set_costing_options(self, parameters, context)

        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

            params.update(get_directions_params(points, self.PROFILE, self.costing_options, mode))
            params['id'] = from_value

            try:
                response = clnt.request('/route', post_json=params)
            except (exceptions.ApiError) as e:
                msg = "Feature ID {} caused a {}:\n{}".format(
                    from_value,
                    e.__class__.__name__,
                    str(e))
                feedback.reportError(msg)
                logger.log(msg)
                continue

            except (exceptions.InvalidKey, exceptions.GenericServerError) as e:
                msg = "{}:\n{}".format(
                    e.__class__.__name__,
                    str(e))
                logger.log(msg)
                raise

            options = {}
            if params.get('costing_options'):
                options = params['costing_options']

            sink.addFeature(directions_core.get_output_feature_directions(
                response,
                self.PROFILE,
                options.get(self.PROFILE),
                from_value=from_value
            ))

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

        return {self.OUT: dest_id}