Skip to content
Permalink
Browse files

Merge pull request #5488 from nyalldawson/network

[processing] Fix some issues with network analysis shortest path algs
  • Loading branch information
nyalldawson committed Oct 31, 2017
2 parents 9a1cd9d + 05ad0bc commit 5000f9aac3ac70ec1fea3611668819817d0b99da
Showing with 1,189 additions and 374 deletions.
  1. +28 −0 doc/api_break.dox
  2. +18 −11 python/analysis/network/qgsgraph.sip
  3. +2 −0 python/analysis/network/qgsgraphbuilder.sip
  4. +1 −11 python/analysis/network/qgsnetworkspeedstrategy.sip
  5. +4 −5 python/analysis/network/qgsnetworkstrategy.sip
  6. +23 −12 python/plugins/processing/algs/qgis/ServiceAreaFromLayer.py
  7. +5 −5 python/plugins/processing/algs/qgis/ServiceAreaFromPoint.py
  8. +31 −22 python/plugins/processing/algs/qgis/ShortestPathLayerToPoint.py
  9. +32 −23 python/plugins/processing/algs/qgis/ShortestPathPointToLayer.py
  10. +7 −9 python/plugins/processing/algs/qgis/ShortestPathPointToPoint.py
  11. +37 −0 python/plugins/processing/tests/testdata/custom/route_points.gml
  12. +30 −0 python/plugins/processing/tests/testdata/custom/route_points.xsd
  13. +2 −2 python/plugins/processing/tests/testdata/expected/service_area.gml
  14. BIN python/plugins/processing/tests/testdata/expected/service_area_from_layer.dbf
  15. +1 −0 python/plugins/processing/tests/testdata/expected/service_area_from_layer.prj
  16. +1 −0 python/plugins/processing/tests/testdata/expected/service_area_from_layer.qpj
  17. BIN python/plugins/processing/tests/testdata/expected/service_area_from_layer.shp
  18. BIN python/plugins/processing/tests/testdata/expected/service_area_from_layer.shx
  19. BIN python/plugins/processing/tests/testdata/expected/shortest_path_layer_to_point.dbf
  20. +50 −0 python/plugins/processing/tests/testdata/expected/shortest_path_layer_to_point.gml
  21. +1 −0 python/plugins/processing/tests/testdata/expected/shortest_path_layer_to_point.prj
  22. +1 −0 python/plugins/processing/tests/testdata/expected/shortest_path_layer_to_point.qpj
  23. BIN python/plugins/processing/tests/testdata/expected/shortest_path_layer_to_point.shp
  24. BIN python/plugins/processing/tests/testdata/expected/shortest_path_layer_to_point.shx
  25. +52 −0 python/plugins/processing/tests/testdata/expected/shortest_path_layer_to_point.xsd
  26. BIN python/plugins/processing/tests/testdata/expected/shortest_path_point_to_layer.dbf
  27. +1 −0 python/plugins/processing/tests/testdata/expected/shortest_path_point_to_layer.prj
  28. +1 −0 python/plugins/processing/tests/testdata/expected/shortest_path_point_to_layer.qpj
  29. BIN python/plugins/processing/tests/testdata/expected/shortest_path_point_to_layer.shp
  30. BIN python/plugins/processing/tests/testdata/expected/shortest_path_point_to_layer.shx
  31. +110 −4 python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
  32. +1 −0 src/analysis/CMakeLists.txt
  33. +13 −13 src/analysis/network/qgsgraph.cpp
  34. +22 −15 src/analysis/network/qgsgraph.h
  35. +8 −9 src/analysis/network/qgsgraphanalyzer.cpp
  36. +2 −2 src/analysis/network/qgsgraphbuilder.h
  37. +3 −3 src/analysis/network/qgsnetworkspeedstrategy.cpp
  38. +1 −7 src/analysis/network/qgsnetworkspeedstrategy.h
  39. +3 −4 src/analysis/network/qgsnetworkstrategy.h
  40. +223 −215 src/analysis/network/qgsvectorlayerdirector.cpp
  41. +5 −2 src/analysis/network/qgsvectorlayerdirector.h
  42. +2 −0 tests/src/analysis/CMakeLists.txt
  43. +468 −0 tests/src/analysis/testqgsnetworkanalysis.cpp
