Merge pull request #4869 from alexbruy/network-analysis

[processing] restore network analysis algorithms
This commit is contained in:
Alexander Bruy 2017-07-17 12:45:16 +03:00 committed by GitHub
commit e61daed979
6 changed files with 535 additions and 510 deletions

View File

@ -87,6 +87,11 @@ from .Ruggedness import Ruggedness
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 .ZonalStatistics import ZonalStatistics
# 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 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
SaveSelectedFeatures(),
SelectByAttribute(),
SelectByExpression(),
ServiceAreaFromLayer(),
ServiceAreaFromPoint(),
ShortestPathLayerToPoint(),
ShortestPathPointToLayer(),
ShortestPathPointToPoint(),
SimplifyGeometries(),
Slope(),
Smooth(),

View File

@ -36,10 +36,16 @@ from qgis.core import (QgsWkbTypes,
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.analysis import (QgsVectorLayerDirector,
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 @@ class ServiceAreaFromLayer(QgisAlgorithm):
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 @@ class ServiceAreaFromLayer(QgisAlgorithm):
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)
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.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)
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 @@ class ServiceAreaFromLayer(QgisAlgorithm):
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 @@ class ServiceAreaFromLayer(QgisAlgorithm):
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,35 +268,37 @@ class ServiceAreaFromLayer(QgisAlgorithm):
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)
if sinkPoints:
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)
feat.setGeometry(geomUpper)
feat['type'] = 'upper'
feat['start'] = origPoint
writerPoints.addFeature(feat, QgsFeatureSink.FastInsert)
feat.setGeometry(geomUpper)
feat['type'] = 'upper'
feat['start'] = origPoint
sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert)
feat.setGeometry(geomLower)
feat['type'] = 'lower'
feat['start'] = origPoint
writerPoints.addFeature(feat, QgsFeatureSink.FastInsert)
feat.setGeometry(geomLower)
feat['type'] = 'lower'
feat['start'] = origPoint
sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert)
upperBoundary.append(origPoint)
lowerBoundary.append(origPoint)
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)
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
writerPolygons.addFeature(feat, QgsFeatureSink.FastInsert)
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
writerPolygons.addFeature(feat, QgsFeatureSink.FastInsert)
geom = geomLower.convexHull()
feat.setGeometry(geom)
feat['type'] = 'lower'
feat['start'] = origPoint
sinkPolygon.addFeature(feat, QgsFeatureSink.FastInsert)
vertices[:] = []
upperBoundary[:] = []
@ -288,5 +306,4 @@ class ServiceAreaFromLayer(QgisAlgorithm):
feedback.setProgress(int(i * total))
del writerPoints
del writerPolygons
return results

View File

