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