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