Exemple #1
0
    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
Exemple #2
0
    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
Exemple #3
0
    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)
        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, 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)
        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, 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
Exemple #8
0
    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
Exemple #9
0
    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}
Exemple #10
0
    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
Exemple #11
0
    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")
Exemple #12
0
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
Exemple #13
0
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
Exemple #14
0
    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