@ -36,11 +36,17 @@ from qgis.core import (QgsWkbTypes,
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsPointXY,
QgsField,
QgsFields,
QgsProcessingParameterDefinition,
QgsProcessingUtils)
QgsField,
QgsProcessing,
QgsProcessingParameterEnum,
QgsProcessingParameterPoint,
QgsProcessingParameterField,
QgsProcessingParameterNumber,
QgsProcessingParameterString,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterDefinition)
from qgis.analysis import (QgsVectorLayerDirector,
QgsNetworkDistanceStrategy,
QgsNetworkSpeedStrategy,
@ -50,22 +56,13 @@ from qgis.analysis import (QgsVectorLayerDirector,
from qgis.utils import iface
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import (ParameterVector,
ParameterPoint,
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 ServiceAreaFromPoint(QgisAlgorithm):
INPUT_VECTOR = 'INPUT_VECTOR'
INPUT = 'INPUT'
START_POINT = 'START_POINT'
STRATEGY = 'STRATEGY'
TRAVEL_COST = 'TRAVEL_COST'
@ -99,61 +96,65 @@ class ServiceAreaFromPoint(QgisAlgorithm):
self.tr('Fastest')
]
self.addParameter(ParameterVector(self.INPUT_VECTOR,
self.tr('Vector layer representing network'),
[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterPoint(self.START_POINT,
self.tr('Start 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(QgsProcessingParameterPoint(self.START_POINT,
self.tr('Start point')))
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 'serviceareafrompoint'
@ -162,33 +163,28 @@ class ServiceAreaFromPoint(QgisAlgorithm):
return self.tr('Service area (from point)')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context)
startPoint = self.getParameterValue(self.START_POINT)
strategy = self.getParameterValue(self.STRATEGY)
travelCost = self.getParameterValue(self.TRAVEL_COST)
network = self.parameterAsSource(parameters, self.INPUT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context)
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context)
directionFieldName = self.getParameterValue(self.DIRECTION_FIELD)
forwardValue = self.getParameterValue(self.VALUE_FORWARD)
backwardValue = self.getParameterValue(self.VALUE_BACKWARD)
bothValue = self.getParameterValue(self.VALUE_BOTH)
defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION)
bothValue = self.getParameterValue(self.VALUE_BOTH)
defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION)
speedFieldName = self.getParameterValue(self.SPEED_FIELD)
defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED)
tolerance = self.getParameterValue(self.TOLERANCE)
tmp = startPoint.split(',')
startPoint = QgsPointXY(float(tmp[0]), float(tmp[1]))
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 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,
@ -209,7 +205,7 @@ class ServiceAreaFromPoint(QgisAlgorithm):
True,
tolerance)
feedback.pushInfo(self.tr('Building graph...'))
snappedPoints = director.makeGraph(builder, [startPoint])
snappedPoints = director.makeGraph(builder, [startPoint], feedback)
feedback.pushInfo(self.tr('Calculating service area...'))
graph = builder.graph()
@ -241,38 +237,43 @@ class ServiceAreaFromPoint(QgisAlgorithm):
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)
writer = self.getOutputFromName(
self.OUTPUT_POINTS).getVectorWriter(fields, QgsWkbTypes.MultiPoint, layer.crs(), context)
(sinkPoints, pointsId) = self.parameterAsSink(parameters, self.OUTPUT_POINTS, context,
fields, QgsWkbTypes.MultiPoint, network.sourceCrs())
feat.setGeometry(geomUpper)
feat['type'] = 'upper'
feat['start'] = startPoint.toString()
writer.addFeature(feat, QgsFeatureSink.FastInsert)
(sinkPolygon, polygonId) = self.parameterAsSink(parameters, self.OUTPUT_POLYGON, context,
fields, QgsWkbTypes.Polygon, network.sourceCrs())
results = {}
if sinkPoints:
feat.setGeometry(geomUpper)
feat['type'] = 'upper'
feat['start'] = startPoint.toString()
sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert)
feat.setGeometry(geomLower)
feat['type'] = 'lower'
feat['start'] = startPoint.toString()
writer.addFeature(feat, QgsFeatureSink.FastInsert)
feat.setGeometry(geomLower)
feat['type'] = 'lower'
feat['start'] = startPoint.toString()
sinkPoints.addFeature(feat, QgsFeatureSink.FastInsert)
del writer
upperBoundary.append(startPoint)
lowerBoundary.append(startPoint)
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)
upperBoundary.append(startPoint)
lowerBoundary.append(startPoint)
geomUpper = QgsGeometry.fromMultiPoint(upperBoundary)
geomLower = QgsGeometry.fromMultiPoint(lowerBoundary)
results[self.OUTPUT_POINTS] = pointsId
writer = self.getOutputFromName(
self.OUTPUT_POLYGON).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context)
if sinkPolygon:
geom = geomUpper.convexHull()
feat.setGeometry(geom)
feat['type'] = 'upper'
feat['start'] = startPoint.toString()
sinkPolygon.addFeature(feat, QgsFeatureSink.FastInsert)
geom = geomUpper.convexHull()
feat.setGeometry(geom)
feat['type'] = 'upper'
feat['start'] = startPoint.toString()
writer.addFeature(feat, QgsFeatureSink.FastInsert)
geom = geomLower.convexHull()
feat.setGeometry(geom)
feat['type'] = 'lower'
feat['start'] = startPoint.toString()
sinkPolygon.addFeature(feat, QgsFeatureSink.FastInsert)
geom = geomLower.convexHull()
feat.setGeometry(geom)
feat['type'] = 'lower'
feat['start'] = startPoint.toString()
writer.addFeature(feat, QgsFeatureSink.FastInsert)
del writer
results[self.OUTPUT_POLYGON] = polygonId
return results

View File