@@ -1379,13 +1379,34 @@ QgsGraduatedSymbolRenderer {#qgis_api_break_3_0_QgsGraduatedSymbolRendere
- sizeScaleFieldChanged() and scaleMethodChanged() were removed. These settings are no longer exposed in the widget's GUI.


QgsGraph {#qgis_api_break_3_0_QgsGraph}
--------

- addEdge now explicitly takes the fromVertex as first argument, and toVertex as second argument. The original
API design was unclear due to the reversed methods in QgsGraphEdge and QgsGraphVertex.


QgsGraphBuilderInterface {#qgis_api_break_3_0_QgsGraphBuilderInterface}
------------------------

- destinationCrs() now returns a copy instead of a reference to the CRS. This has no effect on PyQGIS code, but c++
plugins calling this method will need to be updated.


QgsGraphEdge {#qgis_api_break_3_0_QgsGraphEdge}
------------

- outVertex() was renamed as toVertex() (yes, the original name was the opposite of the returned value!)
- inVertex() was renamed as fromVertex() (yes, the original name was the opposite of the returned value!)


QgsGraphVertex {#qgis_api_break_3_0_QgsGraphVertex}
--------------

- outEdges() was renamed as incomingEdges() (yes, the original name was the opposite of the returned value!)
- inEdges() was renamed as outgoingEdges() (yes, the original name was the opposite of the returned value!)


QgsEditorWidgetRegistry {#qgis_api_break_3_0_QgsEditorWidgetRegistry}
-----------------------

@@ -1777,6 +1798,13 @@ QgsNetworkAccessManager {#qgis_api_break_3_0_QgsNetworkAccessManager}
- deleteReply() was removed. Use abort() and deleteLayer() on the reply directly.
- requestSent signal was removed. This is no longer emitted.


QgsNetworkStrategy {#qgis_api_break_3_0_QgsNetworkStrategy}
------------------

- requiredAttributes() now returns a set of attributes, instead of a list


QgsNewVectorLayerDialog {#qgis_api_break_3_0_QgsNewVectorLayerDialog}
-----------------------

@@ -41,15 +41,17 @@ class QgsGraphEdge
:rtype: list of QVariant
%End

int outVertex() const;
int toVertex() const;
%Docstring
Returns index of the outgoing vertex
Returns the index of the vertex at the end of this edge.
.. seealso:: fromVertex()
:rtype: int
%End

int inVertex() const;
int fromVertex() const;
%Docstring
Returns index of the incoming vertex
Returns the index of the vertex at the start of this edge.
.. seealso:: toVertex()
:rtype: int
%End

@@ -62,6 +64,7 @@ class QgsGraphVertex
{
%Docstring
This class implements a graph vertex
.. versionadded:: 3.0
%End

%TypeHeaderCode
@@ -80,21 +83,23 @@ class QgsGraphVertex
This constructor initializes QgsGraphVertex object and associates a vertex with a point
%End

QgsGraphEdgeIds outEdges() const;
QgsGraphEdgeIds incomingEdges() const;
%Docstring
Returns outgoing edges ids
Returns the incoming edge ids, i.e. edges which end at this node.
.. seealso:: outgoingEdges()
:rtype: QgsGraphEdgeIds
%End

QgsGraphEdgeIds inEdges() const;
QgsGraphEdgeIds outgoingEdges() const;
%Docstring
Return incoming edges ids
Returns outgoing edge ids, i.e. edges which start at this node.
.. seealso:: incomingEdges()
:rtype: QgsGraphEdgeIds
%End

QgsPointXY point() const;
%Docstring
Returns point associated with graph vertex
Returns point associated with graph vertex.
:rtype: QgsPointXY
%End

@@ -105,6 +110,7 @@ class QgsGraph
{
%Docstring
Mathematical graph representation
.. versionadded:: 3.0
%End

%TypeHeaderCode
@@ -124,9 +130,10 @@ class QgsGraph
:rtype: int
%End

int addEdge( int outVertexIdx, int inVertexIdx, const QVector< QVariant > &strategies );
int addEdge( int fromVertexIdx, int toVertexIdx, const QVector< QVariant > &strategies );
%Docstring
Add an edge to the graph
Add an edge to the graph, going from the ``fromVertexIdx``
to ``toVertexIdx``.
:rtype: int
%End

@@ -30,12 +30,14 @@ class QgsGraphBuilder : QgsGraphBuilderInterface
~QgsGraphBuilder();

virtual void addVertex( int id, const QgsPointXY &pt );

%Docstring
MANDATORY BUILDER PROPERTY DECLARATION
%End

virtual void addEdge( int pt1id, const QgsPointXY &pt1, int pt2id, const QgsPointXY &pt2, const QVector< QVariant > &prop );


QgsGraph *graph() /Factory/;
%Docstring
Returns generated QgsGraph
@@ -28,18 +28,8 @@ class QgsNetworkSpeedStrategy : QgsNetworkStrategy

virtual QVariant cost( double distance, const QgsFeature &f ) const;

%Docstring
Returns edge cost
:rtype: QVariant
%End

virtual QgsAttributeList requiredAttributes() const;
virtual QSet< int > requiredAttributes() const;

%Docstring
Returns list of the source layer attributes needed for cost calculation.
This method called by QgsGraphDirector.
:rtype: QgsAttributeList
%End

};

@@ -45,12 +45,11 @@ class QgsNetworkStrategy

virtual ~QgsNetworkStrategy();

virtual QgsAttributeList requiredAttributes() const;
virtual QSet< int > requiredAttributes() const;
%Docstring
Returns list of the source layer attributes needed for cost calculation.
This method called by QgsGraphDirector.
:return: list of required attributes
:rtype: QgsAttributeList
Returns a list of the source layer attributes needed for cost calculation.
This is method called by QgsGraphDirector.
:rtype: set of int
%End

virtual QVariant cost( double distance, const QgsFeature &f ) const = 0;
@@ -38,6 +38,7 @@
QgsFeatureRequest,
QgsGeometry,
QgsFields,
QgsPointXY,
QgsField,
QgsProcessing,
QgsProcessingParameterEnum,
@@ -88,8 +89,8 @@ def __init__(self):
def initAlgorithm(self, config=None):
self.DIRECTIONS = OrderedDict([
(self.tr('Forward direction'), QgsVectorLayerDirector.DirectionForward),
(self.tr('Backward direction'), QgsVectorLayerDirector.DirectionForward),
(self.tr('Both directions'), QgsVectorLayerDirector.DirectionForward)])
(self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward),
(self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)])

self.STRATEGIES = [self.tr('Shortest'),
self.tr('Fastest')
@@ -172,7 +173,7 @@ def processAlgorithm(self, parameters, context, feedback):
defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context)
tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context)

fields = QgsFields()
fields = startPoints.fields()
fields.append(QgsField('type', QVariant.String, '', 254, 0))
fields.append(QgsField('start', QVariant.String, '', 254, 0))

@@ -209,17 +210,25 @@ def processAlgorithm(self, parameters, context, feedback):

feedback.pushInfo(self.tr('Loading start points...'))
request = QgsFeatureRequest()
request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes)
request.setDestinationCrs(network.sourceCrs())
features = startPoints.getFeatures(request)
total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0

points = []
source_attributes = {}
i = 0
for current, f in enumerate(features):
if feedback.isCanceled():
break

points.append(f.geometry().asPoint())
if not f.hasGeometry():
continue

for p in f.geometry().vertices():
points.append(QgsPointXY(p))
source_attributes[i] = f.attributes()
i += 1

feedback.setProgress(int(current * total))

feedback.pushInfo(self.tr('Building graph...'))
@@ -245,25 +254,27 @@ def processAlgorithm(self, parameters, context, feedback):
tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0)
for j, v in enumerate(cost):
if v > travelCost and tree[j] != -1:
vertexId = graph.edge(tree[j]).outVertex()
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]).inVertex()).point())
lowerBoundary.append(graph.vertex(graph.edge(tree[j]).outVertex()).point())
upperBoundary.append(graph.vertex(graph.edge(tree[j]).toVertex()).point())
lowerBoundary.append(graph.vertex(graph.edge(tree[j]).fromVertex()).point())

geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary)
geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary)

