def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) if network is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs()) endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs()) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) directionField = -1 if directionField: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo( QCoreApplication.translate('ShortestPathPointToPoint', 'Building graph…')) snappedPoints = director.makeGraph(builder, [startPoint, endPoint], feedback) feedback.pushInfo( QCoreApplication.translate('ShortestPathPointToPoint', 'Calculating shortest path…')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) idxEnd = graph.findVertex(snappedPoints[1]) tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: raise QgsProcessingException( self.tr('There is no route from start point to end point.')) route = [graph.vertex(idxEnd).point()] cost = costs[idxEnd] current = idxEnd while current != idxStart: current = graph.edge(tree[current]).fromVertex() route.append(graph.vertex(current).point()) route.reverse() feedback.pushInfo( QCoreApplication.translate('ShortestPathPointToPoint', 'Writing results…')) geom = QgsGeometry.fromPolylineXY(route) feat = QgsFeature() feat.setFields(fields) feat['start'] = startPoint.toString() feat['end'] = endPoint.toString() feat['cost'] = cost / multiplier feat.setGeometry(geom) sink.addFeature(feat, QgsFeatureSink.FastInsert) results = {} results[self.TRAVEL_COST] = cost / multiplier results[self.OUTPUT] = dest_id return results
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoint = self.parameterAsPoint(parameters, self.START_POINT, context) endPoints = self.parameterAsSource(parameters, self.END_POINTS, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(context.project().crs(), True, tolerance) feedback.pushInfo(self.tr('Loading end points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) request.setDestinationCrs(network.sourceCrs()) features = endPoints.getFeatures(request) total = 100.0 / endPoints.featureCount() if endPoints.featureCount( ) else 0 points = [startPoint] for current, f in enumerate(features): if feedback.isCanceled(): break points.append(f.geometry().asPoint()) feedback.setProgress(int(current * total)) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(self.tr('Calculating shortest paths...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) route = [] nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, count + 1): if feedback.isCanceled(): break idxEnd = graph.findVertex(snappedPoints[i]) if tree[idxEnd] == -1: msg = self.tr( 'There is no route from start point ({}) to end point ({}).' .format(startPoint.toString(), points[i].toString())) feedback.setProgressText(msg) QgsMessageLog.logMessage(msg, self.tr('Processing'), QgsMessageLog.WARNING) continue cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append( graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[0]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = startPoint.toString() feat['end'] = points[i].toString() feat['cost'] = cost / multiplier sink.addFeature(feat, QgsFeatureSink.FastInsert) route[:] = [] feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) if network is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) if startPoints is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.START_POINTS)) endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs()) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = startPoints.fields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(QCoreApplication.translate('ShortestPathLayerToPoint', 'Loading start points…')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0 points = [endPoint] source_attributes = {} i = 1 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo(QCoreApplication.translate('ShortestPathLayerToPoint', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(QCoreApplication.translate('ShortestPathLayerToPoint', 'Calculating shortest paths…')) graph = builder.graph() idxEnd = graph.findVertex(snappedPoints[0]) nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, nPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: msg = self.tr('There is no route from start point ({}) to end point ({}).'.format(points[i].toString(), endPoint.toString())) feedback.reportError(msg) # add feature with no geometry feat.clearGeometry() attrs = source_attributes[i] attrs.append(points[i].toString()) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) continue route = [graph.vertex(idxEnd).point()] cost = costs[idxEnd] current = idxEnd while current != idxStart: current = graph.edge(tree[current]).fromVertex() route.append(graph.vertex(current).point()) route.reverse() geom = QgsGeometry.fromPolylineXY(route) feat.setGeometry(geom) attrs = source_attributes[i] attrs.extend([points[i].toString(), endPoint.toString(), cost / multiplier]) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
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
def processAlgorithm(self, feedback): layer = dataobjects.getLayerFromString( self.getParameterValue(self.INPUT_VECTOR)) startPoint = self.getParameterValue(self.START_POINT) endPoints = dataobjects.getLayerFromString( self.getParameterValue(self.END_POINTS)) strategy = self.getParameterValue(self.STRATEGY) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields.toList(), QgsWkbTypes.LineString, layer.crs()) tmp = startPoint.split(',') startPoint = QgsPoint(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder( iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading end points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = vector.features(endPoints, request) count = len(features) points = [startPoint] for f in features: points.append(f.geometry().asPoint()) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) feedback.pushInfo(self.tr('Calculating shortest paths...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) route = [] total = 100.0 / count for i in range(1, count + 1): idxEnd = graph.findVertex(snappedPoints[i]) if tree[idxEnd] == -1: msg = self.tr( 'There is no route from start point ({}) to end point ({}).' .format(startPoint.toString(), points[i].toString())) feedback.setProgressText(msg) ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, msg) continue cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append( graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[0]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = startPoint.toString() feat['end'] = points[i].toString() feat['cost'] = cost / multiplier writer.addFeature(feat) route[:] = [] feedback.setProgress(int(i * total)) del writer
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) if network is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs()) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) include_bounds = True # default to true to maintain 3.0 API if self.INCLUDE_BOUNDS in parameters: include_bounds = self.parameterAsBool(parameters, self.INCLUDE_BOUNDS, context) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo( QCoreApplication.translate('ServiceAreaFromPoint', 'Building graph…')) snappedPoints = director.makeGraph(builder, [startPoint], feedback) feedback.pushInfo( QCoreApplication.translate('ServiceAreaFromPoint', 'Calculating service area…')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = set() points = [] lines = [] for vertex, start_vertex_cost in enumerate(cost): inbound_edge_index = tree[vertex] if inbound_edge_index == -1 and vertex != idxStart: # unreachable vertex continue if start_vertex_cost > travelCost: # vertex is too expensive, discard continue vertices.add(vertex) start_point = graph.vertex(vertex).point() # find all edges coming from this vertex for edge_id in graph.vertex(vertex).outgoingEdges(): edge = graph.edge(edge_id) end_vertex_cost = start_vertex_cost + edge.cost(0) end_point = graph.vertex(edge.toVertex()).point() if end_vertex_cost <= travelCost: # end vertex is cheap enough to include vertices.add(edge.toVertex()) lines.append([start_point, end_point]) else: # travelCost sits somewhere on this edge, interpolate position interpolated_end_point = QgsGeometryUtils.interpolatePointOnLineByValue( start_point.x(), start_point.y(), start_vertex_cost, end_point.x(), end_point.y(), end_vertex_cost, travelCost) points.append(interpolated_end_point) lines.append([start_point, interpolated_end_point]) for i in vertices: points.append(graph.vertex(i).point()) feedback.pushInfo( QCoreApplication.translate('ServiceAreaFromPoint', 'Writing results…')) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) (point_sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) results = {} if point_sink is not None: results[self.OUTPUT] = dest_id geomPoints = QgsGeometry.fromMultiPointXY(points) feat.setGeometry(geomPoints) feat['type'] = 'within' feat['start'] = startPoint.toString() point_sink.addFeature(feat, QgsFeatureSink.FastInsert) if include_bounds: upperBoundary = [] lowerBoundary = [] vertices = [] for i, v in enumerate(cost): if v > travelCost and tree[i] != -1: vertexId = graph.edge(tree[i]).fromVertex() if cost[vertexId] <= travelCost: vertices.append(i) for i in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[i]).toVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[i]).fromVertex()).point()) geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary) geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() point_sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() point_sink.addFeature(feat, QgsFeatureSink.FastInsert) (line_sink, line_dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LINES, context, fields, QgsWkbTypes.MultiLineString, network.sourceCrs()) if line_sink is not None: results[self.OUTPUT_LINES] = line_dest_id geom_lines = QgsGeometry.fromMultiPolylineXY(lines) feat.setGeometry(geom_lines) feat['type'] = 'lines' feat['start'] = startPoint.toString() line_sink.addFeature(feat, QgsFeatureSink.FastInsert) return results
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = startPoints.fields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount( ) else 0 points = [] source_attributes = {} i = 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(self.tr('Calculating service areas...')) graph = builder.graph() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) vertices = [] upperBoundary = [] lowerBoundary = [] total = 100.0 / len(snappedPoints) if snappedPoints else 1 for i, p in enumerate(snappedPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) for j, v in enumerate(cost): if v > travelCost and tree[j] != -1: vertexId = graph.edge(tree[j]).fromVertex() if cost[vertexId] <= travelCost: vertices.append(j) for j in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[j]).toVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[j]).fromVertex()).point()) geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary) geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary) feat.setGeometry(geomUpper) attrs = source_attributes[i] attrs.extend(['upper', origPoint]) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) attrs[-2] = 'lower' feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) vertices[:] = [] upperBoundary[:] = [] lowerBoundary[:] = [] feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context) startPoints = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.START_POINTS), context) strategy = self.getParameterValue(self.STRATEGY) travelCost = self.getParameterValue(self.TRAVEL_COST) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) writerPoints = self.getOutputFromName( self.OUTPUT_POINTS).getVectorWriter(fields, QgsWkbTypes.MultiPoint, layer.crs(), context) writerPolygons = self.getOutputFromName( self.OUTPUT_POLYGON).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = QgsProcessingUtils.getFeatures(startPoints, context, request) points = [] for f in features: points.append(f.geometry().asPoint()) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) feedback.pushInfo(self.tr('Calculating service areas...')) graph = builder.graph() vertices = [] upperBoundary = [] lowerBoundary = [] total = 100.0 / len(snappedPoints) for i, p in enumerate(snappedPoints): idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) for j, v in enumerate(cost): if v > travelCost and tree[j] != -1: vertexId = graph.edge(tree[j]).outVertex() if cost[vertexId] <= travelCost: vertices.append(j) for j in vertices: upperBoundary.append(graph.vertex(graph.edge(tree[j]).inVertex()).point()) lowerBoundary.append(graph.vertex(graph.edge(tree[j]).outVertex()).point()) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = origPoint writerPoints.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = origPoint writerPoints.addFeature(feat) upperBoundary.append(origPoint) lowerBoundary.append(origPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = origPoint writerPolygons.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = origPoint writerPolygons.addFeature(feat) vertices[:] = [] upperBoundary[:] = [] lowerBoundary[:] = [] feedback.setProgress(int(i * total)) del writerPoints del writerPolygons
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) startPoint = self.getParameterValue(self.START_POINT) strategy = self.getParameterValue(self.STRATEGY) travelCost = self.getParameterValue(self.TRAVEL_COST) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder( iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint]) feedback.pushInfo(self.tr('Calculating service area...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = [] for i, v in enumerate(cost): if v > travelCost and tree[i] != -1: vertexId = graph.edge(tree[i]).outVertex() if cost[vertexId] <= travelCost: vertices.append(i) upperBoundary = [] lowerBoundary = [] for i in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[i]).inVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[i]).outVertex()).point()) feedback.pushInfo(self.tr('Writing results...')) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName(self.OUTPUT_POINTS).getVectorWriter( fields, QgsWkbTypes.MultiPoint, layer.crs(), context) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat, QgsFeatureSink.FastInsert) del writer upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName(self.OUTPUT_POLYGON).getVectorWriter( fields, QgsWkbTypes.Polygon, layer.crs(), context) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat, QgsFeatureSink.FastInsert) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat, QgsFeatureSink.FastInsert) del writer
def processAlgorithm(self, feedback): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) startPoint = self.getParameterValue(self.START_POINT) endPoint = self.getParameterValue(self.END_POINT) strategy = self.getParameterValue(self.STRATEGY) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields.toList(), QgsWkbTypes.LineString, layer.crs()) tmp = startPoint.split(',') startPoint = QgsPoint(float(tmp[0]), float(tmp[1])) tmp = endPoint.split(',') endPoint = QgsPoint(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder( iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint, endPoint]) feedback.pushInfo(self.tr('Calculating shortest path...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) idxEnd = graph.findVertex(snappedPoints[1]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: raise GeoAlgorithmExecutionException( self.tr('There is no route from start point to end point.')) route = [] cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append( graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[0]) route.reverse() self.setOutputValue(self.TRAVEL_COST, cost / multiplier) feedback.pushInfo(self.tr('Writing results...')) geom = QgsGeometry.fromPolyline(route) feat = QgsFeature() feat.setFields(fields) feat['start'] = startPoint.toString() feat['end'] = endPoint.toString() feat['cost'] = cost / multiplier feat.setGeometry(geom) writer.addFeature(feat) del writer
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs()) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint], feedback) feedback.pushInfo(self.tr('Calculating service area...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = [] for i, v in enumerate(cost): if v > travelCost and tree[i] != -1: vertexId = graph.edge(tree[i]).fromVertex() if cost[vertexId] <= travelCost: vertices.append(i) upperBoundary = [] lowerBoundary = [] for i in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[i]).toVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[i]).fromVertex()).point()) feedback.pushInfo(self.tr('Writing results...')) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary) geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() sink.addFeature(feat, QgsFeatureSink.FastInsert) upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary) geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary) return {self.OUTPUT: dest_id}
def __init__( self, input_network, #QgsProcessingParameterFeatureSource input_points, #[QgsPointXY] or QgsProcessingParameterFeatureSource or QgsVectorLayer --> Implement List of QgsFeatures [QgsFeatures] input_strategy, #int input_directionFieldName, #str, empty if field not given input_forwardValue, #str input_backwardValue, #str input_bothValue, #str input_defaultDirection, #int input_analysisCrs, #QgsCoordinateReferenceSystem input_speedField, #str input_defaultSpeed, #float input_tolerance, #float feedback #feedback object from processing (log window) ): """ Constructor for a Qneat3Network object. @type input_network: QgsProcessingParameterFeatureSource @param input_network: input network dataset from processing algorithm @type input_points: QgsProcessingParameterFeatureSource/QgsVectorLayer/[QgsPointXY] @param input_points: input point dataset from processing algorithm @type input_strategy: int @param input_strategy: Strategy parameter (0 for distance evaluation, 1 time evaluation) @type directionFieldName: string @param directionFieldName: Field name of field containing direction information @type input_forwardValue: string @param input_forwardValue: Value assigned to forward-directed edges @type input_backwardValue: string @param input_backwardValue: Value assigned to backward-directed edges @type input_bothValue: string @param input_bothValues: Value assigned to undirected edges (accessible from both directions) @type input_defaultDirection: QgsVectorLayerDirector.DirectionForward/DirectionBackward/DirectionBoth @param input_defaultDirection: QgsVectorLayerDirector Direction enum to determine default direction @type input_analysisCrs: QgsCoordinateReferenceSystem @param input_analysisCrs: Analysis coordinate system @type input_speedField: string @param input_speedField: Field name of field containing speed information @type input_tolerance: float @param input_tolerance: tolerance value when connecting graph edges @type feedback: QgsProcessingFeedback @param feedback: feedback object from processing algorithm """ #initialize feedback self.feedback = feedback self.feedback.pushInfo( "[QNEAT3Network][__init__] Setting up parameters") self.AnalysisCrs = input_analysisCrs #enable polygon calculation in geographic coordinate systems distUnit = self.AnalysisCrs.mapUnits() self.meter_to_unit_factor = QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, distUnit) #init direction fields self.feedback.pushInfo( "[QNEAT3Network][__init__] Setting up network direction parameters" ) self.directedAnalysis = self.setNetworkDirection( (input_directionFieldName, input_forwardValue, input_backwardValue, input_bothValue, input_defaultDirection)) self.director = QgsVectorLayerDirector( input_network, getFieldIndexFromQgsProcessingFeatureSource( input_network, input_directionFieldName), input_forwardValue, input_backwardValue, input_bothValue, input_defaultDirection) #init analysis points self.feedback.pushInfo( "[QNEAT3Network][__init__] Setting up analysis points") if isinstance(input_points, (list, )): self.list_input_points = input_points #[QgsPointXY] else: self.list_input_points = getListOfPoints( input_points) #[QgsPointXY] self.input_points = input_points #Setup cost-strategy pattern. self.feedback.pushInfo( "[QNEAT3Network][__init__] Setting analysis strategy: {}".format( input_strategy)) self.default_speed = input_defaultSpeed self.setNetworkStrategy(input_strategy, input_network, input_speedField, input_defaultSpeed) #add the strategy to the QgsGraphDirector self.director.addStrategy(self.strategy) self.builder = QgsGraphBuilder(self.AnalysisCrs) #tell the graph-director to make the graph using the builder object and tie the start point geometry to the graph self.feedback.pushInfo( "[QNEAT3Network][__init__] Start tying analysis points to the graph and building it." ) self.feedback.pushInfo( "[QNEAT3Network][__init__] This is a compute intensive task and may take some time depending on network size" ) start_local_time = time.localtime() start_time = time.time() self.feedback.pushInfo( "[QNEAT3Network][__init__] Start Time: {}".format( time.strftime(":%Y-%m-%d %H:%M:%S", start_local_time))) self.feedback.pushInfo("[QNEAT3Network][__init__] Building...") self.list_tiedPoints = self.director.makeGraph(self.builder, self.list_input_points, self.feedback) self.network = self.builder.graph() end_local_time = time.localtime() end_time = time.time() self.feedback.pushInfo("[QNEAT3Network][__init__] End Time: {}".format( time.strftime(":%Y-%m-%d %H:%M:%S", end_local_time))) self.feedback.pushInfo( "[QNEAT3Network][__init__] Total Build Time: {}".format( end_time - start_time)) self.feedback.pushInfo( "[QNEAT3Network][__init__] Analysis setup complete")
def graph_builder(self, network, cost_field, origins, tolerance, crs, epsg): # Settings otf = False # Get index of cost field network_fields = network.fields() network_cost_index = network_fields.indexFromName(cost_field) # Setting up graph build director director = QgsVectorLayerDirector(network, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth) # Determining cost calculation if cost_field != 'length': strategy = ct.CustomCost(network_cost_index, 0.01) else: strategy = QgsNetworkDistanceStrategy() # Creating graph builder director.addStrategy(strategy) builder = QgsGraphBuilder(crs, otf, tolerance, epsg) # Reading origins and making list of coordinates graph_origin_points = [] # Loop through the origin points and add graph vertex indices for index, origin in enumerate(origins): graph_origin_points.append(origins[index]['geom'].asPoint()) # Get origin graph vertex index tied_origin_vertices = director.makeGraph(builder, graph_origin_points) # Build the graph graph = builder.graph() # Create dictionary of origin names and tied origins tied_origins = {} # Combine origin names and tied point vertices for index, tied_origin in enumerate(tied_origin_vertices): tied_origins[index] = { 'name': origins[index]['name'], 'vertex': tied_origin } self.spIndex = QgsSpatialIndex() self.indices = {} self.attributes_dict = {} self.centroids = {} i = 0 for f in network.getFeatures(): if f.geometry().type() == QgsWkbTypes.LineGeometry: if not f.geometry().isMultipart(): self.attributes_dict[f.id()] = f.attributes() polyline = f.geometry().asPolyline() for idx, p in enumerate(polyline[1:]): ml = QgsGeometry.fromPolylineXY([polyline[idx], p]) new_f = QgsFeature() new_f.setGeometry(ml.centroid()) new_f.setAttributes([f.id()]) new_f.setId(i) self.spIndex.addFeature(new_f) self.centroids[i] = f.id() i += 1 else: self.attributes_dict[f.id()] = f.attributes() for pl in f.geometry().asMultiPolyline(): for idx, p in enumerate(pl[1:]): ml = QgsGeometry.fromPolylineXY([pl[idx], p]) new_f = QgsFeature() new_f.setGeometry(ml.centroid()) new_f.setAttributes([f.id()]) new_f.setId(i) self.spIndex.addFeature(new_f) self.centroids[i] = f.id() i += 1 self.network_fields = network_fields return graph, tied_origins