@ -36,13 +36,18 @@ from qgis.core import (QgsWkbTypes,
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsPointXY,
QgsFields,
QgsField,
QgsFeatureRequest,
QgsMessageLog,
QgsProcessingParameterDefinition,
QgsProcessingUtils)
QgsProcessing,
QgsProcessingParameterEnum,
QgsProcessingParameterPoint,
QgsProcessingParameterField,
QgsProcessingParameterNumber,
QgsProcessingParameterString,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterDefinition)
from qgis.analysis import (QgsVectorLayerDirector,
QgsNetworkDistanceStrategy,
QgsNetworkSpeedStrategy,
@ -52,23 +57,14 @@ from qgis.analysis import (QgsVectorLayerDirector,
from qgis.utils import iface
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import (ParameterVector,
ParameterPoint,
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 ShortestPathLayerToPoint(QgisAlgorithm):
INPUT_VECTOR = 'INPUT_VECTOR'
START_POINTS = 'START_POINT'
INPUT = 'INPUT'
START_POINTS = 'START_POINTS'
END_POINT = 'END_POINT'
STRATEGY = 'STRATEGY'
DIRECTION_FIELD = 'DIRECTION_FIELD'
@ -79,7 +75,7 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
SPEED_FIELD = 'SPEED_FIELD'
DEFAULT_SPEED = 'DEFAULT_SPEED'
TOLERANCE = 'TOLERANCE'
OUTPUT_LAYER = 'OUTPUT_LAYER'
OUTPUT = 'OUTPUT'
def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'networkanalysis.svg'))
@ -100,58 +96,59 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
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(ParameterPoint(self.END_POINT,
self.tr('End point')))
self.addParameter(ParameterSelection(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
default=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(QgsProcessingParameterPoint(self.END_POINT,
self.tr('End point')))
self.addParameter(QgsProcessingParameterEnum(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
defaultValue=0))
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_LAYER,
self.tr('Shortest path'),
datatype=[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
self.tr('Shortest path'),
QgsProcessing.TypeVectorLine))
def name(self):
return 'shortestpathlayertopoint'
@ -160,21 +157,19 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
return self.tr('Shortest path (layer to point)')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context)
startPoints = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.START_POINTS), context)
endPoint = self.getParameterValue(self.END_POINT)
strategy = self.getParameterValue(self.STRATEGY)
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.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)
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))
@ -184,20 +179,17 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
feat = QgsFeature()
feat.setFields(fields)
writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.LineString, layer.crs(), context)
tmp = endPoint.split(',')
endPoint = QgsPointXY(float(tmp[0]), float(tmp[1]))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.LineString, network.sourceCrs())
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,15 +214,20 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
feedback.pushInfo(self.tr('Loading start points...'))
request = QgsFeatureRequest()
request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes)
features = QgsProcessingUtils.getFeatures(startPoints, context, request)
count = QgsProcessingUtils.featureCount(startPoints, context)
request.setDestinationCrs(network.sourceCrs())
features = startPoints.getFeatures(request)
total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0
points = [endPoint]
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 shortest paths...'))
graph = builder.graph()
@ -238,8 +235,12 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
idxEnd = graph.findVertex(snappedPoints[0])
route = []
total = 100.0 / count if count else 1
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)
@ -264,10 +265,10 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
feat['start'] = points[i].toString()
feat['end'] = endPoint.toString()
feat['cost'] = cost / multiplier
writer.addFeature(feat, QgsFeatureSink.FastInsert)
sink.addFeature(feat, QgsFeatureSink.FastInsert)
route[:] = []
feedback.setProgress(int(i * total))
del writer
return {self.OUTPUT: dest_id}

View File

