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