Skip to content
Permalink
Browse files

Merge pull request #4869 from alexbruy/network-analysis

[processing] restore network analysis algorithms
  • Loading branch information
alexbruy committed Jul 17, 2017
2 parents 6c0cb2f + f3f74a9 commit e61daed979fe5137cb2455be6d4a36e9706eb535
@@ -87,6 +87,11 @@
from .SaveSelectedFeatures import SaveSelectedFeatures
from .SelectByAttribute import SelectByAttribute
from .SelectByExpression import SelectByExpression
from .ServiceAreaFromLayer import ServiceAreaFromLayer
from .ServiceAreaFromPoint import ServiceAreaFromPoint
from .ShortestPathLayerToPoint import ShortestPathLayerToPoint
from .ShortestPathPointToLayer import ShortestPathPointToLayer
from .ShortestPathPointToPoint import ShortestPathPointToPoint
from .SimplifyGeometries import SimplifyGeometries
from .Slope import Slope
from .Smooth import Smooth
@@ -158,11 +163,6 @@
# from .ExtractSpecificNodes import ExtractSpecificNodes
# from .GeometryByExpression import GeometryByExpression
# from .RasterCalculator import RasterCalculator
# from .ShortestPathPointToPoint import ShortestPathPointToPoint
# from .ShortestPathPointToLayer import ShortestPathPointToLayer
# from .ShortestPathLayerToPoint import ShortestPathLayerToPoint
# from .ServiceAreaFromPoint import ServiceAreaFromPoint
# from .ServiceAreaFromLayer import ServiceAreaFromLayer
# from .TruncateTable import TruncateTable
# from .Polygonize import Polygonize
# from .ExecuteSQL import ExecuteSQL
@@ -274,6 +274,11 @@ def getAlgs(self):
SaveSelectedFeatures(),
SelectByAttribute(),
SelectByExpression(),
ServiceAreaFromLayer(),
ServiceAreaFromPoint(),
ShortestPathLayerToPoint(),
ShortestPathPointToLayer(),
ShortestPathPointToPoint(),
SimplifyGeometries(),
Slope(),
Smooth(),
@@ -36,10 +36,16 @@
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsField,
QgsFields,
QgsFeatureRequest,
QgsProcessingUtils,
QgsField,
QgsProcessing,
QgsProcessingParameterEnum,
QgsProcessingParameterPoint,
QgsProcessingParameterField,
QgsProcessingParameterNumber,
QgsProcessingParameterString,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterDefinition)
from qgis.analysis import (QgsVectorLayerDirector,
QgsNetworkDistanceStrategy,
@@ -50,21 +56,13 @@
from qgis.utils import iface

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import (ParameterVector,
ParameterNumber,
ParameterString,
ParameterTableField,
ParameterSelection
)
from processing.core.outputs import OutputVector
from processing.tools import dataobjects

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]


class ServiceAreaFromLayer(QgisAlgorithm):

INPUT_VECTOR = 'INPUT_VECTOR'
INPUT = 'INPUT'
START_POINTS = 'START_POINTS'
STRATEGY = 'STRATEGY'
TRAVEL_COST = 'TRAVEL_COST'
@@ -98,62 +96,66 @@ def initAlgorithm(self, config=None):
self.tr('Fastest')
]

self.addParameter(ParameterVector(self.INPUT_VECTOR,
self.tr('Vector layer representing network'),
[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterVector(self.START_POINTS,
self.tr('Vector layer with start points'),
[dataobjects.TYPE_VECTOR_POINT]))
self.addParameter(ParameterSelection(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
default=0))
self.addParameter(ParameterNumber(self.TRAVEL_COST,
self.tr('Travel cost (distance for "Shortest", time for "Fastest")'),
0.0, 99999999.999999, 0.0))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Vector layer representing network'),
[QgsProcessing.TypeVectorLine]))
self.addParameter(QgsProcessingParameterFeatureSource(self.START_POINTS,
self.tr('Vector layer with start points'),
[QgsProcessing.TypeVectorPoint]))
self.addParameter(QgsProcessingParameterEnum(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
defaultValue=0))
self.addParameter(QgsProcessingParameterNumber(self.TRAVEL_COST,
self.tr('Travel cost (distance for "Shortest", time for "Fastest")'),
QgsProcessingParameterNumber.Double,
0.0, False, 0, 99999999.99))