@ -31,10 +31,23 @@ from collections import OrderedDict
from qgis.PyQt.QtCore import QVariant
from qgis.PyQt.QtGui import QIcon
from qgis.core import (QgsWkbTypes, QgsUnitTypes, QgsFeatureSink, QgsFeature, QgsGeometry, QgsPointXY, QgsFields, QgsField, QgsFeatureRequest,
from qgis.core import (QgsWkbTypes,
QgsUnitTypes,
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsFields,
QgsField,
QgsMessageLog,
QgsProcessingParameterDefinition,
QgsProcessingUtils)
QgsProcessing,
QgsProcessingParameterEnum,
QgsProcessingParameterPoint,
QgsProcessingParameterField,
QgsProcessingParameterNumber,
QgsProcessingParameterString,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterDefinition)
from qgis.analysis import (QgsVectorLayerDirector,
QgsNetworkDistanceStrategy,
QgsNetworkSpeedStrategy,
@ -44,22 +57,13 @@ from qgis.analysis import (QgsVectorLayerDirector,
from qgis.utils import iface
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import (ParameterVector,
ParameterPoint,
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 ShortestPathPointToLayer(QgisAlgorithm):
INPUT_VECTOR = 'INPUT_VECTOR'
INPUT = 'INPUT'
START_POINT = 'START_POINT'
END_POINTS = 'END_POINTS'
STRATEGY = 'STRATEGY'
@ -71,7 +75,7 @@ class ShortestPathPointToLayer(QgisAlgorithm):
SPEED_FIELD = 'SPEED_FIELD'
DEFAULT_SPEED = 'DEFAULT_SPEED'
TOLERANCE = 'TOLERANCE'
OUTPUT_LAYER = 'OUTPUT_LAYER'
OUTPUT = 'OUTPUT'
def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'networkanalysis.svg'))
@ -92,58 +96,59 @@ class ShortestPathPointToLayer(QgisAlgorithm):
self.tr('Fastest')
]
self.addParameter(ParameterVector(self.INPUT_VECTOR,
self.tr('Vector layer representing network'),
[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterPoint(self.START_POINT,
self.tr('Start point')))
self.addParameter(ParameterVector(self.END_POINTS,
self.tr('Vector layer with end points'),
[dataobjects.TYPE_VECTOR_POINT]))
self.addParameter(ParameterSelection(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
default=0))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Vector layer representing network'),
[QgsProcessing.TypeVectorLine]))
self.addParameter(QgsProcessingParameterPoint(self.START_POINT,
self.tr('Start point')))
self.addParameter(QgsProcessingParameterFeatureSource(self.END_POINTS,
self.tr('Vector layer with end points'),
[QgsProcessing.TypeVectorPoint]))
self.addParameter(QgsProcessingParameterEnum(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
defaultValue=0))
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_LAYER,
self.tr('Shortest path'),
datatype=[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
self.tr('Shortest path'),
QgsProcessing.TypeVectorLine))
def name(self):
return 'shortestpathpointtolayer'
@ -152,21 +157,19 @@ class ShortestPathPointToLayer(QgisAlgorithm):
return self.tr('Shortest path (point to layer)')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context)
startPoint = self.getParameterValue(self.START_POINT)
endPoints = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.END_POINTS), context)
strategy = self.getParameterValue(self.STRATEGY)
network = self.parameterAsSource(parameters, self.INPUT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context)
endPoints = self.parameterAsSource(parameters, self.END_POINTS, context)
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
directionFieldName = self.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)
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))
@ -176,20 +179,17 @@ class ShortestPathPointToLayer(QgisAlgorithm):
feat = QgsFeature()
feat.setFields(fields)
writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.LineString, layer.crs(), context)
tmp = startPoint.split(',')
startPoint = QgsPointXY(float(tmp[0]), float(tmp[1]))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.LineString, network.sourceCrs())
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,
@ -214,15 +214,20 @@ class ShortestPathPointToLayer(QgisAlgorithm):
feedback.pushInfo(self.tr('Loading end points...'))
request = QgsFeatureRequest()
request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes)
features = QgsProcessingUtils.getFeatures(endPoints, context, request)
count = QgsProcessingUtils.featureCount(endPoints, context)
request.setDestinationCrs(network.sourceCrs())
features = endPoints.getFeatures(request)
total = 100.0 / endPoints.featureCount() if endPoints.featureCount() else 0
points = [startPoint]
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 shortest paths...'))
graph = builder.graph()
@ -231,8 +236,12 @@ class ShortestPathPointToLayer(QgisAlgorithm):
tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0)
route = []
total = 100.0 / count if count else 1
nPoints = len(snappedPoints)
total = 100.0 / nPoints if nPoints else 1
for i in range(1, count + 1):
if feedback.isCanceled():
break
idxEnd = graph.findVertex(snappedPoints[i])
if tree[idxEnd] == -1:
@ -256,10 +265,10 @@ class ShortestPathPointToLayer(QgisAlgorithm):
feat['start'] = startPoint.toString()
feat['end'] = points[i].toString()
feat['cost'] = cost / multiplier
writer.addFeature(feat, QgsFeatureSink.FastInsert)
sink.addFeature(feat, QgsFeatureSink.FastInsert)
route[:] = []
feedback.setProgress(int(i * total))
del writer
return {self.OUTPUT: dest_id}

View File

