From 8d32bf729eee69a87c5efbcfc8ba10ac426e22a5 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 30 Oct 2017 12:51:27 +1000 Subject: [PATCH] Fix QgsGraphAnalyzer::dijkstra traverses through edges backwards This means that it flips the direction of the graph edge, breaking route restrictions. Refs #17325 --- .../algs/qgis/ShortestPathLayerToPoint.py | 8 +- .../algs/qgis/ShortestPathPointToLayer.py | 9 +- .../algs/qgis/ShortestPathPointToPoint.py | 8 +- src/analysis/network/qgsgraphanalyzer.cpp | 15 +- src/analysis/network/qgsgraphbuilder.cpp | 2 +- .../network/qgsvectorlayerdirector.cpp | 22 +- tests/src/analysis/testqgsnetworkanalysis.cpp | 308 ++++++++++++++---- 7 files changed, 286 insertions(+), 86 deletions(-) diff --git a/python/plugins/processing/algs/qgis/ShortestPathLayerToPoint.py b/python/plugins/processing/algs/qgis/ShortestPathLayerToPoint.py index cd8b698305b..0ba5af6a3e7 100644 --- a/python/plugins/processing/algs/qgis/ShortestPathLayerToPoint.py +++ b/python/plugins/processing/algs/qgis/ShortestPathLayerToPoint.py @@ -250,7 +250,7 @@ class ShortestPathLayerToPoint(QgisAlgorithm): idxStart = graph.findVertex(snappedPoints[i]) - tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) + tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: msg = self.tr('There is no route from start point ({}) to end point ({}).'.format(points[i].toString(), endPoint.toString())) @@ -266,9 +266,9 @@ class ShortestPathLayerToPoint(QgisAlgorithm): cost = 0.0 current = idxEnd while current != idxStart: - cost += graph.edge(tree[current]).cost(0) - route.append(graph.vertex(graph.edge(tree[current]).fromVertex()).point()) - current = graph.edge(tree[current]).toVertex() + cost += costs[current] + current = graph.edge(tree[current]).fromVertex() + route.append(graph.vertex(current).point()) route.append(snappedPoints[i]) route.reverse() diff --git a/python/plugins/processing/algs/qgis/ShortestPathPointToLayer.py b/python/plugins/processing/algs/qgis/ShortestPathPointToLayer.py index f38db0c46e3..8efeeace0af 100644 --- a/python/plugins/processing/algs/qgis/ShortestPathPointToLayer.py +++ b/python/plugins/processing/algs/qgis/ShortestPathPointToLayer.py @@ -241,8 +241,7 @@ class ShortestPathPointToLayer(QgisAlgorithm): graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) - tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) - route = [] + tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 @@ -266,9 +265,9 @@ class ShortestPathPointToLayer(QgisAlgorithm): cost = 0.0 current = idxEnd while current != idxStart: - cost += graph.edge(tree[current]).cost(0) - route.append(graph.vertex(graph.edge(tree[current]).fromVertex()).point()) - current = graph.edge(tree[current]).toVertex() + cost += costs[current] + current = graph.edge(tree[current]).fromVertex() + route.append(graph.vertex(current).point()) route.append(snappedPoints[0]) route.reverse() diff --git a/python/plugins/processing/algs/qgis/ShortestPathPointToPoint.py b/python/plugins/processing/algs/qgis/ShortestPathPointToPoint.py index 779dc83694c..4dde38b8259 100644 --- a/python/plugins/processing/algs/qgis/ShortestPathPointToPoint.py +++ b/python/plugins/processing/algs/qgis/ShortestPathPointToPoint.py @@ -217,7 +217,7 @@ class ShortestPathPointToPoint(QgisAlgorithm): idxStart = graph.findVertex(snappedPoints[0]) idxEnd = graph.findVertex(snappedPoints[1]) - tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) + tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: raise QgsProcessingException( self.tr('There is no route from start point to end point.')) @@ -226,9 +226,9 @@ class ShortestPathPointToPoint(QgisAlgorithm): cost = 0.0 current = idxEnd while current != idxStart: - cost += graph.edge(tree[current]).cost(0) - route.append(graph.vertex(graph.edge(tree[current]).fromVertex()).point()) - current = graph.edge(tree[current]).toVertex() + cost += costs[current] + current = graph.edge(tree[current]).fromVertex() + route.append(graph.vertex(current).point()) route.append(snappedPoints[0]) route.reverse() diff --git a/src/analysis/network/qgsgraphanalyzer.cpp b/src/analysis/network/qgsgraphanalyzer.cpp index 713ab314d5c..2a0c382137a 100644 --- a/src/analysis/network/qgsgraphanalyzer.cpp +++ b/src/analysis/network/qgsgraphanalyzer.cpp @@ -59,21 +59,20 @@ void QgsGraphAnalyzer::dijkstra( const QgsGraph *source, int startPointIdx, int not_begin.erase( it ); // edge index list - QgsGraphEdgeIds l = source->vertex( curVertex ).incomingEdges(); - QgsGraphEdgeIds::iterator arcIt; - for ( arcIt = l.begin(); arcIt != l.end(); ++arcIt ) + const QgsGraphEdgeIds &outgoingEdges = source->vertex( curVertex ).outgoingEdges(); + for ( int edgeId : outgoingEdges ) { - const QgsGraphEdge arc = source->edge( *arcIt ); + const QgsGraphEdge &arc = source->edge( edgeId ); double cost = arc.cost( criterionNum ).toDouble() + curCost; - if ( cost < ( *result )[ arc.fromVertex()] ) + if ( cost < ( *result )[ arc.toVertex()] ) { - ( *result )[ arc.fromVertex()] = cost; + ( *result )[ arc.toVertex()] = cost; if ( resultTree ) { - ( *resultTree )[ arc.fromVertex()] = *arcIt; + ( *resultTree )[ arc.toVertex()] = edgeId; } - not_begin.insert( cost, arc.fromVertex() ); + not_begin.insert( cost, arc.toVertex() ); } } } diff --git a/src/analysis/network/qgsgraphbuilder.cpp b/src/analysis/network/qgsgraphbuilder.cpp index 85d5cbb67e4..6d3e161e01f 100644 --- a/src/analysis/network/qgsgraphbuilder.cpp +++ b/src/analysis/network/qgsgraphbuilder.cpp @@ -42,7 +42,7 @@ void QgsGraphBuilder::addVertex( int, const QgsPointXY &pt ) void QgsGraphBuilder::addEdge( int pt1id, const QgsPointXY &, int pt2id, const QgsPointXY &, const QVector< QVariant > &prop ) { - mGraph->addEdge( pt2id, pt1id, prop ); + mGraph->addEdge( pt1id, pt2id, prop ); } QgsGraph *QgsGraphBuilder::graph() diff --git a/src/analysis/network/qgsvectorlayerdirector.cpp b/src/analysis/network/qgsvectorlayerdirector.cpp index c104f79c036..25491d1fa4a 100644 --- a/src/analysis/network/qgsvectorlayerdirector.cpp +++ b/src/analysis/network/qgsvectorlayerdirector.cpp @@ -39,12 +39,14 @@ using namespace SpatialIndex; struct TiePointInfo { TiePointInfo() = default; - TiePointInfo( QgsFeatureId featureId, const QgsPointXY &start, const QgsPointXY &end ) - : mNetworkFeatureId( featureId ) + TiePointInfo( int additionalPointId, QgsFeatureId featureId, const QgsPointXY &start, const QgsPointXY &end ) + : additionalPointId( additionalPointId ) + , mNetworkFeatureId( featureId ) , mFirstPoint( start ) , mLastPoint( end ) {} + int additionalPointId = -1; QgsPointXY mTiedPoint; double mLength = DBL_MAX; QgsFeatureId mNetworkFeatureId = -1; @@ -255,7 +257,7 @@ void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const if ( thisSegmentClosestDist < additionalTiePoints[ i ].mLength ) { // found a closer segment for this additional point - TiePointInfo info( feature.id(), pt1, pt2 ); + TiePointInfo info( i, feature.id(), pt1, pt2 ); info.mLength = thisSegmentClosestDist; info.mTiedPoint = snappedPoint; @@ -300,8 +302,11 @@ void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const snappedPoints[ i ] = graphVertices.at( ptIdx ); } } - // also need to update tie points - they need to be matched for snapped points + for ( int i = 0; i < additionalTiePoints.count(); ++i ) + { + additionalTiePoints[ i ].mTiedPoint = snappedPoints.at( additionalTiePoints.at( i ).additionalPointId ); + } // begin graph construction @@ -361,9 +366,9 @@ void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const int pt1idx = -1; int pt2idx = -1; bool isFirstPoint = true; - for ( const QgsPointXY &arcPoint : qgis::as_const( pointsOnArc ) ) + for ( auto arcPointIt = pointsOnArc.constBegin(); arcPointIt != pointsOnArc.constEnd(); ++arcPointIt ) { - pt2 = arcPoint; + pt2 = arcPointIt.value(); pt2idx = findPointWithinTolerance( pt2 ); Q_ASSERT_X( pt2idx >= 0, "QgsVectorLayerDirectory::makeGraph", "encountered a vertex which was not present in graph" ); @@ -372,10 +377,9 @@ void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const { double distance = builder->distanceArea()->measureLine( pt1, pt2 ); QVector< QVariant > prop; - QList< QgsNetworkStrategy * >::const_iterator it; - for ( it = mStrategies.begin(); it != mStrategies.end(); ++it ) + for ( QgsNetworkStrategy *strategy : mStrategies ) { - prop.push_back( ( *it )->cost( distance, feature ) ); + prop.push_back( strategy->cost( distance, feature ) ); } if ( direction == Direction::DirectionForward || diff --git a/tests/src/analysis/testqgsnetworkanalysis.cpp b/tests/src/analysis/testqgsnetworkanalysis.cpp index 19cc46fa255..44214781234 100644 --- a/tests/src/analysis/testqgsnetworkanalysis.cpp +++ b/tests/src/analysis/testqgsnetworkanalysis.cpp @@ -24,6 +24,7 @@ Email : nyall dot dawson at gmail dot com #include "qgsnetworkdistancestrategy.h" #include "qgsgraphbuilder.h" #include "qgsgraph.h" +#include "qgsgraphanalyzer.h" class TestQgsNetworkAnalysis : public QObject { @@ -39,6 +40,7 @@ class TestQgsNetworkAnalysis : public QObject void testGraph(); void testBuild(); void testBuildTolerance(); + void dijkkjkjkskkjsktra(); private: std::unique_ptr< QgsVectorLayer > buildNetwork(); @@ -46,6 +48,18 @@ class TestQgsNetworkAnalysis : public QObject }; +class TestNetworkStrategy : public QgsNetworkStrategy +{ + QSet< int > requiredAttributes() const override + { + return QSet< int >() << 0; + } + QVariant cost( double, const QgsFeature &f ) const override + { + return f.attribute( 0 ); + } +}; + void TestQgsNetworkAnalysis::initTestCase() { // @@ -117,11 +131,12 @@ void TestQgsNetworkAnalysis::testGraph() std::unique_ptr TestQgsNetworkAnalysis::buildNetwork() { - std::unique_ptr< QgsVectorLayer > l = qgis::make_unique< QgsVectorLayer >( QStringLiteral( "LineString?crs=epsg:4326" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) ); + std::unique_ptr< QgsVectorLayer > l = qgis::make_unique< QgsVectorLayer >( QStringLiteral( "LineString?crs=epsg:4326&field=cost:int" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) ); QgsFeature ff( 0 ); QgsGeometry refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(0 0, 10 0, 10 10)" ) ); ff.setGeometry( refGeom ); + ff.setAttributes( QgsAttributes() << 1 ); QgsFeatureList flist; flist << ff; l->dataProvider()->addFeatures( flist ); @@ -146,22 +161,22 @@ void TestQgsNetworkAnalysis::testBuild() QCOMPARE( graph->vertexCount(), 3 ); QCOMPARE( graph->edgeCount(), 4 ); QCOMPARE( graph->vertex( 0 ).point(), QgsPointXY( 0, 0 ) ); - QCOMPARE( graph->vertex( 0 ).outgoingEdges(), QList< int >() << 1 ); - QCOMPARE( graph->edge( 1 ).fromVertex(), 0 ); - QCOMPARE( graph->edge( 1 ).toVertex(), 1 ); - QCOMPARE( graph->vertex( 0 ).incomingEdges(), QList< int >() << 0 ); - QCOMPARE( graph->edge( 0 ).fromVertex(), 1 ); - QCOMPARE( graph->edge( 0 ).toVertex(), 0 ); + QCOMPARE( graph->vertex( 0 ).outgoingEdges(), QList< int >() << 0 ); + QCOMPARE( graph->edge( 0 ).fromVertex(), 0 ); + QCOMPARE( graph->edge( 0 ).toVertex(), 1 ); + QCOMPARE( graph->vertex( 0 ).incomingEdges(), QList< int >() << 1 ); + QCOMPARE( graph->edge( 1 ).fromVertex(), 1 ); + QCOMPARE( graph->edge( 1 ).toVertex(), 0 ); QCOMPARE( graph->vertex( 1 ).point(), QgsPointXY( 10, 0 ) ); - QCOMPARE( graph->vertex( 1 ).outgoingEdges(), QList< int >() << 0 << 3 ); - QCOMPARE( graph->vertex( 1 ).incomingEdges(), QList< int >() << 1 << 2 ); - QCOMPARE( graph->edge( 2 ).fromVertex(), 2 ); - QCOMPARE( graph->edge( 2 ).toVertex(), 1 ); - QCOMPARE( graph->edge( 3 ).fromVertex(), 1 ); - QCOMPARE( graph->edge( 3 ).toVertex(), 2 ); + QCOMPARE( graph->vertex( 1 ).outgoingEdges(), QList< int >() << 1 << 2 ); + QCOMPARE( graph->vertex( 1 ).incomingEdges(), QList< int >() << 0 << 3 ); + QCOMPARE( graph->edge( 3 ).fromVertex(), 2 ); + QCOMPARE( graph->edge( 3 ).toVertex(), 1 ); + QCOMPARE( graph->edge( 2 ).fromVertex(), 1 ); + QCOMPARE( graph->edge( 2 ).toVertex(), 2 ); QCOMPARE( graph->vertex( 2 ).point(), QgsPointXY( 10, 10 ) ); - QCOMPARE( graph->vertex( 2 ).outgoingEdges(), QList< int >() << 2 ); - QCOMPARE( graph->vertex( 2 ).incomingEdges(), QList< int >() << 3 ); + QCOMPARE( graph->vertex( 2 ).outgoingEdges(), QList< int >() << 3 ); + QCOMPARE( graph->vertex( 2 ).incomingEdges(), QList< int >() << 2 ); builder = qgis::make_unique< QgsGraphBuilder > ( network->sourceCrs(), true, 0 ); director->makeGraph( builder.get(), QVector() << QgsPointXY( 10, 0 ) << QgsPointXY( 10, 10 ), snapped ); @@ -205,30 +220,30 @@ void TestQgsNetworkAnalysis::testBuildTolerance() QCOMPARE( graph->vertexCount(), 5 ); QCOMPARE( graph->edgeCount(), 6 ); QCOMPARE( graph->vertex( 0 ).point(), QgsPointXY( 0, 0 ) ); - QCOMPARE( graph->vertex( 0 ).outgoingEdges(), QList< int >() << 1 ); - QCOMPARE( graph->edge( 1 ).fromVertex(), 0 ); - QCOMPARE( graph->edge( 1 ).toVertex(), 1 ); - QCOMPARE( graph->vertex( 0 ).incomingEdges(), QList< int >() << 0 ); - QCOMPARE( graph->edge( 0 ).fromVertex(), 1 ); - QCOMPARE( graph->edge( 0 ).toVertex(), 0 ); + QCOMPARE( graph->vertex( 0 ).outgoingEdges(), QList< int >() << 0 ); + QCOMPARE( graph->edge( 0 ).fromVertex(), 0 ); + QCOMPARE( graph->edge( 0 ).toVertex(), 1 ); + QCOMPARE( graph->vertex( 0 ).incomingEdges(), QList< int >() << 1 ); + QCOMPARE( graph->edge( 1 ).fromVertex(), 1 ); + QCOMPARE( graph->edge( 1 ).toVertex(), 0 ); QCOMPARE( graph->vertex( 1 ).point(), QgsPointXY( 10, 0 ) ); - QCOMPARE( graph->vertex( 1 ).outgoingEdges(), QList< int >() << 0 << 3 ); - QCOMPARE( graph->vertex( 1 ).incomingEdges(), QList< int >() << 1 << 2 ); - QCOMPARE( graph->edge( 2 ).fromVertex(), 2 ); - QCOMPARE( graph->edge( 2 ).toVertex(), 1 ); - QCOMPARE( graph->edge( 3 ).fromVertex(), 1 ); - QCOMPARE( graph->edge( 3 ).toVertex(), 2 ); + QCOMPARE( graph->vertex( 1 ).outgoingEdges(), QList< int >() << 1 << 2 ); + QCOMPARE( graph->vertex( 1 ).incomingEdges(), QList< int >() << 0 << 3 ); + QCOMPARE( graph->edge( 3 ).fromVertex(), 2 ); + QCOMPARE( graph->edge( 3 ).toVertex(), 1 ); + QCOMPARE( graph->edge( 2 ).fromVertex(), 1 ); + QCOMPARE( graph->edge( 2 ).toVertex(), 2 ); QCOMPARE( graph->vertex( 2 ).point(), QgsPointXY( 10, 10 ) ); - QCOMPARE( graph->vertex( 2 ).outgoingEdges(), QList< int >() << 2 ); - QCOMPARE( graph->vertex( 2 ).incomingEdges(), QList< int >() << 3 ); + QCOMPARE( graph->vertex( 2 ).outgoingEdges(), QList< int >() << 3 ); + QCOMPARE( graph->vertex( 2 ).incomingEdges(), QList< int >() << 2 ); QCOMPARE( graph->vertex( 3 ).point(), QgsPointXY( 10.1, 10 ) ); - QCOMPARE( graph->vertex( 3 ).outgoingEdges(), QList< int >() << 5 ); - QCOMPARE( graph->vertex( 3 ).incomingEdges(), QList< int >() << 4 ); + QCOMPARE( graph->vertex( 3 ).outgoingEdges(), QList< int >() << 4 ); + QCOMPARE( graph->vertex( 3 ).incomingEdges(), QList< int >() << 5 ); QCOMPARE( graph->vertex( 4 ).point(), QgsPointXY( 20, 10 ) ); - QCOMPARE( graph->edge( 4 ).fromVertex(), 4 ); - QCOMPARE( graph->edge( 4 ).toVertex(), 3 ); - QCOMPARE( graph->edge( 5 ).fromVertex(), 3 ); - QCOMPARE( graph->edge( 5 ).toVertex(), 4 ); + QCOMPARE( graph->edge( 5 ).fromVertex(), 4 ); + QCOMPARE( graph->edge( 5 ).toVertex(), 3 ); + QCOMPARE( graph->edge( 4 ).fromVertex(), 3 ); + QCOMPARE( graph->edge( 4 ).toVertex(), 4 ); // with tolerance double tolerance = 0.11; @@ -239,29 +254,212 @@ void TestQgsNetworkAnalysis::testBuildTolerance() QCOMPARE( graph->vertexCount(), 4 ); QCOMPARE( graph->edgeCount(), 6 ); QCOMPARE( graph->vertex( 0 ).point(), QgsPointXY( 0, 0 ) ); - QCOMPARE( graph->vertex( 0 ).outgoingEdges(), QList< int >() << 1 ); - QCOMPARE( graph->edge( 1 ).fromVertex(), 0 ); - QCOMPARE( graph->edge( 1 ).toVertex(), 1 ); - QCOMPARE( graph->vertex( 0 ).incomingEdges(), QList< int >() << 0 ); - QCOMPARE( graph->edge( 0 ).fromVertex(), 1 ); - QCOMPARE( graph->edge( 0 ).toVertex(), 0 ); + QCOMPARE( graph->vertex( 0 ).outgoingEdges(), QList< int >() << 0 ); + QCOMPARE( graph->edge( 0 ).fromVertex(), 0 ); + QCOMPARE( graph->edge( 0 ).toVertex(), 1 ); + QCOMPARE( graph->vertex( 0 ).incomingEdges(), QList< int >() << 1 ); + QCOMPARE( graph->edge( 1 ).fromVertex(), 1 ); + QCOMPARE( graph->edge( 1 ).toVertex(), 0 ); QCOMPARE( graph->vertex( 1 ).point(), QgsPointXY( 10, 0 ) ); - QCOMPARE( graph->vertex( 1 ).outgoingEdges(), QList< int >() << 0 << 3 ); - QCOMPARE( graph->vertex( 1 ).incomingEdges(), QList< int >() << 1 << 2 ); - QCOMPARE( graph->edge( 2 ).fromVertex(), 2 ); - QCOMPARE( graph->edge( 2 ).toVertex(), 1 ); - QCOMPARE( graph->edge( 3 ).fromVertex(), 1 ); - QCOMPARE( graph->edge( 3 ).toVertex(), 2 ); + QCOMPARE( graph->vertex( 1 ).outgoingEdges(), QList< int >() << 1 << 2 ); + QCOMPARE( graph->vertex( 1 ).incomingEdges(), QList< int >() << 0 << 3 ); + QCOMPARE( graph->edge( 3 ).fromVertex(), 2 ); + QCOMPARE( graph->edge( 3 ).toVertex(), 1 ); + QCOMPARE( graph->edge( 2 ).fromVertex(), 1 ); + QCOMPARE( graph->edge( 2 ).toVertex(), 2 ); QCOMPARE( graph->vertex( 2 ).point(), QgsPointXY( 10, 10 ) ); - QCOMPARE( graph->vertex( 2 ).outgoingEdges(), QList< int >() << 2 << 5 ); - QCOMPARE( graph->vertex( 2 ).incomingEdges(), QList< int >() << 3 << 4 ); + QCOMPARE( graph->vertex( 2 ).outgoingEdges(), QList< int >() << 3 << 4 ); + QCOMPARE( graph->vertex( 2 ).incomingEdges(), QList< int >() << 2 << 5 ); QCOMPARE( graph->vertex( 3 ).point(), QgsPointXY( 20, 10 ) ); - QCOMPARE( graph->vertex( 3 ).outgoingEdges(), QList< int >() << 4 ); - QCOMPARE( graph->vertex( 3 ).incomingEdges(), QList< int >() << 5 ); - QCOMPARE( graph->edge( 4 ).fromVertex(), 3 ); - QCOMPARE( graph->edge( 4 ).toVertex(), 2 ); - QCOMPARE( graph->edge( 5 ).fromVertex(), 2 ); - QCOMPARE( graph->edge( 5 ).toVertex(), 3 ); + QCOMPARE( graph->vertex( 3 ).outgoingEdges(), QList< int >() << 5 ); + QCOMPARE( graph->vertex( 3 ).incomingEdges(), QList< int >() << 4 ); + QCOMPARE( graph->edge( 5 ).fromVertex(), 3 ); + QCOMPARE( graph->edge( 5 ).toVertex(), 2 ); + QCOMPARE( graph->edge( 4 ).fromVertex(), 2 ); + QCOMPARE( graph->edge( 4 ).toVertex(), 3 ); +} + +void TestQgsNetworkAnalysis::dijkkjkjkskkjsktra() +{ + std::unique_ptr network = buildNetwork(); + // has already a linestring LineString(0 0, 10 0, 10 10) + + // add some more lines to network + QgsFeature ff( 0 ); + QgsFeatureList flist; + + ff.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString(10 10, 20 10 )" ) ) ); + ff.setAttributes( QgsAttributes() << 2 ); + flist << ff; + ff.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString(10 20, 10 10 )" ) ) ); + ff.setAttributes( QgsAttributes() << 3 ); + flist << ff; + ff.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString(20 -10, 20 10 )" ) ) ); + ff.setAttributes( QgsAttributes() << 4 ); + flist << ff; + network->dataProvider()->addFeatures( flist ); + + /* + Out network is: + Numbers in brackets are cost for segment + + 20 o + | + v (3) + | (2) + 10 o---->----o + | | + (1) ^ ^ + (1) | | + 0 o---->-----. |(4) + | + ^ + | + -10 o + + 0 10 20 + + */ + + // build graph + std::unique_ptr< QgsVectorLayerDirector > director = qgis::make_unique< QgsVectorLayerDirector > ( network.get(), + -1, QString(), QString(), QString(), QgsVectorLayerDirector::DirectionBoth ); + std::unique_ptr< QgsNetworkStrategy > strategy = qgis::make_unique< TestNetworkStrategy >(); + director->addStrategy( strategy.release() ); + std::unique_ptr< QgsGraphBuilder > builder = qgis::make_unique< QgsGraphBuilder > ( network->sourceCrs(), true, 0 ); + + QVector snapped; + director->makeGraph( builder.get(), QVector(), snapped ); + std::unique_ptr< QgsGraph > graph( builder->graph() ); + + int startVertexIdx = graph->findVertex( QgsPointXY( 20, -10 ) ); + QVERIFY( startVertexIdx != -1 ); + + // both directions + QVector resultTree; + QVector resultCost; + QgsGraphAnalyzer::dijkstra( graph.get(), startVertexIdx, 0, &resultTree, &resultCost ); + + int point_0_0_idx = graph->findVertex( QgsPointXY( 0, 0 ) ); + QVERIFY( point_0_0_idx != -1 ); + int point_10_0_idx = graph->findVertex( QgsPointXY( 10, 0 ) ); + QVERIFY( point_10_0_idx != -1 ); + int point_10_10_idx = graph->findVertex( QgsPointXY( 10, 10 ) ); + QVERIFY( point_10_10_idx != -1 ); + int point_10_20_idx = graph->findVertex( QgsPointXY( 10, 20 ) ); + QVERIFY( point_10_20_idx != -1 ); + int point_20_10_idx = graph->findVertex( QgsPointXY( 20, 10 ) ); + QVERIFY( point_20_10_idx != -1 ); + + QCOMPARE( resultTree.at( startVertexIdx ), -1 ); + QCOMPARE( resultCost.at( startVertexIdx ), 0.0 ); + + QVERIFY( resultTree.at( point_20_10_idx ) != -1 ); + QCOMPARE( resultCost.at( point_20_10_idx ), 4.0 ); + QCOMPARE( graph->edge( resultTree.at( point_20_10_idx ) ).fromVertex(), startVertexIdx ); + QCOMPARE( graph->edge( resultTree.at( point_20_10_idx ) ).toVertex(), point_20_10_idx ); + QVERIFY( resultTree.at( point_10_10_idx ) != -1 ); + QCOMPARE( resultCost.at( point_10_10_idx ), 6.0 ); + QCOMPARE( graph->edge( resultTree.at( point_10_10_idx ) ).fromVertex(), point_20_10_idx ); + QCOMPARE( graph->edge( resultTree.at( point_10_10_idx ) ).toVertex(), point_10_10_idx ); + QVERIFY( resultTree.at( point_10_20_idx ) != -1 ); + QCOMPARE( resultCost.at( point_10_20_idx ), 9.0 ); + QCOMPARE( graph->edge( resultTree.at( point_10_20_idx ) ).fromVertex(), point_10_10_idx ); + QCOMPARE( graph->edge( resultTree.at( point_10_20_idx ) ).toVertex(), point_10_20_idx ); + QVERIFY( resultTree.at( point_10_0_idx ) != -1 ); + QCOMPARE( resultCost.at( point_10_0_idx ), 7.0 ); + QCOMPARE( graph->edge( resultTree.at( point_10_0_idx ) ).fromVertex(), point_10_10_idx ); + QCOMPARE( graph->edge( resultTree.at( point_10_0_idx ) ).toVertex(), point_10_0_idx ); + QVERIFY( resultTree.at( point_0_0_idx ) != -1 ); + QCOMPARE( resultCost.at( point_0_0_idx ), 8.0 ); + QCOMPARE( graph->edge( resultTree.at( point_0_0_idx ) ).fromVertex(), point_10_0_idx ); + QCOMPARE( graph->edge( resultTree.at( point_0_0_idx ) ).toVertex(), point_0_0_idx ); + + // forward direction + director = qgis::make_unique< QgsVectorLayerDirector > ( network.get(), + -1, QString(), QString(), QString(), QgsVectorLayerDirector::DirectionForward ); + strategy = qgis::make_unique< TestNetworkStrategy >(); + director->addStrategy( strategy.release() ); + builder = qgis::make_unique< QgsGraphBuilder > ( network->sourceCrs(), true, 0 ); + director->makeGraph( builder.get(), QVector(), snapped ); + graph.reset( builder->graph() ); + startVertexIdx = graph->findVertex( QgsPointXY( 0, 0 ) ); + QVERIFY( startVertexIdx != -1 ); + resultTree.clear(); + resultCost.clear(); + QgsGraphAnalyzer::dijkstra( graph.get(), startVertexIdx, 0, &resultTree, &resultCost ); + point_0_0_idx = graph->findVertex( QgsPointXY( 0, 0 ) ); + QVERIFY( point_0_0_idx != -1 ); + point_10_0_idx = graph->findVertex( QgsPointXY( 10, 0 ) ); + QVERIFY( point_10_0_idx != -1 ); + point_10_10_idx = graph->findVertex( QgsPointXY( 10, 10 ) ); + QVERIFY( point_10_10_idx != -1 ); + point_10_20_idx = graph->findVertex( QgsPointXY( 10, 20 ) ); + QVERIFY( point_10_20_idx != -1 ); + point_20_10_idx = graph->findVertex( QgsPointXY( 20, 10 ) ); + QVERIFY( point_20_10_idx != -1 ); + int point_20_n10_idx = graph->findVertex( QgsPointXY( 20, -10 ) ); + QVERIFY( point_20_n10_idx != -1 ); + + QCOMPARE( resultTree.at( startVertexIdx ), -1 ); + QCOMPARE( resultCost.at( startVertexIdx ), 0.0 ); + QVERIFY( resultTree.at( point_10_0_idx ) != -1 ); + QCOMPARE( resultCost.at( point_10_0_idx ), 1.0 ); + QCOMPARE( graph->edge( resultTree.at( point_10_0_idx ) ).fromVertex(), startVertexIdx ); + QCOMPARE( graph->edge( resultTree.at( point_10_0_idx ) ).toVertex(), point_10_0_idx ); + QVERIFY( resultTree.at( point_10_10_idx ) != -1 ); + QCOMPARE( resultCost.at( point_10_10_idx ), 2.0 ); + QCOMPARE( graph->edge( resultTree.at( point_10_10_idx ) ).fromVertex(), point_10_0_idx ); + QCOMPARE( graph->edge( resultTree.at( point_10_10_idx ) ).toVertex(), point_10_10_idx ); + QCOMPARE( resultTree.at( point_10_20_idx ), -1 ); // unreachable + QVERIFY( resultTree.at( point_20_10_idx ) != -1 ); + QCOMPARE( resultCost.at( point_20_10_idx ), 4.0 ); + QCOMPARE( graph->edge( resultTree.at( point_20_10_idx ) ).fromVertex(), point_10_10_idx ); + QCOMPARE( graph->edge( resultTree.at( point_20_10_idx ) ).toVertex(), point_20_10_idx ); + QCOMPARE( resultTree.at( point_20_n10_idx ), -1 ); // unreachable + + // backward direction + director = qgis::make_unique< QgsVectorLayerDirector > ( network.get(), + -1, QString(), QString(), QString(), QgsVectorLayerDirector::DirectionBackward ); + strategy = qgis::make_unique< TestNetworkStrategy >(); + director->addStrategy( strategy.release() ); + builder = qgis::make_unique< QgsGraphBuilder > ( network->sourceCrs(), true, 0 ); + director->makeGraph( builder.get(), QVector(), snapped ); + graph.reset( builder->graph() ); + startVertexIdx = graph->findVertex( QgsPointXY( 10, 10 ) ); + QVERIFY( startVertexIdx != -1 ); + resultTree.clear(); + resultCost.clear(); + QgsGraphAnalyzer::dijkstra( graph.get(), startVertexIdx, 0, &resultTree, &resultCost ); + point_0_0_idx = graph->findVertex( QgsPointXY( 0, 0 ) ); + QVERIFY( point_0_0_idx != -1 ); + point_10_0_idx = graph->findVertex( QgsPointXY( 10, 0 ) ); + QVERIFY( point_10_0_idx != -1 ); + point_10_10_idx = graph->findVertex( QgsPointXY( 10, 10 ) ); + QVERIFY( point_10_10_idx != -1 ); + point_10_20_idx = graph->findVertex( QgsPointXY( 10, 20 ) ); + QVERIFY( point_10_20_idx != -1 ); + point_20_10_idx = graph->findVertex( QgsPointXY( 20, 10 ) ); + QVERIFY( point_20_10_idx != -1 ); + point_20_n10_idx = graph->findVertex( QgsPointXY( 20, -10 ) ); + QVERIFY( point_20_n10_idx != -1 ); + + QCOMPARE( resultTree.at( startVertexIdx ), -1 ); + QCOMPARE( resultCost.at( startVertexIdx ), 0.0 ); + QCOMPARE( resultTree.at( point_20_10_idx ), -1 ); + QCOMPARE( resultTree.at( point_20_n10_idx ), -1 ); + QVERIFY( resultTree.at( point_10_20_idx ) != -1 ); + QCOMPARE( resultCost.at( point_10_20_idx ), 3.0 ); + QCOMPARE( graph->edge( resultTree.at( point_10_20_idx ) ).fromVertex(), startVertexIdx ); + QCOMPARE( graph->edge( resultTree.at( point_10_20_idx ) ).toVertex(), point_10_20_idx ); + QVERIFY( resultTree.at( point_10_0_idx ) != -1 ); + QCOMPARE( resultCost.at( point_10_0_idx ), 1.0 ); + QCOMPARE( graph->edge( resultTree.at( point_10_0_idx ) ).fromVertex(), startVertexIdx ); + QCOMPARE( graph->edge( resultTree.at( point_10_0_idx ) ).toVertex(), point_10_0_idx ); + QVERIFY( resultTree.at( point_0_0_idx ) != -1 ); + QCOMPARE( resultCost.at( point_0_0_idx ), 2.0 ); + QCOMPARE( graph->edge( resultTree.at( point_0_0_idx ) ).fromVertex(), point_10_0_idx ); + QCOMPARE( graph->edge( resultTree.at( point_0_0_idx ) ).toVertex(), point_0_0_idx ); }