params = []
params.append(ParameterTableField(self.DIRECTION_FIELD,
self.tr('Direction field'),
self.INPUT_VECTOR,
optional=True))
params.append(ParameterString(self.VALUE_FORWARD,
self.tr('Value for forward direction'),
'',
optional=True))
params.append(ParameterString(self.VALUE_BACKWARD,
self.tr('Value for backward direction'),
'',
optional=True))
params.append(ParameterString(self.VALUE_BOTH,
self.tr('Value for both directions'),
'',
optional=True))
params.append(ParameterSelection(self.DEFAULT_DIRECTION,
self.tr('Default direction'),
list(self.DIRECTIONS.keys()),
default=2))
params.append(ParameterTableField(self.SPEED_FIELD,
self.tr('Speed field'),
self.INPUT_VECTOR,
optional=True))
params.append(ParameterNumber(self.DEFAULT_SPEED,
self.tr('Default speed (km/h)'),
0.0, 99999999.999999, 5.0))
params.append(ParameterNumber(self.TOLERANCE,
self.tr('Topology tolerance'),
0.0, 99999999.999999, 0.0))
params.append(QgsProcessingParameterField(self.DIRECTION_FIELD,
self.tr('Direction field'),
None,
self.INPUT,
optional=True))
params.append(QgsProcessingParameterString(self.VALUE_FORWARD,
self.tr('Value for forward direction'),
optional=True))
params.append(QgsProcessingParameterString(self.VALUE_BACKWARD,
self.tr('Value for backward direction'),
optional=True))
params.append(QgsProcessingParameterString(self.VALUE_BOTH,
self.tr('Value for both directions'),
optional=True))
params.append(QgsProcessingParameterEnum(self.DEFAULT_DIRECTION,
self.tr('Default direction'),
list(self.DIRECTIONS.keys()),
defaultValue=2))
params.append(QgsProcessingParameterField(self.SPEED_FIELD,
self.tr('Speed field'),
None,
self.INPUT,
optional=True))
params.append(QgsProcessingParameterNumber(self.DEFAULT_SPEED,
self.tr('Default speed (km/h)'),
QgsProcessingParameterNumber.Double,
5.0, False, 0, 99999999.99))
params.append(QgsProcessingParameterNumber(self.TOLERANCE,
self.tr('Topology tolerance'),
QgsProcessingParameterNumber.Double,
0.0, False, 0, 99999999.99))

for p in params:
p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
self.addParameter(p)

self.addOutput(OutputVector(self.OUTPUT_POINTS,
self.tr('Service area (boundary nodes)'),
datatype=[dataobjects.TYPE_VECTOR_POINT]))
self.addOutput(OutputVector(self.OUTPUT_POLYGON,
self.tr('Service area (convex hull)'),
datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_POINTS,
self.tr('Service area (boundary nodes)'),
QgsProcessing.TypeVectorPoint,
optional=True))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_POLYGON,
self.tr('Service area (convex hull)'),
QgsProcessing.TypeVectorPolygon,
optional=True))

def name(self):
return 'serviceareafromlayer'
@@ -162,21 +164,19 @@ def displayName(self):
return self.tr('Service area (from layer)')

def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context)
startPoints = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.START_POINTS), context)
strategy = self.getParameterValue(self.STRATEGY)
travelCost = self.getParameterValue(self.TRAVEL_COST)

directionFieldName = self.getParameterValue(self.DIRECTION_FIELD)
forwardValue = self.getParameterValue(self.VALUE_FORWARD)
backwardValue = self.getParameterValue(self.VALUE_BACKWARD)
bothValue = self.getParameterValue(self.VALUE_BOTH)
defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION)
bothValue = self.getParameterValue(self.VALUE_BOTH)
defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION)
speedFieldName = self.getParameterValue(self.SPEED_FIELD)
defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED)
tolerance = self.getParameterValue(self.TOLERANCE)
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 = QgsFields()
fields.append(QgsField('type', QVariant.String, '', 254, 0))
@@ -185,20 +185,14 @@ def processAlgorithm(self, parameters, context, feedback):
feat = QgsFeature()
feat.setFields(fields)