@ -36,11 +36,19 @@ from qgis.core import (QgsWkbTypes,
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsPointXY,
QgsFields,
QgsField,
QgsProcessingParameterDefinition,
QgsProcessingUtils)
QgsProcessing,
QgsProcessingException,
QgsProcessingOutputNumber,
QgsProcessingParameterEnum,
QgsProcessingParameterPoint,
QgsProcessingParameterField,
QgsProcessingParameterNumber,
QgsProcessingParameterString,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterDefinition)
from qgis.analysis import (QgsVectorLayerDirector,
QgsNetworkDistanceStrategy,
QgsNetworkSpeedStrategy,
@ -50,25 +58,13 @@ from qgis.analysis import (QgsVectorLayerDirector,
from qgis.utils import iface
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import (ParameterVector,
ParameterPoint,
ParameterNumber,
ParameterString,
ParameterTableField,
ParameterSelection
)
from processing.core.outputs import (OutputNumber,
OutputVector
)
from processing.tools import dataobjects
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class ShortestPathPointToPoint(QgisAlgorithm):
INPUT_VECTOR = 'INPUT_VECTOR'
INPUT = 'INPUT'
START_POINT = 'START_POINT'
END_POINT = 'END_POINT'
STRATEGY = 'STRATEGY'
@ -81,7 +77,7 @@ class ShortestPathPointToPoint(QgisAlgorithm):
DEFAULT_SPEED = 'DEFAULT_SPEED'
TOLERANCE = 'TOLERANCE'
TRAVEL_COST = 'TRAVEL_COST'
OUTPUT_LAYER = 'OUTPUT_LAYER'
OUTPUT = 'OUTPUT'
def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'networkanalysis.svg'))
@ -102,59 +98,60 @@ class ShortestPathPointToPoint(QgisAlgorithm):
self.tr('Fastest')
]
self.addParameter(ParameterVector(self.INPUT_VECTOR,
self.tr('Vector layer representing network'),
[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterPoint(self.START_POINT,
self.tr('Start point')))
self.addParameter(ParameterPoint(self.END_POINT,
self.tr('End point')))
self.addParameter(ParameterSelection(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
default=0))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Vector layer representing network'),
[QgsProcessing.TypeVectorLine]))
self.addParameter(QgsProcessingParameterPoint(self.START_POINT,
self.tr('Start point')))
self.addParameter(QgsProcessingParameterPoint(self.END_POINT,
self.tr('End point')))
self.addParameter(QgsProcessingParameterEnum(self.STRATEGY,
self.tr('Path type to calculate'),
self.STRATEGIES,
defaultValue=0))
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(OutputNumber(self.TRAVEL_COST,
self.tr('Travel cost')))
self.addOutput(OutputVector(self.OUTPUT_LAYER,
self.tr('Shortest path'),
datatype=[dataobjects.TYPE_VECTOR_LINE]))
self.addOutput(QgsProcessingOutputNumber(self.TRAVEL_COST,
self.tr('Travel cost')))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
self.tr('Shortest path'),
QgsProcessing.TypeVectorLine))
def name(self):
return 'shortestpathpointtopoint'
@ -163,43 +160,36 @@ class ShortestPathPointToPoint(QgisAlgorithm):
return self.tr('Shortest path (point to point)')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context)
startPoint = self.getParameterValue(self.START_POINT)
endPoint = self.getParameterValue(self.END_POINT)
strategy = self.getParameterValue(self.STRATEGY)
network = self.parameterAsSource(parameters, self.INPUT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context)
endPoint = self.parameterAsPoint(parameters, self.END_POINT, context)
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
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)
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))
writer = self.getOutputFromName(
self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.LineString, layer.crs(), context)
tmp = startPoint.split(',')
startPoint = QgsPointXY(float(tmp[0]), float(tmp[1]))
tmp = endPoint.split(',')
endPoint = QgsPointXY(float(tmp[0]), float(tmp[1]))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.LineString, network.sourceCrs())
directionField = -1
if directionFieldName is not None:
directionField = layer.fields().lookupField(directionFieldName)
if directionField:
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,
@ -221,7 +211,7 @@ class ShortestPathPointToPoint(QgisAlgorithm):
True,
tolerance)
feedback.pushInfo(self.tr('Building graph...'))
snappedPoints = director.makeGraph(builder, [startPoint, endPoint])
snappedPoints = director.makeGraph(builder, [startPoint, endPoint], feedback)
feedback.pushInfo(self.tr('Calculating shortest path...'))
graph = builder.graph()
@ -230,7 +220,7 @@ class ShortestPathPointToPoint(QgisAlgorithm):
tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0)
if tree[idxEnd] == -1:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('There is no route from start point to end point.'))
route = []
@ -244,8 +234,6 @@ class ShortestPathPointToPoint(QgisAlgorithm):
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()
@ -254,5 +242,9 @@ class ShortestPathPointToPoint(QgisAlgorithm):
feat['end'] = endPoint.toString()
feat['cost'] = cost / multiplier
feat.setGeometry(geom)
writer.addFeature(feat, QgsFeatureSink.FastInsert)
del writer
sink.addFeature(feat, QgsFeatureSink.FastInsert)
results = {}
results[self.TRAVEL_COST] = cost / multiplier
results[self.OUTPUT] = dest_id
return results