feat.setGeometry(geomUpper)
feat['type'] = 'upper'
feat['start'] = origPoint

attrs = source_attributes[i]
attrs.extend(['upper', origPoint])
feat.setAttributes(attrs)
sink.addFeature(feat, QgsFeatureSink.FastInsert)

feat.setGeometry(geomLower)
feat['type'] = 'lower'
feat['start'] = origPoint
attrs[-2] = 'lower'
feat.setAttributes(attrs)
sink.addFeature(feat, QgsFeatureSink.FastInsert)

vertices[:] = []
@@ -87,8 +87,8 @@ def __init__(self):
def initAlgorithm(self, config=None):
self.DIRECTIONS = OrderedDict([
(self.tr('Forward direction'), QgsVectorLayerDirector.DirectionForward),
(self.tr('Backward direction'), QgsVectorLayerDirector.DirectionForward),
(self.tr('Both directions'), QgsVectorLayerDirector.DirectionForward)])
(self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward),
(self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)])

self.STRATEGIES = [self.tr('Shortest'),
self.tr('Fastest')
@@ -208,15 +208,15 @@ def processAlgorithm(self, parameters, context, feedback):
vertices = []
for i, v in enumerate(cost):
if v > travelCost and tree[i] != -1:
vertexId = graph.edge(tree[i]).outVertex()
vertexId = graph.edge(tree[i]).fromVertex()
if cost[vertexId] <= travelCost:
vertices.append(i)

upperBoundary = []
lowerBoundary = []
for i in vertices:
upperBoundary.append(graph.vertex(graph.edge(tree[i]).inVertex()).point())
lowerBoundary.append(graph.vertex(graph.edge(tree[i]).outVertex()).point())
upperBoundary.append(graph.vertex(graph.edge(tree[i]).toVertex()).point())
lowerBoundary.append(graph.vertex(graph.edge(tree[i]).fromVertex()).point())

feedback.pushInfo(self.tr('Writing results...'))

0 comments on commit 5000f9a

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