writerPoints = self.getOutputFromName(
self.OUTPUT_POINTS).getVectorWriter(fields, QgsWkbTypes.MultiPoint, layer.crs(), context)

writerPolygons = self.getOutputFromName(
self.OUTPUT_POLYGON).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context)

directionField = -1
if directionFieldName is not None:
directionField = layer.fields().lookupField(directionFieldName)
if directionFieldName:
directionField = network.fields().lookupField(directionFieldName)
speedField = -1
if speedFieldName is not None:
speedField = layer.fields().lookupField(speedFieldName)
if speedFieldName:
speedField = network.fields().lookupField(speedFieldName)

director = QgsVectorLayerDirector(layer,
director = QgsVectorLayerDirector(network,
directionField,
forwardValue,
backwardValue,
@@ -222,22 +216,44 @@ def processAlgorithm(self, parameters, context, feedback):
feedback.pushInfo(self.tr('Loading start points...'))
request = QgsFeatureRequest()
request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes)
features = QgsProcessingUtils.getFeatures(startPoints, context, request)
request.setDestinationCrs(network.sourceCrs())
features = startPoints.getFeatures(request)
total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0

points = []
for f in features:
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)
snappedPoints = director.makeGraph(builder, points, feedback)

feedback.pushInfo(self.tr('Calculating service areas...'))
graph = builder.graph()

results = {}
(sinkPoints, pointsId) = self.parameterAsSink(parameters, self.OUTPUT_POINTS, context,
fields, QgsWkbTypes.MultiPoint, network.sourceCrs())

(sinkPolygon, polygonId) = self.parameterAsSink(parameters, self.OUTPUT_POLYGON, context,
fields, QgsWkbTypes.Polygon, network.sourceCrs())

if sinkPoints:
results[self.OUTPUT_POINTS] = pointsId
if sinkPolygon:
results[self.OUTPUT_POLYGON] = polygonId

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

@@ -252,41 +268,42 @@ def processAlgorithm(self, parameters, context, feedback):
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, QgsFeatureSink.FastInsert)

feat.setGeometry(geomLower)
feat['type'] = 'lower'
feat['start'] = origPoint
writerPoints.addFeature(feat, QgsFeatureSink.FastInsert)

upperBoundary.append(origPoint)
lowerBoundary.append(origPoint)
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)

geom = geomUpper.convexHull()
feat.setGeometry(geom)
feat['type'] = 'upper'
feat['start'] = origPoint
writerPolygons.addFeature(feat, QgsFeatureSink.FastInsert)

geom = geomLower.convexHull()
feat.setGeometry(geom)
feat['type'] = 'lower'
feat['start'] = origPoint
writerPolygons.addFeature(feat, QgsFeatureSink.FastInsert)
if sinkPoints:
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)

feat.setGeometry(geomUpper)
feat['type'] = 'upper'
feat['start'] = origPoint
sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert)

feat.setGeometry(geomLower)
feat['type'] = 'lower'
feat['start'] = origPoint
sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert)

if sinkPolygon:
upperBoundary.append(origPoint)
lowerBoundary.append(origPoint)
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)

geom = geomUpper.convexHull()
feat.setGeometry(geom)
feat['type'] = 'upper'
feat['start'] = origPoint
sinkPolygon.addFeature(feat, QgsFeatureSink.FastInsert)

geom = geomLower.convexHull()
feat.setGeometry(geom)
feat['type'] = 'lower'
feat['start'] = origPoint
sinkPolygon.addFeature(feat, QgsFeatureSink.FastInsert)

vertices[:] = []
upperBoundary[:] = []
lowerBoundary[:] = []

feedback.setProgress(int(i * total))

del writerPoints
del writerPolygons
return results

0 comments on commit e61daed

Please sign in to comment.
You can’t perform that action at this time.