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
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) 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, QgsWkbTypes.LineString, layer.crs(), context) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) tmp = endPoint.split(',') endPoint = 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) 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) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) endPoint = self.parameterAsPoint(parameters, self.END_POINT, 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 start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) request.setDestinationCrs(network.sourceCrs()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0 points = [endPoint] 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() idxEnd = graph.findVertex(snappedPoints[0]) route = [] nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, count + 1): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) tree, cost = 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.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[i]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = points[i].toString() feat['end'] = endPoint.toString() feat['cost'] = cost / multiplier sink.addFeature(feat, QgsFeatureSink.FastInsert) route[:] = [] feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
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(), iface.mapCanvas().hasCrsTransformEnabled(), 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 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, feedback): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) startPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.START_POINTS)) 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()) writerPolygons = self.getOutputFromName( self.OUTPUT_POLYGON).getVectorWriter( fields, QgsWkbTypes.Polygon, layer.crs()) 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(), iface.mapCanvas().hasCrsTransformEnabled(), tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = vector.features(startPoints, 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(startPoint) lowerBoundary.append(startPoint) 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, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) 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 = 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) director.addStrategy(strategy) builder = QgsGraphBuilder(iface.mapCanvas().mapSettings().destinationCrs(), iface.mapCanvas().hasCrsTransformEnabled(), tolerance) progress.setInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint]) progress.setInfo(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()) progress.setInfo(self.tr('Writting 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()) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer writer = self.getOutputFromName( self.OUTPUT_POLYGON).getVectorWriter( fields, QgsWkbTypes.Polygon, layer.crs()) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer
class Qneat3Network(): """ Qneat3Network: Provides basic logic for more advanced network analysis algorithms """ 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) ): """ Constructur 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]: setting up parameters") self.AnalysisCrs = input_analysisCrs #init direction fields self.feedback.pushInfo( "[QNEAT3Network]: 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]: 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]: Setting analysis strategy: {}".format( input_strategy)) self.setNetworkStrategy(input_strategy, input_network, input_speedField, input_defaultSpeed) self.director.addStrategy(self.strategy) #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]: Start tying analysis points to the graph and building it." ) self.feedback.pushInfo( "...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("...Start Time: {}".format( time.strftime(":%Y-%m-%d %H:%M:%S", start_local_time))) self.list_tiedPoints = self.director.makeGraph(self.builder, self.list_input_points) self.network = self.builder.graph() end_local_time = time.localtime() end_time = time.time() self.feedback.pushInfo("...End Time: {}".format( time.strftime(":%Y-%m-%d %H:%M:%S", end_local_time))) self.feedback.pushInfo("...Total Build Time: {}".format(end_time - start_time)) self.feedback.pushInfo("[QNEAT3Network]: Analysis setup complete") def setNetworkDirection(self, directionArgs): if directionArgs.count("") == 0: self.directedAnalysis = True self.directionFieldId, self.input_forwardValue, self.input_backwardValue, self.input_bothValue, self.input_defaultDirection = directionArgs else: self.directedAnalysis = False def setNetworkStrategy(self, input_strategy, input_network, input_speedField, input_defaultSpeed): distUnit = self.AnalysisCrs.mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) speedFieldId = getFieldIndexFromQgsProcessingFeatureSource( input_network, input_speedField) if input_strategy == 0: self.strategy = QgsNetworkDistanceStrategy() else: self.strategy = QgsNetworkSpeedStrategy( speedFieldId, float(input_defaultSpeed), multiplier * 1000.0 / 3600.0) self.multiplier = 3600 def calcDijkstra(self, startpoint_id, criterion): """Calculates Dijkstra on whole network beginning from one startPoint. Returns a list containing a TreeId-Array and Cost-Array that match up with their indices [[tree],[cost]] """ tree, cost = QgsGraphAnalyzer.dijkstra(self.network, startpoint_id, criterion) dijkstra_query = list() dijkstra_query.insert(0, tree) dijkstra_query.insert(1, cost) return dijkstra_query def calcShortestTree(self, startpoint_id, criterion): tree = QgsGraphAnalyzer.shortestTree(self.network, startpoint_id, criterion) return tree def calcIsoPoints(self, analysis_point_list, max_dist): iso_pointcloud = dict() for point in analysis_point_list: dijkstra_query = self.calcDijkstra(point.network_vertex_id, 0) tree = dijkstra_query[0] cost = dijkstra_query[1] current_start_point_id = point.point_id field_type = getFieldDatatypeFromPythontype(current_start_point_id) i = 0 while i < len(cost): #as long as costs at vertex i is greater than iso_distance and there exists an incoming edge (tree[i]!=-1) #consider it as a possible catchment polygon element if tree[i] != -1: toVertexId = self.network.edge(tree[i]).toVertex() #if the costs of the current vertex are lower than the radius, append the vertex id to results. if cost[toVertexId] <= max_dist: current_cost = cost[toVertexId] #build feature feat = QgsFeature() fields = QgsFields() fields.append( QgsField('vertex_id', QVariant.Int, '', 254, 0)) fields.append( QgsField('cost', QVariant.Double, '', 254, 7)) fields.append( QgsField('origin_point_id', field_type, '', 254, 7)) feat.setFields(fields) feat['vertex_id'] = toVertexId feat['cost'] = current_cost feat['origin_point_id'] = current_start_point_id geom = QgsGeometry().fromPointXY( self.network.vertex(toVertexId).point()) feat.setGeometry(geom) if toVertexId not in iso_pointcloud: self.feedback.pushInfo( "insert idx {} with {} cost".format( toVertexId, current_cost)) iso_pointcloud.update({toVertexId: feat}) if toVertexId in iso_pointcloud.keys( ) and iso_pointcloud.get( toVertexId)['cost'] > current_cost: #if the vertex already exists in the iso_pointcloud and the c self.feedback.pushInfo( "replace idx {} with {} cost".format( toVertexId, current_cost)) iso_pointcloud.pop(toVertexId) iso_pointcloud.update({toVertexId: feat}) #count up to next vertex i = i + 1 return iso_pointcloud.values() #list of QgsFeature (=QgsFeatureList) def calcIsoInterpolation(self, iso_point_layer, resolution, interpolation_raster_path): layer_data = QgsInterpolator.LayerData() QgsInterpolator.LayerData layer_data.source = iso_point_layer #in QGIS2: vectorLayer layer_data.valueSource = QgsInterpolator.ValueAttribute layer_data.interpolationAttribute = 1 #take second field to get costs layer_data.sourceType = QgsInterpolator.SourcePoints tin_interpolator = QgsTinInterpolator([layer_data], QgsTinInterpolator.Linear) rect = iso_point_layer.extent() ncol = int((rect.xMaximum() - rect.xMinimum()) / resolution) nrows = int((rect.yMaximum() - rect.yMinimum()) / resolution) writer = QgsGridFileWriter(tin_interpolator, self.interpolation_raster_path, rect, ncol, nrows, resolution, resolution) writer.writeFile(self.feedback) # Creating .asc raste return QgsRasterLayer(self.interpolation_raster_path, "temp_qneat3_interpolation_raster", True) def calcIsoContours(self, interval, sink): ds_in = gdal.Open(self.interpolation_raster_path) band_in = ds_in.GetRasterBand(1) xsize_in = band_in.XSize ysize_in = band_in.YSize geotransform_in = ds_in.GetGeoTransform() srs = osr.SpatialReference() srs.ImportFromWkt(ds_in.GetProjectionRef()) x_pos = arange(geotransform_in[0], geotransform_in[0] + xsize_in * geotransform_in[1], geotransform_in[1]) y_pos = arange(geotransform_in[3], geotransform_in[3] + ysize_in * geotransform_in[5], geotransform_in[5]) x_grid, y_grid = meshgrid(x_pos, y_pos) raster_values = band_in.ReadAsArray(0, 0, xsize_in, ysize_in) stats = band_in.GetStatistics(False, True) min_value = stats[0] min_level = interval * floor(min_value / interval) max_value = stats[1] #Due to range issues, a level is added max_level = interval * (1 + ceil(max_value / interval)) levels = arange(min_level, max_level, interval) contours = plt.contourf(x_grid, y_grid, raster_values, levels) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) fields.append(QgsField('cost_level', QVariant.Double, '', 254, 7)) """Maybe move to algorithm""" for i, level in enumerate(range(len(contours.collections))): paths = contours.collections[level].get_paths() for path in paths: feat = QgsFeature() feat.setFields(fields) geom = QgsGeometry().fromPolygonXY(path) feat.setGeometry(geom) feat['id'] = i feat['cost_level'] = level sink.addFeature(feat, QgsFeatureSink.FastInsert) """Maybe move to algorithm""" return sink def calcIsoPolygon(self): """ feat_out = ogr.Feature( dst_layer.GetLayerDefn()) feat_out.SetField( attr_name, contours.levels[level] ) pol = ogr.Geometry(ogr.wkbPolygon) ring = None for i in range(len(path.vertices)): point = path.vertices[i] if path.codes[i] == 1: if ring != None: pol.AddGeometry(ring) ring = ogr.Geometry(ogr.wkbLinearRing) ring.AddPoint_2D(point[0], point[1]) pol.AddGeometry(ring) feat_out.SetGeometry(pol) if dst_layer.CreateFeature(feat_out) != 0: print "Failed to create feature in shapefile.\n" exit( 1 ) feat_out.Destroy() """ self.iso_polygon pass
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) ): """ Constructur 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]: setting up parameters") self.AnalysisCrs = input_analysisCrs #init direction fields self.feedback.pushInfo( "[QNEAT3Network]: 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]: 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]: Setting analysis strategy: {}".format( input_strategy)) self.setNetworkStrategy(input_strategy, input_network, input_speedField, input_defaultSpeed) self.director.addStrategy(self.strategy) #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]: Start tying analysis points to the graph and building it." ) self.feedback.pushInfo( "...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("...Start Time: {}".format( time.strftime(":%Y-%m-%d %H:%M:%S", start_local_time))) self.list_tiedPoints = self.director.makeGraph(self.builder, self.list_input_points) self.network = self.builder.graph() end_local_time = time.localtime() end_time = time.time() self.feedback.pushInfo("...End Time: {}".format( time.strftime(":%Y-%m-%d %H:%M:%S", end_local_time))) self.feedback.pushInfo("...Total Build Time: {}".format(end_time - start_time)) self.feedback.pushInfo("[QNEAT3Network]: Analysis setup complete")
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
class Qneat3Network(): """ Qneat3Network: Provides basic logic for more advanced network analysis algorithms """ 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 setNetworkDirection(self, directionArgs): if directionArgs.count("") == 0: self.directedAnalysis = True self.directionFieldId, self.input_forwardValue, self.input_backwardValue, self.input_bothValue, self.input_defaultDirection = directionArgs else: self.directedAnalysis = False def setNetworkStrategy(self, input_strategy, input_network, input_speedField, input_defaultSpeed): distUnit = self.AnalysisCrs.mapUnits() unit_to_meter_factor = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) speedFieldId = getFieldIndexFromQgsProcessingFeatureSource( input_network, input_speedField) if input_strategy == 0: self.strategy = QgsNetworkDistanceStrategy() self.strategy_int = 0 else: self.strategy = QgsNetworkSpeedStrategy( speedFieldId, float(input_defaultSpeed), unit_to_meter_factor * 1000.0 / 3600.0) self.strategy_int = 1 self.multiplier = 3600 def calcDijkstra(self, startpoint_id, criterion): """Calculates Dijkstra on whole network beginning from one startPoint. Returns a list containing a TreeId-Array and Cost-Array that match up with their indices [[tree],[cost]] """ tree, cost = QgsGraphAnalyzer.dijkstra(self.network, startpoint_id, criterion) dijkstra_query = list() dijkstra_query.insert(0, tree) dijkstra_query.insert(1, cost) return dijkstra_query def calcShortestTree(self, startpoint_id, criterion): tree = QgsGraphAnalyzer.shortestTree(self.network, startpoint_id, criterion) return tree def calcIsoPoints(self, analysis_point_list, max_dist): iso_pointcloud = dict() for counter, point in enumerate(analysis_point_list): self.feedback.pushInfo( "[QNEAT3Network][calcIsoPoints] Processing Point {}".format( counter)) dijkstra_query = self.calcDijkstra(point.network_vertex_id, 0) tree = dijkstra_query[0] cost = dijkstra_query[1] current_start_point_id = point.point_id #id of the input point current_vertex_id = point.network_vertex_id entry_cost = point.entry_cost field_type = getFieldDatatypeFromPythontype(current_start_point_id) #startpoints are not part of the Query so they have to be added manually before #dikstra is called. start_vertex_feat = QgsFeature() start_vertex_fields = QgsFields() start_vertex_fields.append( QgsField('vertex_id', QVariant.Int, '', 254, 0)) start_vertex_fields.append( QgsField('cost', QVariant.Double, '', 254, 7)) start_vertex_fields.append( QgsField('origin_point_id', field_type, '', 254, 7)) start_vertex_feat.setFields(start_vertex_fields) start_vertex_feat['vertex_id'] = current_vertex_id start_vertex_feat['cost'] = entry_cost start_vertex_feat['origin_point_id'] = current_start_point_id pt_m = QgsPoint(self.network.vertex(current_vertex_id).point()) pt_m.addMValue(entry_cost) geom = QgsGeometry(pt_m) start_vertex_feat.setGeometry(geom) iso_pointcloud.update({current_vertex_id: start_vertex_feat}) i = 0 while i < len(cost): #as long as costs at vertex i is greater than iso_distance and there exists an incoming edge (tree[i]!=-1) #consider it as a possible catchment polygon element if tree[i] != -1: fromVertexId = self.network.edge(tree[i]).toVertex() real_cost = cost[fromVertexId] + entry_cost #if the costs of the current vertex are lower than the radius, append the vertex id to results. if real_cost <= max_dist: #build feature feat = QgsFeature() fields = QgsFields() fields.append( QgsField('vertex_id', QVariant.Int, '', 254, 0)) fields.append( QgsField('cost', QVariant.Double, '', 254, 7)) fields.append( QgsField('origin_point_id', field_type, '', 254, 7)) feat.setFields(fields) feat['vertex_id'] = fromVertexId feat['cost'] = real_cost feat['origin_point_id'] = current_start_point_id pt_m = QgsPoint( self.network.vertex(fromVertexId).point()) pt_m.addMValue((500 - cost[fromVertexId]) * 2) geom = QgsGeometry(pt_m) feat.setGeometry(geom) if fromVertexId not in iso_pointcloud: #ERROR: FIRST POINT IN POINTCLOUD WILL NEVER BE ADDED iso_pointcloud.update({fromVertexId: feat}) if fromVertexId in iso_pointcloud.keys( ) and iso_pointcloud.get( fromVertexId)['cost'] > real_cost: #if the vertex already exists in the iso_pointcloud and the cost is greater than the existing cost del iso_pointcloud[fromVertexId] #iso_pointcloud.pop(toVertexId) iso_pointcloud.update({fromVertexId: feat}) #count up to next vertex i = i + 1 if (i % 10000) == 0: self.feedback.pushInfo( "[QNEAT3Network][calcIsoPoints] Added {} Nodes to iso pointcloud..." .format(i)) return iso_pointcloud.values() #list of QgsFeature (=QgsFeatureList) def calcQneatInterpolation(self, iso_pointcloud_featurelist, resolution, interpolation_raster_path): #prepare spatial index uri = 'PointM?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&key=vertex_id&index=yes'.format( self.AnalysisCrs.authid()) mIsoPointcloud = QgsVectorLayer(uri, "mIsoPointcloud_layer", "memory") mIsoPointcloud_provider = mIsoPointcloud.dataProvider() mIsoPointcloud_provider.addFeatures(iso_pointcloud_featurelist, QgsFeatureSink.FastInsert) #implement spatial index for lines (closest line, etc...) spt_idx = QgsSpatialIndex( mIsoPointcloud.getFeatures(QgsFeatureRequest()), self.feedback) #prepare numpy coordinate grids NoData_value = -9999 raster_rectangle = mIsoPointcloud.extent() #top left point xmin = raster_rectangle.xMinimum() ymin = raster_rectangle.yMinimum() xmax = raster_rectangle.xMaximum() ymax = raster_rectangle.yMaximum() cols = int((xmax - xmin) / resolution) rows = int((ymax - ymin) / resolution) output_interpolation_raster = gdal.GetDriverByName('GTiff').Create( interpolation_raster_path, cols, rows, 1, gdal.GDT_Float64) output_interpolation_raster.SetGeoTransform( (xmin, resolution, 0, ymax, 0, -resolution)) band = output_interpolation_raster.GetRasterBand(1) band.SetNoDataValue(NoData_value) #initialize zero array with 2 dimensions (according to rows and cols) raster_data = zeros(shape=(rows, cols)) #compute raster cell MIDpoints x_pos = linspace(xmin + (resolution / 2), xmax - (resolution / 2), raster_data.shape[1]) y_pos = linspace(ymax - (resolution / 2), ymin + (resolution / 2), raster_data.shape[0]) x_grid, y_grid = meshgrid(x_pos, y_pos) self.feedback.pushInfo( '[QNEAT3Network][calcQneatInterpolation] Beginning with interpolation' ) total_work = rows * cols counter = 0 self.feedback.pushInfo( '[QNEAT3Network][calcQneatInterpolation] Total workload: {} cells'. format(total_work)) self.feedback.setProgress(0) for i in range(rows): for j in range(cols): current_pixel_midpoint = QgsPointXY(x_grid[i, j], y_grid[i, j]) nearest_vertex_fid = spt_idx.nearestNeighbor( current_pixel_midpoint, 1)[0] nearest_feature = mIsoPointcloud.getFeature(nearest_vertex_fid) nearest_vertex = self.network.vertex( nearest_feature['vertex_id']) edges = nearest_vertex.incomingEdges( ) + nearest_vertex.outgoingEdges() vertex_found = False nearest_counter = 2 while vertex_found == False: n_nearest_feature_fid = spt_idx.nearestNeighbor( current_pixel_midpoint, nearest_counter)[nearest_counter - 1] n_nearest_feature = mIsoPointcloud.getFeature( n_nearest_feature_fid) n_nearest_vertex_id = n_nearest_feature['vertex_id'] for edge_id in edges: from_vertex_id = self.network.edge( edge_id).fromVertex() to_vertex_id = self.network.edge(edge_id).toVertex() if n_nearest_vertex_id == from_vertex_id: vertex_found = True vertex_type = "from_vertex" from_point = n_nearest_feature.geometry().asPoint() from_vertex_cost = n_nearest_feature['cost'] if n_nearest_vertex_id == to_vertex_id: vertex_found = True vertex_type = "to_vertex" to_point = n_nearest_feature.geometry().asPoint() to_vertex_cost = n_nearest_feature['cost'] nearest_counter = nearest_counter + 1 """ if nearest_counter == 5: vertex_found = True vertex_type = "end_vertex" """ if vertex_type == "from_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY( [from_point, nearest_vertex.point()]) res = nearest_edge_geometry.closestSegmentWithContext( current_pixel_midpoint) segment_point = res[ 1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance( current_pixel_midpoint) dist_edge = from_point.distance(segment_point) #self.feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #self.feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #self.feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = from_vertex_cost + dist_edge + dist_to_segment raster_data[i, j] = pixel_cost elif vertex_type == "to_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY( [nearest_vertex.point(), to_point]) res = nearest_edge_geometry.closestSegmentWithContext( current_pixel_midpoint) segment_point = res[ 1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance( current_pixel_midpoint) dist_edge = to_point.distance(segment_point) #self.feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #self.feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #self.feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = to_vertex_cost + dist_edge + dist_to_segment raster_data[i, j] = pixel_cost else: pixel_cost = -99999 #nearest_feature['cost'] + (nearest_vertex.point().distance(current_pixel_midpoint)) """ nearest_feature_pointxy = nearest_feature.geometry().asPoint() nearest_feature_cost = nearest_feature['cost'] dist_to_vertex = current_pixel_midpoint.distance(nearest_feature_pointxy) #implement time cost pixel_cost = dist_to_vertex + nearest_feature_cost raster_data[i,j] = pixel_cost """ counter = counter + 1 if counter % 1000 == 0: self.feedback.pushInfo( "[QNEAT3Network][calcQneatInterpolation] Interpolated {} cells..." .format(counter)) self.feedback.setProgress((counter / total_work) * 100) band.WriteArray(raster_data) outRasterSRS = osr.SpatialReference() outRasterSRS.ImportFromWkt(self.AnalysisCrs.toWkt()) output_interpolation_raster.SetProjection(outRasterSRS.ExportToWkt()) band.FlushCache() def calcIsoTinInterpolation(self, iso_point_layer, resolution, interpolation_raster_path): if self.AnalysisCrs.isGeographic(): raise QgsProcessingException( 'The TIN-Interpolation algorithm in QGIS is designed to work with projected coordinate systems.Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!' ) layer_data = QgsInterpolator.LayerData() QgsInterpolator.LayerData layer_data.source = iso_point_layer #in QGIS2: vectorLayer layer_data.valueSource = QgsInterpolator.ValueAttribute layer_data.interpolationAttribute = 1 #take second field to get costs layer_data.sourceType = QgsInterpolator.SourcePoints tin_interpolator = QgsTinInterpolator([layer_data], QgsTinInterpolator.Linear) rect = iso_point_layer.extent() ncol = int((rect.xMaximum() - rect.xMinimum()) / resolution) nrows = int((rect.yMaximum() - rect.yMinimum()) / resolution) writer = QgsGridFileWriter(tin_interpolator, interpolation_raster_path, rect, ncol, nrows) writer.writeFile(self.feedback) # Creating .asc raste return QgsRasterLayer(interpolation_raster_path, "temp_qneat3_interpolation_raster") def calcIsoContours(self, max_dist, interval, interpolation_raster_path): featurelist = [] try: import matplotlib.pyplot as plt except: return featurelist ds_in = gdal.Open(interpolation_raster_path) band_in = ds_in.GetRasterBand(1) xsize_in = band_in.XSize ysize_in = band_in.YSize geotransform_in = ds_in.GetGeoTransform() srs = osr.SpatialReference() srs.ImportFromWkt(ds_in.GetProjectionRef()) raster_values = band_in.ReadAsArray(0, 0, xsize_in, ysize_in) raster_values[ raster_values < 0] = max_dist + 1000 #necessary to produce rectangular array from raster #nodata values get replaced by the maximum value + 1 x_pos = linspace( geotransform_in[0], geotransform_in[0] + geotransform_in[1] * raster_values.shape[1], raster_values.shape[1]) y_pos = linspace( geotransform_in[3], geotransform_in[3] + geotransform_in[5] * raster_values.shape[0], raster_values.shape[0]) x_grid, y_grid = meshgrid(x_pos, y_pos) start = interval end = interval * ceil(max_dist / interval) + interval levels = arange(start, end, interval) fid = 0 for current_level in nditer(levels): self.feedback.pushInfo( "[QNEAT3Network][calcIsoContours] Calculating {}-level contours" .format(current_level)) contours = plt.contourf(x_grid, y_grid, raster_values, [0, current_level], antialiased=True) for collection in contours.collections: for contour_paths in collection.get_paths(): for polygon in contour_paths.to_polygons(): x = polygon[:, 0] y = polygon[:, 1] polylinexy_list = [ QgsPointXY(i[0], i[1]) for i in zip(x, y) ] feat = QgsFeature() fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) fields.append( QgsField('cost_level', QVariant.Double, '', 20, 7)) feat.setFields(fields) geom = QgsGeometry().fromPolylineXY(polylinexy_list) feat.setGeometry(geom) feat['id'] = fid feat['cost_level'] = float(current_level) featurelist.insert(0, feat) fid = fid + 1 return featurelist def calcIsoPolygons(self, max_dist, interval, interpolation_raster_path): featurelist = [] try: import matplotlib.pyplot as plt except: return featurelist ds_in = gdal.Open(interpolation_raster_path) band_in = ds_in.GetRasterBand(1) xsize_in = band_in.XSize ysize_in = band_in.YSize geotransform_in = ds_in.GetGeoTransform() srs = osr.SpatialReference() srs.ImportFromWkt(ds_in.GetProjectionRef()) raster_values = band_in.ReadAsArray(0, 0, xsize_in, ysize_in) raster_values[ raster_values < 0] = max_dist + 1000 #necessary to produce rectangular array from raster #nodata values get replaced by the maximum value + 1 x_pos = linspace( geotransform_in[0], geotransform_in[0] + geotransform_in[1] * raster_values.shape[1], raster_values.shape[1]) y_pos = linspace( geotransform_in[3], geotransform_in[3] + geotransform_in[5] * raster_values.shape[0], raster_values.shape[0]) x_grid, y_grid = meshgrid(x_pos, y_pos) start = interval end = interval * ceil(max_dist / interval) + interval levels = arange(start, end, interval) fid = 0 for current_level in nditer(levels): self.feedback.pushInfo( "[QNEAT3Network][calcIsoPolygons] calculating {}-level contours" .format(current_level)) contours = plt.contourf(x_grid, y_grid, raster_values, [0, current_level], antialiased=True) for collection in contours.collections: for contour_path in collection.get_paths(): polygon_list = [] for vertex in contour_path.to_polygons(): x = vertex[:, 0] y = vertex[:, 1] polylinexy_list = [ QgsPointXY(i[0], i[1]) for i in zip(x, y) ] polygon_list.append(polylinexy_list) feat = QgsFeature() fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) fields.append( QgsField('cost_level', QVariant.Double, '', 20, 7)) feat.setFields(fields) geom = QgsGeometry().fromPolygonXY(polygon_list) feat.setGeometry(geom) feat['id'] = fid feat['cost_level'] = float(current_level) featurelist.insert(0, feat) fid = fid + 1 """Maybe move to algorithm""" #featurelist = featurelist[::-1] #reverse self.feedback.pushInfo( "[QNEAT3Network][calcIsoPolygons] number of elements in contour_featurelist: {}" .format(len(featurelist))) return featurelist
class mtrForm(MTR_RestrictionDialog): def __init__(self, iface, parent=None): if not parent: parent = iface.mainWindow() super().__init__(parent) self.iface = iface QgsMessageLog.logMessage("In generateMTRForm::init", tag="TOMs panel") #self.currDialog = MTR_RestrictionDialog(self.iface.mainWindow()) self.linkLayer = QgsProject.instance().mapLayersByName( "OS_RAMI_RoadLink")[0] self.dbConn = self.getDbConnection() if not self.dbConn: reply = QMessageBox.information(None, "Information", "Problem with db connection", QMessageBox.Ok) return self.currPointReferenceMapTool = None self.setupThisUi() # https://www.tutorialspoint.com/pyqt/pyqt_qstackedwidget.htm self.setupTrace() self.ptList = [] def setupThisUi(self): # create stacked widgets that can be used based on the restriction type choosen self.accessRestrictionAttributeStack = QWidget() self.turnRestrictionAttributeStack = QWidget() self.highwayDedicationAttributeStack = QWidget() self.restrictionForVehiclesAttributeStack = QWidget() self.attributeLayout = self.findChild(QFormLayout, "attributesFormLayout") self.accessRestrictionGeometryStack = QWidget() self.turnRestrictionGeometryStack = QWidget() self.highwayDedicationGeometryStack = QWidget() self.restrictionForVehiclesGeometryStack = QWidget() self.geometryLayout = self.findChild(QFormLayout, "geometryFormLayout") self.generateAccessRestrictionForm() self.generateTurnRestrictionForm() self.generateHighwayDedicationForm() self.generateRestrictionForVehiclesForm() self.geometryStack = QStackedWidget(self) self.geometryStack.addWidget(self.accessRestrictionGeometryStack) self.geometryStack.addWidget(self.turnRestrictionGeometryStack) self.geometryStack.addWidget(self.highwayDedicationGeometryStack) self.geometryStack.addWidget(self.restrictionForVehiclesGeometryStack) self.geometryLayout.addWidget(self.geometryStack) self.attributeStack = QStackedWidget(self) self.attributeStack.addWidget(self.accessRestrictionAttributeStack) self.attributeStack.addWidget(self.turnRestrictionAttributeStack) self.attributeStack.addWidget(self.highwayDedicationAttributeStack) self.attributeStack.addWidget( self.restrictionForVehiclesAttributeStack) self.attributeLayout.addWidget(self.attributeStack) self.generateFirstStageForm() # Display for shortest path self.rbShortPath = QgsRubberBand(self.iface.mapCanvas()) self.rbShortPath.setColor(Qt.green) self.rbShortPath.setWidth(5) def setupTrace(self): self.director = QgsVectorLayerDirector( self.linkLayer, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth) strategy = QgsNetworkDistanceStrategy() self.director.addStrategy(strategy) self.builder = QgsGraphBuilder(self.linkLayer.crs()) def generateFirstStageForm(self): QgsMessageLog.logMessage( "In generateFirstStageForm::generateForm ... ", tag="TOMs panel") mtrTypeLayout = self.findChild(QFormLayout, "MTR_Type_Layout") self.mtrTypeCB = self.findChild(QComboBox, "cmb_MTR_list") enumList = self.getEnumList('MT_RestrictionType') #QgsMessageLog.logMessage("In generateFirstStageForm::generateForm ... signal connected 1", tag="TOMs panel") self.mtrTypeCB.addItems(enumList) #QgsMessageLog.logMessage("In generateFirstStageForm::generateForm ... signal connected 2", tag="TOMs panel") self.mtrTypeCB.currentIndexChanged.connect(self.onChanged) self.mtrTypeCB.setCurrentIndex(1) QgsMessageLog.logMessage( "In generateFirstStageForm::generateForm ... signal connected 3", tag="TOMs panel") def onChanged(self, i): QgsMessageLog.logMessage( "In generateFirstStageForm::selectionchange...", tag="TOMs panel") #QgsMessageLog.logMessage("In generateFirstStageForm::selectionchange. Current index selection changed " + text, tag="TOMs panel") self.attributeStack.setCurrentIndex(i - 1) # to take account of the "null" self.geometryStack.setCurrentIndex(i - 1) # to take account of the "null" def getPointReference(self): QgsMessageLog.logMessage("In getPointReference ..." + self.mtrTypeCB.currentText(), tag="TOMs panel") self.mapTool = self.currPointReferenceMapTool if not self.mapTool: self.mapTool = getLinkDetailsMapTool(self.iface) self.currPointReferenceMapTool = self.mapTool self.iface.mapCanvas().setMapTool(self.mapTool) self.mapTool.notifyLinkFound.connect(self.foundLinkForPoint) def getLinkReference(self): QgsMessageLog.logMessage("In getLinkReference ..." + self.mtrTypeCB.currentText(), tag="TOMs panel") self.mapTool = self.currPointReferenceMapTool if not self.mapTool: self.mapTool = getLinkDetailsMapTool(self.iface) self.currPointReferenceMapTool = self.mapTool self.iface.mapCanvas().setMapTool(self.mapTool) self.mapTool.notifyLinkFound.connect(self.foundLinkForLine) def getLinkReferenceFirst(self): QgsMessageLog.logMessage("In getLinkReferenceFirst ... ", tag="TOMs panel") self.ptList = [] self.rbShortPath.reset() self.getLinkReference() def foundLinkForPoint(self, nearestPt, feature, length): QgsMessageLog.logMessage("In foundLink ... length: " + str(length), tag="TOMs panel") self.mapTool.notifyLinkFound.disconnect(self.foundLinkForPoint) # check restriction type # if only point # process point # add details to relevant layers # make the geometry layout "not available" and highlight the attribute details # otherwise check whether or not a point is already found def foundLinkForLine(self, nearestPt, feature, length): QgsMessageLog.logMessage("In foundLink ... length: " + str(length), tag="TOMs panel") self.mapTool.notifyLinkFound.disconnect(self.foundLinkForLine) # add details to list self.ptList.append((nearestPt, feature, length)) if len(self.ptList) >= 2: # we can now display the line - taken from Qgis Py Cookbook startPt = self.ptList[0][0].asPoint() endPt = self.ptList[1][0].asPoint() QgsMessageLog.logMessage("In foundLinkForLine::startPt " + startPt.asWkt(), tag="TOMs panel") QgsMessageLog.logMessage("In foundLinkForLine::endPt " + endPt.asWkt(), tag="TOMs panel") route = self.showShortestPath(startPt, endPt) # prepare list of links startFeature = self.ptList[0][1] endFeature = self.ptList[1][1] if startFeature != endFeature: # need to step through route and identify each new link self.linkList = [startFeature] currLink = startFeature QgsMessageLog.logMessage("In foundLinkForLine::currLink " + currLink.geometry().asWkt(), tag="TOMs panel") for currPt in route: # if p is on currLink, move on. Otherwise add link to list QgsMessageLog.logMessage("In foundLinkForLine::currPt " + currPt.asWkt(), tag="TOMs panel") if not self.pointOnLine(currPt, currLink.geometry()): # get link that contains p and prevPt nextLink = self.findLinkContainingLine(prevPt, currPt) if nextLink == endFeature: break else: self.linkList.append(nextLink) currLink = nextLink QgsMessageLog.logMessage( "In foundLinkForLine::currLink " + currLink.geometry().asWkt(), tag="TOMs panel") prevPt = currPt self.linkList.append(endFeature) def pointOnLine(self, pt, lineGeom): ptGeom = QgsGeometry.fromPointXY(pt) dist = ptGeom.distance(lineGeom.nearestPoint(ptGeom)) QgsMessageLog.logMessage("In pointOnLine. Dist " + str(dist), tag="TOMs panel") if dist > 0.001: return False return True def findLinkContainingLine(self, pt1, pt2): QgsMessageLog.logMessage("In findLinkContainingLine::pt1 " + pt1.asWkt() + " pt2: " + pt2.asWkt(), tag="TOMs panel") lineGeom = QgsGeometry.fromPolylineXY([pt1, pt2]) request = QgsFeatureRequest().setFilterRect(QgsRectangle(pt1, pt2)) for feature in self.linkLayer.getFeatures(request): intersectGeom = lineGeom.intersection(feature.geometry()) QgsMessageLog.logMessage( "In findLinkContainingLine:: intersectGeom " + intersectGeom.asWkt(), tag="TOMs panel") if intersectGeom.type() == QgsWkbTypes.LineGeometry: # this is our next feature return feature QgsMessageLog.logMessage( "In findLinkContainingLine: ERROR. No link found containing line.", tag="TOMs panel") return None def showShortestPath(self, startPoint, endPoint): # taken from Qgis Py Cookbook #startPoint = self.ptList[0][0].asPoint() #endPoint = self.ptList[1][0].asPoint() tiedPoints = self.director.makeGraph(self.builder, [startPoint, endPoint]) tStart, tStop = tiedPoints graph = self.builder.graph() idxStart = graph.findVertex(tStart) tree = QgsGraphAnalyzer.shortestTree(graph, idxStart, 0) idxStart = tree.findVertex(tStart) idxEnd = tree.findVertex(tStop) if idxEnd == -1: raise Exception('No route!') # Add last point route = [tree.vertex(idxEnd).point()] # Iterate the graph while idxEnd != idxStart: edgeIds = tree.vertex(idxEnd).incomingEdges() if len(edgeIds) == 0: break edge = tree.edge(edgeIds[0]) route.insert(0, tree.vertex(edge.fromVertex()).point()) idxEnd = edge.fromVertex() # This may require coordinate transformation if project's CRS # is different than layer's CRS for p in route: self.rbShortPath.addPoint(p) return route def generateAccessRestrictionForm(self): QgsMessageLog.logMessage( "In generateAccessRestrictionForm::generateForm ... ", tag="TOMs panel") layout = QFormLayout() #groupBox = QGroupBox("Restriction Attributes", self.currDialog) #formLayout = QFormLayout() # Add access restriction type self.cb_accessRestrictionType = QComboBox(self) enumList = self.getEnumList('AccessRestrictionValue') self.cb_accessRestrictionType.addItems(enumList) layout.addRow(self.tr("&Access Restriction Type:"), self.cb_accessRestrictionType) # Add vehicle exemption self.cb_accessRestrictionVehicleExemptions = QComboBox(self) enumList = self.getTableList('"moving_traffic"."vehicleQualifiers"') self.cb_accessRestrictionVehicleExemptions.addItems(enumList) layout.addRow(self.tr("&Vehicle exemptions:"), self.cb_accessRestrictionVehicleExemptions) # Add vehicle inclusions self.cb_accessRestrictionVehicleInclusions = QComboBox(self) enumList = self.getTableList('"moving_traffic"."vehicleQualifiers"') self.cb_accessRestrictionVehicleInclusions.addItems(enumList) layout.addRow(self.tr("&Vehicle inclusions:"), self.cb_accessRestrictionVehicleInclusions) # add time intervals self.cb_accessRestrictionTimePeriods = QComboBox(self) enumList = self.getTableList('"moving_traffic"."TimePeriods"') self.cb_accessRestrictionTimePeriods.addItems(enumList) layout.addRow(self.tr("&Time Period:"), self.cb_accessRestrictionTimePeriods) # add traffic sign """self.cb_trafficSign = QComboBox(self) enumList = self.getTableList('Signs') self.cb_timePeriods.addItems(enumList) layout.addRow(self.tr("&Traffic Sign:"), self.cb_timePeriods)""" self.accessRestrictionAttributeStack.setLayout(layout) # set up network reference capture geomLayout = QFormLayout() self.btn_PointReference = QPushButton("Location") self.btn_PointReference.clicked.connect(self.getPointReference) geomLayout.addRow(self.tr("&Access Restriction Location:"), self.btn_PointReference) # add link direction self.cb_accessRestrictionLinkDirectionValue = QComboBox(self) enumList = self.getEnumList('LinkDirectionValue') self.cb_accessRestrictionLinkDirectionValue.addItems(enumList) geomLayout.addRow(self.tr("&Applicable link direction:"), self.cb_accessRestrictionLinkDirectionValue) self.accessRestrictionGeometryStack.setLayout(geomLayout) # create relevant features """ self.accessRestrictionFeature = QgsFeature(self.accessRestrictionLayer) self.accessRestrictionNetworkReference = QgsFeature(self.pointReferenceLayer) # link them together with a uuid?? # self.accessRestrictionFeature """ def generateTurnRestrictionForm(self): QgsMessageLog.logMessage( "In generateTurnRestrictionForm::generateForm ... ", tag="TOMs panel") layout = QFormLayout() # Add turn restriction type self.cb_turnRestrictionType = QComboBox(self) enumList = self.getEnumList('TurnRestrictionValue') self.cb_turnRestrictionType.addItems(enumList) layout.addRow(self.tr("&Turn Restriction Type:"), self.cb_turnRestrictionType) # Add vehicle exemption self.cb_turnRestrictionVehicleExemptions = QComboBox(self) enumList = self.getTableList('"moving_traffic"."vehicleQualifiers"') self.cb_turnRestrictionVehicleExemptions.addItems(enumList) layout.addRow(self.tr("&Vehicle exemptions:"), self.cb_turnRestrictionVehicleExemptions) # Add vehicle inclusions self.cb_turnRestrictionVehicleInclusions = QComboBox(self) enumList = self.getTableList('"moving_traffic"."vehicleQualifiers"') self.cb_turnRestrictionVehicleInclusions.addItems(enumList) layout.addRow(self.tr("&Vehicle inclusions:"), self.cb_turnRestrictionVehicleInclusions) # add time intervals self.cb_turnRestrictionTimePeriods = QComboBox(self) enumList = self.getTableList('"moving_traffic"."TimePeriods"') self.cb_turnRestrictionTimePeriods.addItems(enumList) layout.addRow(self.tr("&Time Period:"), self.cb_turnRestrictionTimePeriods) self.turnRestrictionAttributeStack.setLayout(layout) # set up network reference capture geomLayout = QFormLayout() self.btn_StartReference = QPushButton("Start") self.btn_EndReference = QPushButton("End") self.btn_StartReference.clicked.connect(self.getLinkReferenceFirst) self.btn_EndReference.clicked.connect(self.getLinkReference) geomLayout.addRow(self.tr("&Turn Restriction Start:"), self.btn_StartReference) geomLayout.addRow(self.tr("&Turn Restriction End:"), self.btn_EndReference) self.turnRestrictionGeometryStack.setLayout(geomLayout) # create relevant features """ self.turnRestrictionFeature = QgsFeature(self.turnRestrictionLayer) self.turnRestrictionNetworkReference = QgsFeature(self.linkReferenceLayer) # link them together with a uuid?? # self.accessRestrictionFeature """ def generateHighwayDedicationForm(self): QgsMessageLog.logMessage( "In generateHighwayDedicationForm::generateForm ... ", tag="TOMs panel") layout = QFormLayout() # Add turn restriction type self.cb_dedicationValue = QComboBox(self) enumList = self.getEnumList('DedicationValue') self.cb_dedicationValue.addItems(enumList) layout.addRow(self.tr("&Dedication:"), self.cb_dedicationValue) self.highwayDedicationAttributeStack.setLayout(layout) # set up network reference capture geomLayout = QFormLayout() self.btn_StartReference = QPushButton("Start") self.btn_EndReference = QPushButton("End") self.btn_StartReference.clicked.connect(self.getLinkReferenceFirst) self.btn_EndReference.clicked.connect(self.getLinkReference) geomLayout.addRow(self.tr("&Highway Dedication Start:"), self.btn_StartReference) geomLayout.addRow(self.tr("&Highway Dedication End:"), self.btn_EndReference) self.highwayDedicationGeometryStack.setLayout(geomLayout) # create relevant features """ self.turnRestrictionFeature = QgsFeature(self.turnRestrictionLayer) self.turnRestrictionNetworkReference = QgsFeature(self.linkReferenceLayer) # link them together with a uuid?? # self.accessRestrictionFeature """ def generateRestrictionForVehiclesForm(self): QgsMessageLog.logMessage( "In generateRestrictionForVehiclesForm::generateForm ... ", tag="TOMs panel") layout = QFormLayout() # Add restriction for vehicles type self.cb_restrictionForVehiclesType = QComboBox(self) enumList = self.getEnumList('RestrictionTypeValue') self.cb_restrictionForVehiclesType.addItems(enumList) layout.addRow(self.tr("&Access Restriction Type:"), self.cb_restrictionForVehiclesType) # add measure self.le_restrictionForVehiclesMeasure = QLineEdit() self.le_restrictionForVehiclesMeasure.setValidator( QDoubleValidator(0.99, 999.99, 2)) layout.addRow(self.tr("&Measure (in metric units):"), self.le_restrictionForVehiclesMeasure) # add measure self.le_restrictionForVehiclesMeasure2 = QLineEdit() self.le_restrictionForVehiclesMeasure2.setValidator( QDoubleValidator(0.99, 999.99, 2)) layout.addRow( self.tr("&Measure (in imperial units) [only if present]:"), self.le_restrictionForVehiclesMeasure2) # Add vehicle exemption self.cb_restrictionForVehiclesVehicleExemptions = QComboBox(self) enumList = self.getTableList('"moving_traffic"."vehicleQualifiers"') self.cb_restrictionForVehiclesVehicleExemptions.addItems(enumList) layout.addRow(self.tr("&Vehicle exemptions:"), self.cb_restrictionForVehiclesVehicleExemptions) # Add vehicle inclusions self.cb_restrictionForVehiclesVehicleInclusions = QComboBox(self) enumList = self.getTableList('"moving_traffic"."vehicleQualifiers"') self.cb_restrictionForVehiclesVehicleInclusions.addItems(enumList) layout.addRow(self.tr("&Vehicle inclusions:"), self.cb_restrictionForVehiclesVehicleInclusions) # add structure type self.cb_restrictionForVehiclesStructureType = QComboBox(self) enumList = self.getEnumList('StructureTypeValue') self.cb_restrictionForVehiclesStructureType.addItems(enumList) layout.addRow(self.tr("&Structure Type:"), self.cb_restrictionForVehiclesStructureType) # add traffic sign """self.cb_trafficSign = QComboBox(self) enumList = self.getTableList('Signs') self.cb_timePeriods.addItems(enumList) layout.addRow(self.tr("&Traffic Sign:"), self.cb_timePeriods)""" self.restrictionForVehiclesAttributeStack.setLayout(layout) geomLayout = QFormLayout() self.btn_PointReference = QPushButton("Location") self.btn_PointReference.clicked.connect(self.getPointReference) geomLayout.addRow(self.tr("&Restriction For Vehicle Location:"), self.btn_PointReference) # add link direction self.cb_restrictionForVehiclesLinkDirectionValue = QComboBox(self) enumList = self.getEnumList('LinkDirectionValue') self.cb_restrictionForVehiclesLinkDirectionValue.addItems(enumList) geomLayout.addRow(self.tr("&Applicable link direction:"), self.cb_restrictionForVehiclesLinkDirectionValue) self.restrictionForVehiclesGeometryStack.setLayout(geomLayout) # create relevant features """ self.accessRestrictionFeature = QgsFeature(self.accessRestrictionLayer) self.accessRestrictionNetworkReference = QgsFeature(self.pointReferenceLayer) # link them together with a uuid?? # self.accessRestrictionFeature """ def getDbConnection(self): # http://pyqgis.org/blog/2013/04/11/creating-a-postgresql-connection-from-a-qgis-layer-datasource/ # get the active layer dbConn = None #layer = self.iface.activeLayer() # TODO: use a layer know to be using the database # get the underlying data provider provider = self.linkLayer.dataProvider() if provider.name() == 'postgres': # get the URI containing the connection parameters uri = QgsDataSourceUri(provider.dataSourceUri()) QgsMessageLog.logMessage( "In captureGPSFeatures::getDbConnection. db URI :" + uri.uri(), tag="TOMs panel") dbConn = psycopg2.connect(uri.connectionInfo()) return dbConn def getEnumList(self, enum): typeList = [ '', ] query = 'SELECT unnest(enum_range(NULL::"{}"))::text'.format(enum) QgsMessageLog.logMessage("In generateMTRForm::getEnumList. query is " + query, tag="TOMs panel") cursor = self.dbConn.cursor() cursor.execute(query) result = cursor.fetchall() for value, in result: typeList.append(value) return typeList def getTableList(self, table): typeList = [ '', ] query = 'SELECT "Description" FROM {}'.format(table) QgsMessageLog.logMessage( "In generateMTRForm::getTableList. query is " + query, tag="TOMs panel") cursor = self.dbConn.cursor() cursor.execute(query) result = cursor.fetchall() for value, in result: typeList.append(value) return typeList
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) endPoint = self.parameterAsPoint(parameters, self.END_POINT, 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 start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) request.setDestinationCrs(network.sourceCrs()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount( ) else 0 points = [endPoint] 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() idxEnd = graph.findVertex(snappedPoints[0]) route = [] nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, count + 1): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) tree, cost = 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.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[i]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = points[i].toString() feat['end'] = endPoint.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) startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs()) 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 = endPoints.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()) 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(self.tr('Loading end points...')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = endPoints.getFeatures(request) total = 100.0 / endPoints.featureCount() if endPoints.featureCount( ) else 0 points = [startPoint] 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(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, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, nPoints): 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.reportError(msg) # add feature with no geometry feat.clearGeometry() attrs = source_attributes[i] attrs.extend([NULL, 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) attrs = source_attributes[i] attrs.extend([ startPoint.toString(), points[i].toString(), cost / multiplier ]) feat.setAttributes(attrs) feat.setGeometry(geom) sink.addFeature(feat, QgsFeatureSink.FastInsert) 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)) 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) 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(QCoreApplication.translate('ServiceAreaFromLayer', '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 = [] 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(QCoreApplication.translate('ServiceAreaFromLayer', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', '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, 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, feedback): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) startPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.START_POINTS)) 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)) feat = QgsFeature() feat.setFields(fields) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields.toList(), QgsWkbTypes.LineString, layer.crs()) 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('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = vector.features(startPoints, request) count = len(features) points = [endPoint] 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() idxEnd = graph.findVertex(snappedPoints[0]) route = [] total = 100.0 / count for i in range(1, count + 1): idxStart = graph.findVertex(snappedPoints[i]) tree, cost = 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.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[i]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = points[i].toString() feat['end'] = endPoint.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)) 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 processAlgorithm(self, feedback): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) 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 = 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) 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()) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) 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()) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) startPoint = self.getParameterValue(self.START_POINT) endPoints = dataobjects.getObjectFromUri( 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(), iface.mapCanvas().hasCrsTransformEnabled(), tolerance) progress.setInfo(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()) progress.setInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) progress.setInfo(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())) progress.setText(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[:] = [] progress.setPercentage(int(i * total)) del writer
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoint = self.parameterAsPoint(parameters, self.START_POINT, 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) 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(context.project().crs(), 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]).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) (sinkPoints, pointsId) = self.parameterAsSink(parameters, self.OUTPUT_POINTS, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) (sinkPolygon, polygonId) = self.parameterAsSink(parameters, self.OUTPUT_POLYGON, context, fields, QgsWkbTypes.Polygon, network.sourceCrs()) results = {} if sinkPoints: feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert) upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) results[self.OUTPUT_POINTS] = pointsId if sinkPolygon: geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = startPoint.toString() sinkPolygon.addFeature(feat, QgsFeatureSink.FastInsert) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = startPoint.toString() sinkPolygon.addFeature(feat, QgsFeatureSink.FastInsert) results[self.OUTPUT_POLYGON] = polygonId return results
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
# Get index of cost field network_fields = network.pendingFields() network_cost_index = network_fields.indexFromName(cost_field) # Setting up graph build director director = QgsLineVectorLayerDirector(network, -1, '', '', '', 3) # Determining cost calculation if cost_field != 'length': properter = CustomCost(network_cost_index, 0.01) else: properter = QgsDistanceArcProperter() # Creating graph builder director.addProperter(properter) 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