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