QGIS API Documentation 3.41.0-Master (57ec4277f5e)
Loading...
Searching...
No Matches
qgsalgorithmshortestpathlayertopoint.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmshortestpathlayertopoint.cpp
3 ---------------------
4 begin : July 2018
5 copyright : (C) 2018 by Alexander Bruy
6 email : alexander dot bruy at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsgraphanalyzer.h"
21
22#include "qgsmessagelog.h"
23
25
26QString QgsShortestPathLayerToPointAlgorithm::name() const
27{
28 return QStringLiteral( "shortestpathlayertopoint" );
29}
30
31QString QgsShortestPathLayerToPointAlgorithm::displayName() const
32{
33 return QObject::tr( "Shortest path (layer to point)" );
34}
35
36QStringList QgsShortestPathLayerToPointAlgorithm::tags() const
37{
38 return QObject::tr( "network,path,shortest,fastest" ).split( ',' );
39}
40
41QString QgsShortestPathLayerToPointAlgorithm::shortHelpString() const
42{
43 return QObject::tr( "This algorithm computes optimal (shortest or fastest) route from multiple start points defined by vector layer and given end point." );
44}
45
46QgsShortestPathLayerToPointAlgorithm *QgsShortestPathLayerToPointAlgorithm::createInstance() const
47{
48 return new QgsShortestPathLayerToPointAlgorithm();
49}
50
51void QgsShortestPathLayerToPointAlgorithm::initAlgorithm( const QVariantMap & )
52{
53 addCommonParams();
54 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "START_POINTS" ), QObject::tr( "Vector layer with start points" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) );
55 addParameter( new QgsProcessingParameterPoint( QStringLiteral( "END_POINT" ), QObject::tr( "End point" ) ) );
56
57 std::unique_ptr<QgsProcessingParameterNumber> maxEndPointDistanceFromNetwork = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "POINT_TOLERANCE" ), QObject::tr( "Maximum point distance from network" ), QVariant(), QStringLiteral( "INPUT" ), true, 0 );
58 maxEndPointDistanceFromNetwork->setFlags( maxEndPointDistanceFromNetwork->flags() | Qgis::ProcessingParameterFlag::Advanced );
59 maxEndPointDistanceFromNetwork->setHelp( QObject::tr( "Specifies an optional limit on the distance from the start and end points to the network layer. If the start feature is further from the network than this distance it will be treated as non-routable. If the end point is further from the network than this distance an error will be raised." ) );
60 addParameter( maxEndPointDistanceFromNetwork.release() );
61
62 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Shortest path" ), Qgis::ProcessingSourceType::VectorLine ) );
63
64 std::unique_ptr<QgsProcessingParameterFeatureSink> outputNonRoutable = std::make_unique<QgsProcessingParameterFeatureSink>( QStringLiteral( "OUTPUT_NON_ROUTABLE" ), QObject::tr( "Non-routable features" ), Qgis::ProcessingSourceType::VectorPoint, QVariant(), true );
65 outputNonRoutable->setHelp( QObject::tr( "An optional output which will be used to store any input features which could not be routed (e.g. those which are too far from the network layer)." ) );
66 outputNonRoutable->setCreateByDefault( false );
67 addParameter( outputNonRoutable.release() );
68}
69
70QVariantMap QgsShortestPathLayerToPointAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
71{
72 loadCommonParams( parameters, context, feedback );
73
74 const QgsPointXY endPoint = parameterAsPoint( parameters, QStringLiteral( "END_POINT" ), context, mNetwork->sourceCrs() );
75
76 std::unique_ptr<QgsFeatureSource> startPoints( parameterAsSource( parameters, QStringLiteral( "START_POINTS" ), context ) );
77 if ( !startPoints )
78 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "START_POINTS" ) ) );
79
80 QgsFields fields = startPoints->fields();
81 fields.append( QgsField( QStringLiteral( "start" ), QMetaType::Type::QString ) );
82 fields.append( QgsField( QStringLiteral( "end" ), QMetaType::Type::QString ) );
83 fields.append( QgsField( QStringLiteral( "cost" ), QMetaType::Type::Double ) );
84
85 QString dest;
86 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::LineString, mNetwork->sourceCrs() ) );
87 if ( !sink )
88 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
89
90 QString nonRoutableSinkId;
91 std::unique_ptr<QgsFeatureSink> nonRoutableSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_NON_ROUTABLE" ), context, nonRoutableSinkId, startPoints->fields(), Qgis::WkbType::Point, mNetwork->sourceCrs() ) );
92
93 const double pointDistanceThreshold = parameters.value( QStringLiteral( "POINT_TOLERANCE" ) ).isValid() ? parameterAsDouble( parameters, QStringLiteral( "POINT_TOLERANCE" ), context ) : -1;
94
95 QVector<QgsPointXY> points;
96 points.push_front( endPoint );
97 QHash<int, QgsAttributes> sourceAttributes;
98 loadPoints( startPoints.get(), points, sourceAttributes, context, feedback );
99
100 feedback->pushInfo( QObject::tr( "Building graph…" ) );
101 QVector<QgsPointXY> snappedPoints;
102 mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback );
103
104 const QgsPointXY snappedEndPoint = snappedPoints[0];
105
106 if ( pointDistanceThreshold >= 0 )
107 {
108 double distanceEndPointToNetwork = 0;
109 try
110 {
111 distanceEndPointToNetwork = mBuilder->distanceArea()->measureLine( endPoint, snappedEndPoint );
112 }
113 catch ( QgsCsException & )
114 {
115 throw QgsProcessingException( QObject::tr( "An error occurred while calculating length" ) );
116 }
117
118 if ( distanceEndPointToNetwork > pointDistanceThreshold )
119 {
120 throw QgsProcessingException( QObject::tr( "End point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distanceEndPointToNetwork ).arg( pointDistanceThreshold ) );
121 }
122 }
123
124 feedback->pushInfo( QObject::tr( "Calculating shortest paths…" ) );
125 std::unique_ptr<QgsGraph> graph( mBuilder->takeGraph() );
126 const int idxEnd = graph->findVertex( snappedEndPoint );
127 int idxStart;
128 int currentIdx;
129
130 QVector<int> tree;
131 QVector<double> costs;
132
133 QVector<QgsPointXY> route;
134 double cost;
135
136 QgsFeature feat;
137 feat.setFields( fields );
138 QgsAttributes attributes;
139
140 const double step = points.size() > 0 ? 100.0 / points.size() : 1;
141 for ( int i = 1; i < points.size(); i++ )
142 {
143 if ( feedback->isCanceled() )
144 {
145 break;
146 }
147
148 const QgsPointXY snappedPoint = snappedPoints.at( i );
149 const QgsPointXY originalPoint = points.at( i );
150
151 if ( pointDistanceThreshold >= 0 )
152 {
153 double distancePointToNetwork = 0;
154 try
155 {
156 distancePointToNetwork = mBuilder->distanceArea()->measureLine( originalPoint, snappedPoint );
157 }
158 catch ( QgsCsException & )
159 {
160 throw QgsProcessingException( QObject::tr( "An error occurred while calculating length" ) );
161 }
162
163 if ( distancePointToNetwork > pointDistanceThreshold )
164 {
165 feedback->pushWarning( QObject::tr( "Point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distancePointToNetwork ).arg( pointDistanceThreshold ) );
166 if ( nonRoutableSink )
167 {
168 feat.setGeometry( QgsGeometry::fromPointXY( originalPoint ) );
169 attributes = sourceAttributes.value( i );
170 feat.setAttributes( attributes );
171 if ( !nonRoutableSink->addFeature( feat, QgsFeatureSink::FastInsert ) )
172 throw QgsProcessingException( writeFeatureError( nonRoutableSink.get(), parameters, QStringLiteral( "OUTPUT_NON_ROUTABLE" ) ) );
173 }
174
175 feedback->setProgress( i * step );
176 continue;
177 }
178 }
179
180 idxStart = graph->findVertex( snappedPoint );
181 QgsGraphAnalyzer::dijkstra( graph.get(), idxStart, 0, &tree, &costs );
182
183 if ( tree.at( idxEnd ) == -1 )
184 {
185 feedback->reportError( QObject::tr( "There is no route from start point (%1) to end point (%2)." )
186 .arg( originalPoint.toString(), endPoint.toString() ) );
187 feat.clearGeometry();
188 attributes = sourceAttributes.value( i );
189 attributes.append( originalPoint.toString() );
190 feat.setAttributes( attributes );
191 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
192 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
193 continue;
194 }
195
196 route.clear();
197 route.push_front( graph->vertex( idxEnd ).point() );
198 cost = costs.at( idxEnd );
199 currentIdx = idxEnd;
200 while ( currentIdx != idxStart )
201 {
202 currentIdx = graph->edge( tree.at( currentIdx ) ).fromVertex();
203 route.push_front( graph->vertex( currentIdx ).point() );
204 }
205
206 const QgsGeometry geom = QgsGeometry::fromPolylineXY( route );
207 QgsFeature feat;
208 feat.setFields( fields );
209 attributes = sourceAttributes.value( i );
210 attributes.append( originalPoint.toString() );
211 attributes.append( endPoint.toString() );
212 attributes.append( cost / mMultiplier );
213 feat.setAttributes( attributes );
214 feat.setGeometry( geom );
215 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
216 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
217
218 feedback->setProgress( i * step );
219 }
220
221 if ( sink )
222 sink->finalize();
223
224 QVariantMap outputs;
225 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
226 if ( nonRoutableSink )
227 {
228 nonRoutableSink->finalize();
229 outputs.insert( QStringLiteral( "OUTPUT_NON_ROUTABLE" ), nonRoutableSinkId );
230 }
231 return outputs;
232}
233
@ VectorPoint
Vector point layers.
@ VectorLine
Vector line layers.
@ LineString
LineString.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
A vector of attributes.
Custom exception class for Coordinate Reference System related exceptions.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
void clearGeometry()
Removes any geometry associated with the feature.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:70
A geometry is the spatial representation of a feature.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
static void dijkstra(const QgsGraph *source, int startVertexIdx, int criterionNum, QVector< int > *resultTree=nullptr, QVector< double > *resultCost=nullptr)
Solve shortest path problem using Dijkstra algorithm.
A class to represent a 2D point.
Definition qgspointxy.h:60
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A point parameter for processing algorithms.