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