QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
qgsalgorithmshortestpathpointtolayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmshortestpathpointtolayer.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 QgsShortestPathPointToLayerAlgorithm::name() const
27{
28 return QStringLiteral( "shortestpathpointtolayer" );
29}
30
31QString QgsShortestPathPointToLayerAlgorithm::displayName() const
32{
33 return QObject::tr( "Shortest path (point to layer)" );
34}
35
36QStringList QgsShortestPathPointToLayerAlgorithm::tags() const
37{
38 return QObject::tr( "network,path,shortest,fastest" ).split( ',' );
39}
40
41QString QgsShortestPathPointToLayerAlgorithm::shortHelpString() const
42{
43 return QObject::tr( "This algorithm computes optimal (shortest or fastest) route between a given start point "
44 "and multiple end points defined by a point vector layer." );
45}
46
47QString QgsShortestPathPointToLayerAlgorithm::shortDescription() const
48{
49 return QObject::tr( "Computes optimal (shortest or fastest) route between a given start point "
50 "and multiple end points defined by a point vector layer." );
51}
52
53Qgis::ProcessingAlgorithmDocumentationFlags QgsShortestPathPointToLayerAlgorithm::documentationFlags() const
54{
56}
57
58QgsShortestPathPointToLayerAlgorithm *QgsShortestPathPointToLayerAlgorithm::createInstance() const
59{
60 return new QgsShortestPathPointToLayerAlgorithm();
61}
62
63void QgsShortestPathPointToLayerAlgorithm::initAlgorithm( const QVariantMap & )
64{
65 addCommonParams();
66 addParameter( new QgsProcessingParameterPoint( QStringLiteral( "START_POINT" ), QObject::tr( "Start point" ) ) );
67 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "END_POINTS" ), QObject::tr( "Vector layer with end points" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) );
68
69 std::unique_ptr<QgsProcessingParameterNumber> maxEndPointDistanceFromNetwork = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "POINT_TOLERANCE" ), QObject::tr( "Maximum point distance from network" ), QVariant(), QStringLiteral( "INPUT" ), true, 0 );
70 maxEndPointDistanceFromNetwork->setFlags( maxEndPointDistanceFromNetwork->flags() | Qgis::ProcessingParameterFlag::Advanced );
71 maxEndPointDistanceFromNetwork->setHelp( QObject::tr( "Specifies an optional limit on the distance from the start and end points to the network layer.If the start point is further from the network than this distance an error will be raised. If the end feature is further from the network than this distance it will be treated as non-routable." ) );
72 addParameter( maxEndPointDistanceFromNetwork.release() );
73
74 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Shortest path" ), Qgis::ProcessingSourceType::VectorLine ) );
75
76 auto outputNonRoutable = std::make_unique<QgsProcessingParameterFeatureSink>( QStringLiteral( "OUTPUT_NON_ROUTABLE" ), QObject::tr( "Non-routable features" ), Qgis::ProcessingSourceType::VectorPoint, QVariant(), true );
77 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)." ) );
78 outputNonRoutable->setCreateByDefault( false );
79 addParameter( outputNonRoutable.release() );
80}
81
82QVariantMap QgsShortestPathPointToLayerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
83{
84 loadCommonParams( parameters, context, feedback );
85
86 const QgsPointXY startPoint = parameterAsPoint( parameters, QStringLiteral( "START_POINT" ), context, mNetwork->sourceCrs() );
87
88 std::unique_ptr<QgsFeatureSource> endPoints( parameterAsSource( parameters, QStringLiteral( "END_POINTS" ), context ) );
89 if ( !endPoints )
90 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "END_POINTS" ) ) );
91
92 QgsFields newFields;
93 newFields.append( QgsField( QStringLiteral( "start" ), QMetaType::Type::QString ) );
94 newFields.append( QgsField( QStringLiteral( "end" ), QMetaType::Type::QString ) );
95 newFields.append( QgsField( QStringLiteral( "cost" ), QMetaType::Type::Double ) );
96 QgsFields fields = QgsProcessingUtils::combineFields( endPoints->fields(), newFields );
97
98 QString dest;
99 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::LineString, mNetwork->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
100 if ( !sink )
101 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
102
103 QString nonRoutableSinkId;
104 std::unique_ptr<QgsFeatureSink> nonRoutableSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_NON_ROUTABLE" ), context, nonRoutableSinkId, endPoints->fields(), Qgis::WkbType::Point, mNetwork->sourceCrs() ) );
105
106 const double pointDistanceThreshold = parameters.value( QStringLiteral( "POINT_TOLERANCE" ) ).isValid() ? parameterAsDouble( parameters, QStringLiteral( "POINT_TOLERANCE" ), context ) : -1;
107
108 QVector<QgsPointXY> points;
109 points.push_front( startPoint );
110 QHash<int, QgsAttributes> sourceAttributes;
111 loadPoints( endPoints.get(), points, sourceAttributes, context, feedback );
112
113 feedback->pushInfo( QObject::tr( "Building graph…" ) );
114 QVector<QgsPointXY> snappedPoints;
115 mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback );
116
117 const QgsPointXY snappedStartPoint = snappedPoints[0];
118
119 if ( pointDistanceThreshold >= 0 )
120 {
121 double distanceStartPointToNetwork = 0;
122 try
123 {
124 distanceStartPointToNetwork = mBuilder->distanceArea()->measureLine( startPoint, snappedStartPoint );
125 }
126 catch ( QgsCsException & )
127 {
128 throw QgsProcessingException( QObject::tr( "An error occurred while calculating length" ) );
129 }
130
131 if ( distanceStartPointToNetwork > pointDistanceThreshold )
132 {
133 throw QgsProcessingException( QObject::tr( "Start point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distanceStartPointToNetwork ).arg( pointDistanceThreshold ) );
134 }
135 }
136
137 feedback->pushInfo( QObject::tr( "Calculating shortest paths…" ) );
138 std::unique_ptr<QgsGraph> graph( mBuilder->takeGraph() );
139 const int idxStart = graph->findVertex( snappedStartPoint );
140 int idxEnd;
141
142 QVector<int> tree;
143 QVector<double> costs;
144 QgsGraphAnalyzer::dijkstra( graph.get(), idxStart, 0, &tree, &costs );
145
146 QVector<QgsPointXY> route;
147 double cost;
148
149 QgsFeature feat;
150 feat.setFields( fields );
151 QgsAttributes attributes;
152
153 const double step = points.size() > 0 ? 100.0 / points.size() : 1;
154 for ( int i = 1; i < points.size(); i++ )
155 {
156 if ( feedback->isCanceled() )
157 {
158 break;
159 }
160
161 const QgsPointXY snappedPoint = snappedPoints.at( i );
162 const QgsPointXY originalPoint = points.at( i );
163
164 if ( pointDistanceThreshold >= 0 )
165 {
166 double distancePointToNetwork = 0;
167 try
168 {
169 distancePointToNetwork = mBuilder->distanceArea()->measureLine( originalPoint, snappedPoint );
170 }
171 catch ( QgsCsException & )
172 {
173 throw QgsProcessingException( QObject::tr( "An error occurred while calculating length" ) );
174 }
175
176 if ( distancePointToNetwork > pointDistanceThreshold )
177 {
178 feedback->pushWarning( QObject::tr( "Point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distancePointToNetwork ).arg( pointDistanceThreshold ) );
179 if ( nonRoutableSink )
180 {
181 feat.setGeometry( QgsGeometry::fromPointXY( originalPoint ) );
182 attributes = sourceAttributes.value( i );
183 feat.setAttributes( attributes );
184 if ( !nonRoutableSink->addFeature( feat, QgsFeatureSink::FastInsert ) )
185 throw QgsProcessingException( writeFeatureError( nonRoutableSink.get(), parameters, QStringLiteral( "OUTPUT_NON_ROUTABLE" ) ) );
186 }
187
188 feedback->setProgress( i * step );
189 continue;
190 }
191 }
192
193 idxEnd = graph->findVertex( snappedPoint );
194 if ( tree.at( idxEnd ) == -1 )
195 {
196 feedback->reportError( QObject::tr( "There is no route from start point (%1) to end point (%2)." )
197 .arg( startPoint.toString(), originalPoint.toString() ) );
198 feat.clearGeometry();
199 attributes = sourceAttributes.value( i );
200 attributes.append( QVariant() );
201 attributes.append( originalPoint.toString() );
202 feat.setAttributes( attributes );
203 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
204 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
205 continue;
206 }
207
208 route.clear();
209 route.push_front( graph->vertex( idxEnd ).point() );
210 cost = costs.at( idxEnd );
211 while ( idxEnd != idxStart )
212 {
213 idxEnd = graph->edge( tree.at( idxEnd ) ).fromVertex();
214 route.push_front( graph->vertex( idxEnd ).point() );
215 }
216
217 const QgsGeometry geom = QgsGeometry::fromPolylineXY( route );
218 QgsFeature feat;
219 feat.setFields( fields );
220 attributes = sourceAttributes.value( i );
221 attributes.append( startPoint.toString() );
222 attributes.append( originalPoint.toString() );
223 attributes.append( cost / mMultiplier );
224 feat.setAttributes( attributes );
225 feat.setGeometry( geom );
226 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
227 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
228
229 feedback->setProgress( i * step );
230 }
231
232 sink->finalize();
233
234 QVariantMap outputs;
235 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
236 if ( nonRoutableSink )
237 {
238 nonRoutableSink->finalize();
239 outputs.insert( QStringLiteral( "OUTPUT_NON_ROUTABLE" ), nonRoutableSinkId );
240 }
241 return outputs;
242}
243
@ VectorPoint
Vector point layers.
@ VectorLine
Vector line layers.
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3496
@ 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...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
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.
Represents 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.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).