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