QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 loadCommonParams( parameters, context, feedback );
98
99 const QgsPointXY startPoint = parameterAsPoint( parameters, u"START_POINT"_s, context, mNetwork->sourceCrs() );
100
101 std::unique_ptr<QgsFeatureSource> endPoints( parameterAsSource( parameters, u"END_POINTS"_s, context ) );
102 if ( !endPoints )
103 throw QgsProcessingException( invalidSourceError( parameters, u"END_POINTS"_s ) );
104
105 QgsFields newFields;
106 newFields.append( QgsField( u"start"_s, QMetaType::Type::QString ) );
107 newFields.append( QgsField( u"end"_s, QMetaType::Type::QString ) );
108 newFields.append( QgsField( u"cost"_s, QMetaType::Type::Double ) );
109 QgsFields fields = QgsProcessingUtils::combineFields( endPoints->fields(), newFields );
110
111 QString dest;
112 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, fields, Qgis::WkbType::LineString, mNetwork->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
113 if ( !sink )
114 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
115
116 QString nonRoutableSinkId;
117 std::unique_ptr<QgsFeatureSink> nonRoutableSink( parameterAsSink( parameters, u"OUTPUT_NON_ROUTABLE"_s, context, nonRoutableSinkId, endPoints->fields(), Qgis::WkbType::Point, mNetwork->sourceCrs() ) );
118
119 const double pointDistanceThreshold = parameters.value( u"POINT_TOLERANCE"_s ).isValid() ? parameterAsDouble( parameters, u"POINT_TOLERANCE"_s, context ) : -1;
120
121 QVector<QgsPointXY> points;
122 points.push_front( startPoint );
123 QHash<int, QgsAttributes> sourceAttributes;
124 loadPoints( endPoints.get(), &points, &sourceAttributes, context, feedback, nullptr );
125
126 feedback->pushInfo( QObject::tr( "Building graph…" ) );
127 QVector<QgsPointXY> snappedPoints;
128 mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback );
129
130 const QgsPointXY snappedStartPoint = snappedPoints[0];
131
132 if ( pointDistanceThreshold >= 0 )
133 {
134 double distanceStartPointToNetwork = 0;
135 try
136 {
137 distanceStartPointToNetwork = mBuilder->distanceArea()->measureLine( startPoint, snappedStartPoint );
138 }
139 catch ( QgsCsException & )
140 {
141 throw QgsProcessingException( QObject::tr( "An error occurred while calculating length" ) );
142 }
143
144 if ( distanceStartPointToNetwork > pointDistanceThreshold )
145 {
146 throw QgsProcessingException( QObject::tr( "Start point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distanceStartPointToNetwork ).arg( pointDistanceThreshold ) );
147 }
148 }
149
150 feedback->pushInfo( QObject::tr( "Calculating shortest paths…" ) );
151 std::unique_ptr<QgsGraph> graph( mBuilder->takeGraph() );
152 const int idxStart = graph->findVertex( snappedStartPoint );
153 int idxEnd;
154
155 QVector<int> tree;
156 QVector<double> costs;
157 QgsGraphAnalyzer::dijkstra( graph.get(), idxStart, 0, &tree, &costs );
158
159 QVector<QgsPointXY> route;
160 double cost;
161
162 QgsFeature feat;
163 feat.setFields( fields );
164 QgsAttributes attributes;
165
166 const double step = points.size() > 0 ? 100.0 / points.size() : 1;
167 for ( int i = 1; i < points.size(); i++ )
168 {
169 if ( feedback->isCanceled() )
170 {
171 break;
172 }
173
174 const QgsPointXY snappedPoint = snappedPoints.at( i );
175 const QgsPointXY originalPoint = points.at( i );
176
177 if ( pointDistanceThreshold >= 0 )
178 {
179 double distancePointToNetwork = 0;
180 try
181 {
182 distancePointToNetwork = mBuilder->distanceArea()->measureLine( originalPoint, snappedPoint );
183 }
184 catch ( QgsCsException & )
185 {
186 throw QgsProcessingException( QObject::tr( "An error occurred while calculating length" ) );
187 }
188
189 if ( distancePointToNetwork > pointDistanceThreshold )
190 {
191 feedback->pushWarning( QObject::tr( "Point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distancePointToNetwork ).arg( pointDistanceThreshold ) );
192 if ( nonRoutableSink )
193 {
194 feat.setGeometry( QgsGeometry::fromPointXY( originalPoint ) );
195 attributes = sourceAttributes.value( i );
196 feat.setAttributes( attributes );
197 if ( !nonRoutableSink->addFeature( feat, QgsFeatureSink::FastInsert ) )
198 throw QgsProcessingException( writeFeatureError( nonRoutableSink.get(), parameters, u"OUTPUT_NON_ROUTABLE"_s ) );
199 }
200
201 feedback->setProgress( i * step );
202 continue;
203 }
204 }
205
206 idxEnd = graph->findVertex( snappedPoint );
207 if ( tree.at( idxEnd ) == -1 )
208 {
209 feedback->reportError( QObject::tr( "There is no route from start point (%1) to end point (%2)." ).arg( startPoint.toString(), originalPoint.toString() ) );
210 feat.clearGeometry();
211 attributes = sourceAttributes.value( i );
212 attributes.append( QVariant() );
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 continue;
218 }
219
220 route.clear();
221 route.push_front( graph->vertex( idxEnd ).point() );
222 cost = costs.at( idxEnd );
223 while ( idxEnd != idxStart )
224 {
225 idxEnd = graph->edge( tree.at( idxEnd ) ).fromVertex();
226 route.push_front( graph->vertex( idxEnd ).point() );
227 }
228
229 const QgsGeometry geom = QgsGeometry::fromPolylineXY( route );
230 QgsFeature feat;
231 feat.setFields( fields );
232 attributes = sourceAttributes.value( i );
233 attributes.append( startPoint.toString() );
234 attributes.append( originalPoint.toString() );
235 attributes.append( cost / mMultiplier );
236 feat.setAttributes( attributes );
237 feat.setGeometry( geom );
238 if ( !sink->addFeature( feat, QgsFeatureSink::FastInsert ) )
239 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
240
241 feedback->setProgress( i * step );
242 }
243
244 sink->finalize();
245
246 QVariantMap outputs;
247 outputs.insert( u"OUTPUT"_s, dest );
248 if ( nonRoutableSink )
249 {
250 nonRoutableSink->finalize();
251 outputs.insert( u"OUTPUT_NON_ROUTABLE"_s, nonRoutableSinkId );
252 }
253 return outputs;
254}
255
@ VectorPoint
Vector point layers.
Definition qgis.h:3648
@ VectorLine
Vector line layers.
Definition qgis.h:3649
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
Definition qgis.h:3734
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3745
@ 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:3880
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:46
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.
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).