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