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