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