mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[processing] Fixes for Service Area algorithms
- Output interpolated points when travel cost falls mid-way along an edge - Output all intermediate reachable points also - Make outputting upper/lower bound points optional, and non-default. Now by default we just output all definitely reachable points and the interpolated points along edges which correspond to the travel cost. This allows the output to be used to correctly generate service areas e.g. by concave/convex polygons and all reachable nodes will be included in the area. - Allow algorithm to optionally output a line layer (and make the point layer optional too, and default to just the line layer output) containing all reachable line segments (including interpolated segments of lines when the travel cost sits midway along that edge). This output is more easily understandably for users.
This commit is contained in:
parent
2e7455c180
commit
ccb72ebce2
@ -30,7 +30,8 @@ Solve shortest path problem using Dijkstra algorithm
|
||||
:param source: source graph
|
||||
:param startVertexIdx: index of the start vertex
|
||||
:param criterionNum: index of the optimization strategy
|
||||
:param resultTree: array that represents shortest path tree. resultTree[ vertexIndex ] == inboundingArcIndex if vertex reachable, otherwise resultTree[ vertexIndex ] == -1
|
||||
:param resultTree: array that represents shortest path tree. resultTree[ vertexIndex ] == inboundingArcIndex if vertex reachable, otherwise resultTree[ vertexIndex ] == -1.
|
||||
Note that the startVertexIdx will also have a value of -1 and may need special handling by callers.
|
||||
:param resultCost: array of the paths costs
|
||||
%End
|
||||
|
||||
|
@ -37,10 +37,12 @@ from qgis.core import (QgsWkbTypes,
|
||||
QgsFeatureSink,
|
||||
QgsFeatureRequest,
|
||||
QgsGeometry,
|
||||
QgsGeometryUtils,
|
||||
QgsFields,
|
||||
QgsPointXY,
|
||||
QgsField,
|
||||
QgsProcessing,
|
||||
QgsProcessingParameterBoolean,
|
||||
QgsProcessingParameterEnum,
|
||||
QgsProcessingParameterPoint,
|
||||
QgsProcessingParameterField,
|
||||
@ -75,7 +77,9 @@ class ServiceAreaFromLayer(QgisAlgorithm):
|
||||
SPEED_FIELD = 'SPEED_FIELD'
|
||||
DEFAULT_SPEED = 'DEFAULT_SPEED'
|
||||
TOLERANCE = 'TOLERANCE'
|
||||
INCLUDE_BOUNDS = 'INCLUDE_BOUNDS'
|
||||
OUTPUT = 'OUTPUT'
|
||||
OUTPUT_LINES = 'OUTPUT_LINES'
|
||||
|
||||
def icon(self):
|
||||
return QIcon(os.path.join(pluginPath, 'images', 'networkanalysis.svg'))
|
||||
@ -146,14 +150,24 @@ class ServiceAreaFromLayer(QgisAlgorithm):
|
||||
self.tr('Topology tolerance'),
|
||||
QgsProcessingParameterNumber.Double,
|
||||
0.0, False, 0, 99999999.99))
|
||||
|
||||
params.append(QgsProcessingParameterBoolean(self.INCLUDE_BOUNDS,
|
||||
self.tr('Include upper/lower bound points'),
|
||||
defaultValue=False))
|
||||
for p in params:
|
||||
p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
|
||||
self.addParameter(p)
|
||||
|
||||
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
|
||||
self.tr('Service area (boundary nodes)'),
|
||||
QgsProcessing.TypeVectorPoint))
|
||||
lines_output = QgsProcessingParameterFeatureSink(self.OUTPUT_LINES,
|
||||
self.tr('Service area (lines)'),
|
||||
QgsProcessing.TypeVectorLine, optional=True)
|
||||
lines_output.setCreateByDefault(True)
|
||||
self.addParameter(lines_output)
|
||||
|
||||
nodes_output = QgsProcessingParameterFeatureSink(self.OUTPUT,
|
||||
self.tr('Service area (boundary nodes)'),
|
||||
QgsProcessing.TypeVectorPoint, optional=True)
|
||||
nodes_output.setCreateByDefault(False)
|
||||
self.addParameter(nodes_output)
|
||||
|
||||
def name(self):
|
||||
return 'serviceareafromlayer'
|
||||
@ -176,6 +190,10 @@ class ServiceAreaFromLayer(QgisAlgorithm):
|
||||
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)
|
||||
|
||||
fields = startPoints.fields()
|
||||
fields.append(QgsField('type', QVariant.String, '', 254, 0))
|
||||
fields.append(QgsField('start', QVariant.String, '', 254, 0))
|
||||
@ -240,12 +258,11 @@ class ServiceAreaFromLayer(QgisAlgorithm):
|
||||
feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', 'Calculating service areas…'))
|
||||
graph = builder.graph()
|
||||
|
||||
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
|
||||
fields, QgsWkbTypes.MultiPoint, network.sourceCrs())
|
||||
(point_sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
|
||||
fields, QgsWkbTypes.MultiPoint, network.sourceCrs())
|
||||
(line_sink, line_dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LINES, context,
|
||||
fields, QgsWkbTypes.MultiLineString, network.sourceCrs())
|
||||
|
||||
vertices = []
|
||||
upperBoundary = []
|
||||
lowerBoundary = []
|
||||
total = 100.0 / len(snappedPoints) if snappedPoints else 1
|
||||
for i, p in enumerate(snappedPoints):
|
||||
if feedback.isCanceled():
|
||||
@ -255,35 +272,92 @@ class ServiceAreaFromLayer(QgisAlgorithm):
|
||||
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())
|
||||
vertices = set()
|
||||
area_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
|
||||
|
||||
geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary)
|
||||
geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary)
|
||||
if start_vertex_cost > travelCost:
|
||||
# vertex is too expensive, discard
|
||||
continue
|
||||
|
||||
feat.setGeometry(geomUpper)
|
||||
vertices.add(vertex)
|
||||
start_point = graph.vertex(vertex).point()
|
||||
|
||||
attrs = source_attributes[i]
|
||||
attrs.extend(['upper', origPoint])
|
||||
feat.setAttributes(attrs)
|
||||
sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
# 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)
|
||||
area_points.append(interpolated_end_point)
|
||||
lines.append([start_point, interpolated_end_point])
|
||||
|
||||
feat.setGeometry(geomLower)
|
||||
attrs[-2] = 'lower'
|
||||
feat.setAttributes(attrs)
|
||||
sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
for v in vertices:
|
||||
area_points.append(graph.vertex(v).point())
|
||||
|
||||
vertices[:] = []
|
||||
upperBoundary[:] = []
|
||||
lowerBoundary[:] = []
|
||||
feat = QgsFeature()
|
||||
if point_sink is not None:
|
||||
geomPoints = QgsGeometry.fromMultiPointXY(area_points)
|
||||
feat.setGeometry(geomPoints)
|
||||
attrs = source_attributes[i]
|
||||
attrs.extend(['within', origPoint])
|
||||
feat.setAttributes(attrs)
|
||||
point_sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
|
||||
if include_bounds:
|
||||
upperBoundary = []
|
||||
lowerBoundary = []
|
||||
|
||||
vertices = []
|
||||
for vertex, c in enumerate(cost):
|
||||
if c > travelCost and tree[vertex] != -1:
|
||||
vertexId = graph.edge(tree[vertex]).fromVertex()
|
||||
if cost[vertexId] <= travelCost:
|
||||
vertices.append(vertex)
|
||||
|
||||
for v in vertices:
|
||||
upperBoundary.append(graph.vertex(graph.edge(tree[v]).toVertex()).point())
|
||||
lowerBoundary.append(graph.vertex(graph.edge(tree[v]).fromVertex()).point())
|
||||
|
||||
geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary)
|
||||
geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary)
|
||||
|
||||
feat.setGeometry(geomUpper)
|
||||
attrs[-2] = 'upper'
|
||||
feat.setAttributes(attrs)
|
||||
point_sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
|
||||
feat.setGeometry(geomLower)
|
||||
attrs[-2] = 'lower'
|
||||
feat.setAttributes(attrs)
|
||||
point_sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
|
||||
if line_sink is not None:
|
||||
geom_lines = QgsGeometry.fromMultiPolylineXY(lines)
|
||||
feat.setGeometry(geom_lines)
|
||||
attrs = source_attributes[i]
|
||||
attrs.extend(['lines', origPoint])
|
||||
feat.setAttributes(attrs)
|
||||
line_sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
|
||||
feedback.setProgress(int(i * total))
|
||||
|
||||
return {self.OUTPUT: dest_id}
|
||||
results = {}
|
||||
if point_sink is not None:
|
||||
results[self.OUTPUT] = dest_id
|
||||
if line_sink is not None:
|
||||
results[self.OUTPUT_LINES] = line_dest_id
|
||||
return results
|
||||
|
@ -36,9 +36,11 @@ from qgis.core import (QgsWkbTypes,
|
||||
QgsFeature,
|
||||
QgsFeatureSink,
|
||||
QgsGeometry,
|
||||
QgsGeometryUtils,
|
||||
QgsFields,
|
||||
QgsField,
|
||||
QgsProcessing,
|
||||
QgsProcessingParameterBoolean,
|
||||
QgsProcessingParameterEnum,
|
||||
QgsProcessingParameterPoint,
|
||||
QgsProcessingParameterField,
|
||||
@ -73,7 +75,9 @@ class ServiceAreaFromPoint(QgisAlgorithm):
|
||||
SPEED_FIELD = 'SPEED_FIELD'
|
||||
DEFAULT_SPEED = 'DEFAULT_SPEED'
|
||||
TOLERANCE = 'TOLERANCE'
|
||||
INCLUDE_BOUNDS = 'INCLUDE_BOUNDS'
|
||||
OUTPUT = 'OUTPUT'
|
||||
OUTPUT_LINES = 'OUTPUT_LINES'
|
||||
|
||||
def icon(self):
|
||||
return QIcon(os.path.join(pluginPath, 'images', 'networkanalysis.svg'))
|
||||
@ -143,14 +147,25 @@ class ServiceAreaFromPoint(QgisAlgorithm):
|
||||
self.tr('Topology tolerance'),
|
||||
QgsProcessingParameterNumber.Double,
|
||||
0.0, False, 0, 99999999.99))
|
||||
params.append(QgsProcessingParameterBoolean(self.INCLUDE_BOUNDS,
|
||||
self.tr('Include upper/lower bound points'),
|
||||
defaultValue=False))
|
||||
|
||||
for p in params:
|
||||
p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
|
||||
self.addParameter(p)
|
||||
|
||||
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
|
||||
self.tr('Service area (boundary nodes)'),
|
||||
QgsProcessing.TypeVectorPoint))
|
||||
lines_output = QgsProcessingParameterFeatureSink(self.OUTPUT_LINES,
|
||||
self.tr('Service area (lines)'),
|
||||
QgsProcessing.TypeVectorLine, optional=True)
|
||||
lines_output.setCreateByDefault(True)
|
||||
self.addParameter(lines_output)
|
||||
|
||||
nodes_output = QgsProcessingParameterFeatureSink(self.OUTPUT,
|
||||
self.tr('Service area (boundary nodes)'),
|
||||
QgsProcessing.TypeVectorPoint, optional=True)
|
||||
nodes_output.setCreateByDefault(False)
|
||||
self.addParameter(nodes_output)
|
||||
|
||||
def name(self):
|
||||
return 'serviceareafrompoint'
|
||||
@ -173,6 +188,10 @@ class ServiceAreaFromPoint(QgisAlgorithm):
|
||||
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)
|
||||
@ -208,18 +227,41 @@ class ServiceAreaFromPoint(QgisAlgorithm):
|
||||
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)
|
||||
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])
|
||||
|
||||
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())
|
||||
points.append(graph.vertex(i).point())
|
||||
|
||||
feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromPoint', 'Writing results…'))
|
||||
|
||||
@ -230,25 +272,55 @@ class ServiceAreaFromPoint(QgisAlgorithm):
|
||||
feat = QgsFeature()
|
||||
feat.setFields(fields)
|
||||
|
||||
geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary)
|
||||
geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary)
|
||||
(point_sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
|
||||
fields, QgsWkbTypes.MultiPoint, network.sourceCrs())
|
||||
|
||||
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
|
||||
fields, QgsWkbTypes.MultiPoint, network.sourceCrs())
|
||||
results = {}
|
||||
|
||||
feat.setGeometry(geomUpper)
|
||||
feat['type'] = 'upper'
|
||||
feat['start'] = startPoint.toString()
|
||||
sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
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)
|
||||
|
||||
feat.setGeometry(geomLower)
|
||||
feat['type'] = 'lower'
|
||||
feat['start'] = startPoint.toString()
|
||||
sink.addFeature(feat, QgsFeatureSink.FastInsert)
|
||||
if include_bounds:
|
||||
upperBoundary = []
|
||||
lowerBoundary = []
|
||||
|
||||
upperBoundary.append(startPoint)
|
||||
lowerBoundary.append(startPoint)
|
||||
geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary)
|
||||
geomLower = QgsGeometry.fromMultiPointXY(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)
|
||||
|
||||
return {self.OUTPUT: dest_id}
|
||||
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
|
||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.dbf
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.dbf
vendored
Normal file
Binary file not shown.
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.prj
vendored
Normal file
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.prj
vendored
Normal file
@ -0,0 +1 @@
|
||||
PROJCS["WGS_1984_UTM_Zone_33S",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",15],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["Meter",1]]
|
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.qpj
vendored
Normal file
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.qpj
vendored
Normal file
@ -0,0 +1 @@
|
||||
PROJCS["WGS 84 / UTM zone 33S",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",15],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32733"]]
|
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.shp
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.shp
vendored
Normal file
Binary file not shown.
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.shx
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_lines.shx
vendored
Normal file
Binary file not shown.
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.dbf
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.dbf
vendored
Normal file
Binary file not shown.
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.prj
vendored
Normal file
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.prj
vendored
Normal file
@ -0,0 +1 @@
|
||||
PROJCS["WGS_1984_UTM_Zone_33S",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",15],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["Meter",1]]
|
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.qpj
vendored
Normal file
1
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.qpj
vendored
Normal file
@ -0,0 +1 @@
|
||||
PROJCS["WGS 84 / UTM zone 33S",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",15],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32733"]]
|
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.shp
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.shp
vendored
Normal file
Binary file not shown.
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.shx
vendored
Normal file
BIN
python/plugins/processing/tests/testdata/expected/service_area_from_layer_no_bounds.shx
vendored
Normal file
Binary file not shown.
21
python/plugins/processing/tests/testdata/expected/service_area_lines.gml
vendored
Normal file
21
python/plugins/processing/tests/testdata/expected/service_area_lines.gml
vendored
Normal file
File diff suppressed because one or more lines are too long
37
python/plugins/processing/tests/testdata/expected/service_area_lines.xsd
vendored
Normal file
37
python/plugins/processing/tests/testdata/expected/service_area_lines.xsd
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
|
||||
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
|
||||
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
|
||||
<xs:complexType name="FeatureCollectionType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureCollectionType">
|
||||
<xs:attribute name="lockId" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="scope" type="xs:string" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="service_area_lines" type="ogr:service_area_lines_Type" substitutionGroup="gml:_Feature"/>
|
||||
<xs:complexType name="service_area_lines_Type">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureType">
|
||||
<xs:sequence>
|
||||
<xs:element name="geometryProperty" type="gml:MultiLineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="type" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:maxLength value="254"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="start" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:maxLength value="254"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
21
python/plugins/processing/tests/testdata/expected/service_area_no_bounds.gml
vendored
Normal file
21
python/plugins/processing/tests/testdata/expected/service_area_no_bounds.gml
vendored
Normal file
File diff suppressed because one or more lines are too long
37
python/plugins/processing/tests/testdata/expected/service_area_no_bounds.xsd
vendored
Normal file
37
python/plugins/processing/tests/testdata/expected/service_area_no_bounds.xsd
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
|
||||
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
|
||||
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
|
||||
<xs:complexType name="FeatureCollectionType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureCollectionType">
|
||||
<xs:attribute name="lockId" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="scope" type="xs:string" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="service_area_no_bounds" type="ogr:service_area_no_bounds_Type" substitutionGroup="gml:_Feature"/>
|
||||
<xs:complexType name="service_area_no_bounds_Type">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureType">
|
||||
<xs:sequence>
|
||||
<xs:element name="geometryProperty" type="gml:MultiPointPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="type" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:maxLength value="254"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="start" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:maxLength value="254"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
@ -3049,6 +3049,66 @@ tests:
|
||||
start: skip
|
||||
end: skip
|
||||
|
||||
- algorithm: qgis:serviceareafrompoint
|
||||
name: Service area (from point, shortest, no bounds)
|
||||
params:
|
||||
DEFAULT_DIRECTION: 2
|
||||
DEFAULT_SPEED: 5.0
|
||||
INCLUDE_BOUNDS: false
|
||||
INPUT:
|
||||
name: roads.gml
|
||||
type: vector
|
||||
START_POINT: 1002465.0089601517,6221875.432489508
|
||||
STRATEGY: 0
|
||||
TOLERANCE: 0.0
|
||||
TRAVEL_COST: 700.0
|
||||
VALUE_BACKWARD: ''
|
||||
VALUE_BOTH: ''
|
||||
VALUE_FORWARD: ''
|
||||
results:
|
||||
OUTPUT:
|
||||
name: expected/service_area_no_bounds.gml
|
||||
type: vector
|
||||
compare:
|
||||
ignore_crs_check: true
|
||||
geometry:
|
||||
precision: 2
|
||||
fields:
|
||||
cost:
|
||||
precision: 2
|
||||
start: skip
|
||||
end: skip
|
||||
|
||||
- algorithm: qgis:serviceareafrompoint
|
||||
name: Service area from point, output lines
|
||||
params:
|
||||
DEFAULT_DIRECTION: 2
|
||||
DEFAULT_SPEED: 5.0
|
||||
INCLUDE_BOUNDS: false
|
||||
INPUT:
|
||||
name: roads.gml
|
||||
type: vector
|
||||
START_POINT: 1003654.8065293541,6222397.723338357 [EPSG:32733]
|
||||
STRATEGY: 0
|
||||
TOLERANCE: 0.0
|
||||
TRAVEL_COST: 400.0
|
||||
VALUE_BACKWARD: ''
|
||||
VALUE_BOTH: ''
|
||||
VALUE_FORWARD: ''
|
||||
results:
|
||||
OUTPUT_LINES:
|
||||
name: expected/service_area_lines.gml
|
||||
type: vector
|
||||
compare:
|
||||
ignore_crs_check: true
|
||||
geometry:
|
||||
precision: 2
|
||||
fields:
|
||||
cost:
|
||||
precision: 2
|
||||
start: skip
|
||||
end: skip
|
||||
|
||||
- algorithm: qgis:serviceareafromlayer
|
||||
name: Service area from layer
|
||||
params:
|
||||
@ -3083,6 +3143,75 @@ tests:
|
||||
- d
|
||||
- type
|
||||
|
||||
- algorithm: qgis:serviceareafromlayer
|
||||
name: Service area from layer no bounds
|
||||
params:
|
||||
DEFAULT_DIRECTION: 2
|
||||
DEFAULT_SPEED: 5.0
|
||||
INCLUDE_BOUNDS: false
|
||||
INPUT:
|
||||
name: roads.gml
|
||||
type: vector
|
||||
START_POINTS:
|
||||
name: custom/route_points.gml
|
||||
type: vector
|
||||
STRATEGY: 0
|
||||
TOLERANCE: 0.0
|
||||
TRAVEL_COST: 700.0
|
||||
VALUE_BACKWARD: ''
|
||||
VALUE_BOTH: ''
|
||||
VALUE_FORWARD: ''
|
||||
results:
|
||||
OUTPUT:
|
||||
name: expected/service_area_from_layer_no_bounds.shp
|
||||
type: vector
|
||||
compare:
|
||||
ignore_crs_check: true
|
||||
geometry:
|
||||
precision: 2
|
||||
fields:
|
||||
cost:
|
||||
precision: 2
|
||||
start: skip
|
||||
end: skip
|
||||
pk:
|
||||
- d
|
||||
- type
|
||||
|
||||
- algorithm: qgis:serviceareafromlayer
|
||||
name: Service area from layer (lines)
|
||||
params:
|
||||
DEFAULT_DIRECTION: 2
|
||||
DEFAULT_SPEED: 5.0
|
||||
INCLUDE_BOUNDS: false
|
||||
INPUT:
|
||||
name: roads.gml
|
||||
type: vector
|
||||
START_POINTS:
|
||||
name: custom/route_points.gml
|
||||
type: vector
|
||||
STRATEGY: 0
|
||||
TOLERANCE: 0.0
|
||||
TRAVEL_COST: 500.0
|
||||
VALUE_BACKWARD: ''
|
||||
VALUE_BOTH: ''
|
||||
VALUE_FORWARD: ''
|
||||
results:
|
||||
OUTPUT_LINES:
|
||||
name: expected/service_area_from_layer_lines.shp
|
||||
type: vector
|
||||
compare:
|
||||
ignore_crs_check: true
|
||||
geometry:
|
||||
precision: 2
|
||||
fields:
|
||||
cost:
|
||||
precision: 2
|
||||
start: skip
|
||||
end: skip
|
||||
pk:
|
||||
- d
|
||||
- type
|
||||
|
||||
- algorithm: qgis:createattributeindex
|
||||
name: Create attribute index
|
||||
|
@ -38,7 +38,8 @@ class ANALYSIS_EXPORT QgsGraphAnalyzer
|
||||
* \param source source graph
|
||||
* \param startVertexIdx index of the start vertex
|
||||
* \param criterionNum index of the optimization strategy
|
||||
* \param resultTree array that represents shortest path tree. resultTree[ vertexIndex ] == inboundingArcIndex if vertex reachable, otherwise resultTree[ vertexIndex ] == -1
|
||||
* \param resultTree array that represents shortest path tree. resultTree[ vertexIndex ] == inboundingArcIndex if vertex reachable, otherwise resultTree[ vertexIndex ] == -1.
|
||||
* Note that the startVertexIdx will also have a value of -1 and may need special handling by callers.
|
||||
* \param resultCost array of the paths costs
|
||||
*/
|
||||
static void SIP_PYALTERNATIVETYPE( SIP_PYLIST ) dijkstra( const QgsGraph *source, int startVertexIdx, int criterionNum, QVector<int> *resultTree = nullptr, QVector<double> *resultCost = nullptr );
|
||||
|
Loading…
x
Reference in New Issue
Block a user