QGIS API Documentation 4.3.0-Master (2b7e6c9893e)
Loading...
Searching...
No Matches
qgsalgorithmserviceareafrompoint.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmserviceareafrompoint.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 "qgsgeometryutils.h"
21#include "qgsgraphanalyzer.h"
22
23#include <QString>
24
25using namespace Qt::StringLiterals;
26
28
29QString QgsServiceAreaFromPointAlgorithm::name() const
30{
31 return u"serviceareafrompoint"_s;
32}
33
34QString QgsServiceAreaFromPointAlgorithm::displayName() const
35{
36 return QObject::tr( "Service area (from point)" );
37}
38
39QStringList QgsServiceAreaFromPointAlgorithm::tags() const
40{
41 return QObject::tr( "network,service,area,shortest,fastest" ).split( ',' );
42}
43
44QString QgsServiceAreaFromPointAlgorithm::shortHelpString() const
45{
46 return QObject::tr(
47 "This algorithm creates a new vector layer with all the edges or parts of edges "
48 "of a network line layer that can be reached within a distance or a time, "
49 "starting from a point feature. The distance and the time (both referred to "
50 "as \"travel cost\") must be specified respectively in the network layer "
51 "units or in hours."
52 );
53}
54
55QString QgsServiceAreaFromPointAlgorithm::shortDescription() const
56{
57 return QObject::tr(
58 "Creates a vector layer with all the edges or parts of edges "
59 "of a network line layer that can be reached within a distance or a time, "
60 "starting from a point feature."
61 );
62}
63
64QgsServiceAreaFromPointAlgorithm *QgsServiceAreaFromPointAlgorithm::createInstance() const
65{
66 return new QgsServiceAreaFromPointAlgorithm();
67}
68
69void QgsServiceAreaFromPointAlgorithm::initAlgorithm( const QVariantMap & )
70{
71 addCommonParams();
72 addParameter( new QgsProcessingParameterPoint( u"START_POINT"_s, QObject::tr( "Start point" ) ) );
73
74 auto travelCost
75 = std::make_unique<QgsProcessingParameterNumber>( u"TRAVEL_COST"_s, QObject::tr( "Travel cost (distance for 'Shortest', time for 'Fastest')" ), Qgis::ProcessingNumberParameterType::Double, 0, true, 0 );
76 travelCost->setFlags( travelCost->flags() | Qgis::ProcessingParameterFlag::Hidden );
77 addParameter( travelCost.release() );
78
79 addParameter(
80 new QgsProcessingParameterNumber( u"TRAVEL_COST2"_s, QObject::tr( "Travel cost (distance for 'Shortest', time for 'Fastest')" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0 )
81 );
82
83 std::unique_ptr<QgsProcessingParameterNumber> maxPointDistanceFromNetwork
84 = std::make_unique<QgsProcessingParameterDistance>( u"POINT_TOLERANCE"_s, QObject::tr( "Maximum point distance from network" ), QVariant(), u"INPUT"_s, true, 0 );
85 maxPointDistanceFromNetwork->setFlags( maxPointDistanceFromNetwork->flags() | Qgis::ProcessingParameterFlag::Advanced );
86 maxPointDistanceFromNetwork->setHelp(
87 QObject::tr( "Specifies an optional limit on the distance from the point to the network layer. If the point is further from the network than this distance an error will be raised." )
88 );
89 addParameter( maxPointDistanceFromNetwork.release() );
90
91 auto includeBounds = std::make_unique<QgsProcessingParameterBoolean>( u"INCLUDE_BOUNDS"_s, QObject::tr( "Include upper/lower bound points" ), false, true );
92 includeBounds->setFlags( includeBounds->flags() | Qgis::ProcessingParameterFlag::Advanced );
93 addParameter( includeBounds.release() );
94
95 auto outputLines = std::make_unique<QgsProcessingParameterFeatureSink>( u"OUTPUT_LINES"_s, QObject::tr( "Service area (lines)" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true );
96 outputLines->setCreateByDefault( true );
97 addParameter( outputLines.release() );
98
99 auto outputPoints = std::make_unique<QgsProcessingParameterFeatureSink>( u"OUTPUT"_s, QObject::tr( "Service area (boundary nodes)" ), Qgis::ProcessingSourceType::VectorPoint, QVariant(), true );
100 outputPoints->setCreateByDefault( false );
101 addParameter( outputPoints.release() );
102}
103
104QVariantMap QgsServiceAreaFromPointAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
105{
106 QGS_MARK_ALGORITHM_SOURCE
107
108 loadCommonParams( parameters, context, feedback );
109
110 const QgsPointXY startPoint = parameterAsPoint( parameters, u"START_POINT"_s, context, mNetwork->sourceCrs() );
111
112 // use older deprecated travel cost style if specified, to maintain old api
113 const bool useOldTravelCost = parameters.value( u"TRAVEL_COST"_s ).isValid();
114 double travelCost = parameterAsDouble( parameters, useOldTravelCost ? u"TRAVEL_COST"_s : u"TRAVEL_COST2"_s, context );
115
116 const int strategy = parameterAsInt( parameters, u"STRATEGY"_s, context );
117 if ( strategy && !useOldTravelCost )
118 travelCost *= mMultiplier;
119
120 bool includeBounds = true; // default to true to maintain 3.0 API
121 if ( parameters.contains( u"INCLUDE_BOUNDS"_s ) )
122 {
123 includeBounds = parameterAsBool( parameters, u"INCLUDE_BOUNDS"_s, context );
124 }
125
126 feedback->pushInfo( QObject::tr( "Building graph…" ) );
127 QVector<QgsPointXY> snappedPoints;
128 mDirector->makeGraph( mBuilder.get(), { startPoint }, snappedPoints, feedback );
129 const QgsPointXY snappedStartPoint = snappedPoints[0];
130
131 // check distance for the snapped point
132 if ( parameters.value( u"POINT_TOLERANCE"_s ).isValid() )
133 {
134 const double pointDistanceThreshold = parameterAsDouble( parameters, u"POINT_TOLERANCE"_s, context );
135
136 double distancePointToNetwork = 0;
137 try
138 {
139 distancePointToNetwork = mBuilder->distanceArea()->measureLine( startPoint, snappedStartPoint );
140 }
141 catch ( QgsCsException & )
142 {
143 throw QgsProcessingException( QObject::tr( "An error occurred while calculating length" ) );
144 }
145
146
147 if ( distancePointToNetwork > pointDistanceThreshold )
148 {
149 throw QgsProcessingException( QObject::tr( "Point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distancePointToNetwork ).arg( pointDistanceThreshold ) );
150 }
151 }
152
153 feedback->pushInfo( QObject::tr( "Calculating service area…" ) );
154 std::unique_ptr<QgsGraph> graph( mBuilder->takeGraph() );
155 const int idxStart = graph->findVertex( snappedStartPoint );
156
157 QVector<int> tree;
158 QVector<double> costs;
159 QgsGraphAnalyzer::dijkstra( graph.get(), idxStart, 0, &tree, &costs );
160
161 QgsMultiPointXY points;
162 QgsMultiPolylineXY lines;
163 QSet<int> vertices;
164
165 int inboundEdgeIndex;
166 double startVertexCost, endVertexCost;
167 QgsPointXY edgeStart, edgeEnd;
168 QgsGraphEdge edge;
169
170 for ( int i = 0; i < costs.size(); i++ )
171 {
172 inboundEdgeIndex = tree.at( i );
173 if ( inboundEdgeIndex == -1 && i != idxStart )
174 {
175 // unreachable vertex
176 continue;
177 }
178
179 startVertexCost = costs.at( i );
180 if ( startVertexCost > travelCost )
181 {
182 // vertex is too expensive, discard
183 continue;
184 }
185
186 vertices.insert( i );
187 edgeStart = graph->vertex( i ).point();
188
189 // find all edges coming from this vertex
190 const QList<int> outgoingEdges = graph->vertex( i ).outgoingEdges();
191 for ( const int edgeId : outgoingEdges )
192 {
193 edge = graph->edge( edgeId );
194 endVertexCost = startVertexCost + edge.cost( 0 ).toDouble();
195 edgeEnd = graph->vertex( edge.toVertex() ).point();
196 if ( endVertexCost <= travelCost )
197 {
198 // end vertex is cheap enough to include
199 vertices.insert( edge.toVertex() );
200 lines.push_back( QgsPolylineXY() << edgeStart << edgeEnd );
201 }
202 else
203 {
204 // travelCost sits somewhere on this edge, interpolate position
205 const QgsPointXY interpolatedEndPoint = QgsGeometryUtils::interpolatePointOnLineByValue( edgeStart.x(), edgeStart.y(), startVertexCost, edgeEnd.x(), edgeEnd.y(), endVertexCost, travelCost );
206
207 points.push_back( interpolatedEndPoint );
208 lines.push_back( QgsPolylineXY() << edgeStart << interpolatedEndPoint );
209 }
210 } // edges
211 } // costs
212
213 // convert to list and sort to maintain same order of points between algorithm runs
214 QList<int> verticesList = qgis::setToList( vertices );
215 points.reserve( verticesList.size() );
216 std::sort( verticesList.begin(), verticesList.end() );
217 for ( const int v : verticesList )
218 {
219 points.push_back( graph->vertex( v ).point() );
220 }
221
222 feedback->pushInfo( QObject::tr( "Writing results…" ) );
223
224 QVariantMap outputs;
225
226 QgsFields fields;
227 fields.append( QgsField( u"type"_s, QMetaType::Type::QString ) );
228 fields.append( QgsField( u"start"_s, QMetaType::Type::QString ) );
229
230 QgsFeature feat;
231 feat.setFields( fields );
232
233 QString pointsSinkId;
234 std::unique_ptr<QgsFeatureSink> pointsSink( parameterAsSink( parameters, u"OUTPUT"_s, context, pointsSinkId, fields, Qgis::WkbType::MultiPoint, mNetwork->sourceCrs() ) );
235
236 if ( pointsSink )
237 {
238 outputs.insert( u"OUTPUT"_s, pointsSinkId );
239
240 const QgsGeometry geomPoints = QgsGeometry::fromMultiPointXY( points );
241 feat.setGeometry( geomPoints );
242 feat.setAttributes( QgsAttributes() << u"within"_s << startPoint.toString() );
243 if ( !pointsSink->addFeature( feat, QgsFeatureSink::FastInsert ) )
244 throw QgsProcessingException( writeFeatureError( pointsSink.get(), parameters, u"OUTPUT"_s ) );
245 else
246 feedback->featureAddedToSink( u"OUTPUT"_s );
247
248 if ( includeBounds )
249 {
250 QgsMultiPointXY upperBoundary, lowerBoundary;
251 QVector<int> nodes;
252
253 int vertexId;
254 for ( int i = 0; i < costs.size(); i++ )
255 {
256 if ( costs.at( i ) > travelCost && tree.at( i ) != -1 )
257 {
258 vertexId = graph->edge( tree.at( i ) ).fromVertex();
259 if ( costs.at( vertexId ) <= travelCost )
260 {
261 nodes.push_back( i );
262 }
263 }
264 } // costs
265
266 upperBoundary.reserve( nodes.size() );
267 lowerBoundary.reserve( nodes.size() );
268 for ( const int i : nodes )
269 {
270 upperBoundary.push_back( graph->vertex( graph->edge( tree.at( i ) ).toVertex() ).point() );
271 lowerBoundary.push_back( graph->vertex( graph->edge( tree.at( i ) ).fromVertex() ).point() );
272 } // nodes
273
274 const QgsGeometry geomUpper = QgsGeometry::fromMultiPointXY( upperBoundary );
275 const QgsGeometry geomLower = QgsGeometry::fromMultiPointXY( lowerBoundary );
276
277 feat.setGeometry( geomUpper );
278 feat.setAttributes( QgsAttributes() << u"upper"_s << startPoint.toString() );
279 if ( !pointsSink->addFeature( feat, QgsFeatureSink::FastInsert ) )
280 throw QgsProcessingException( writeFeatureError( pointsSink.get(), parameters, u"OUTPUT"_s ) );
281 else
282 feedback->featureAddedToSink( u"OUTPUT"_s );
283
284 feat.setGeometry( geomLower );
285 feat.setAttributes( QgsAttributes() << u"lower"_s << startPoint.toString() );
286 if ( !pointsSink->addFeature( feat, QgsFeatureSink::FastInsert ) )
287 throw QgsProcessingException( writeFeatureError( pointsSink.get(), parameters, u"OUTPUT"_s ) );
288 else
289 feedback->featureAddedToSink( u"OUTPUT"_s );
290 } // includeBounds
291
292 pointsSink->finalize();
293 feedback->featureSinkFinalized( u"OUTPUT"_s );
294 }
295
296 QString linesSinkId;
297 std::unique_ptr<QgsFeatureSink> linesSink( parameterAsSink( parameters, u"OUTPUT_LINES"_s, context, linesSinkId, fields, Qgis::WkbType::MultiLineString, mNetwork->sourceCrs() ) );
298
299 if ( linesSink )
300 {
301 outputs.insert( u"OUTPUT_LINES"_s, linesSinkId );
302 const QgsGeometry geomLines = QgsGeometry::fromMultiPolylineXY( lines );
303 feat.setGeometry( geomLines );
304 feat.setAttributes( QgsAttributes() << u"lines"_s << startPoint.toString() );
305 if ( !linesSink->addFeature( feat, QgsFeatureSink::FastInsert ) )
306 throw QgsProcessingException( writeFeatureError( linesSink.get(), parameters, u"OUTPUT_LINES"_s ) );
307 else
308 feedback->featureAddedToSink( u"OUTPUT_LINES"_s );
309 linesSink->finalize();
310 feedback->featureSinkFinalized( u"OUTPUT_LINES"_s );
311 }
312
313 return outputs;
314}
315
@ VectorPoint
Vector point layers.
Definition qgis.h:3752
@ VectorLine
Vector line layers.
Definition qgis.h:3753
@ MultiPoint
MultiPoint.
Definition qgis.h:300
@ MultiLineString
MultiLineString.
Definition qgis.h:301
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3985
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3984
@ Double
Double/float values.
Definition qgis.h:4025
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 setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
Container of fields for a vector layer.
Definition qgsfields.h:45
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
static QgsPointXY interpolatePointOnLineByValue(double x1, double y1, double v1, double x2, double y2, double v2, double value)
Interpolates the position of a point along the line from (x1, y1) to (x2, y2).
A geometry is the spatial representation of a feature.
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY 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 an edge in a graph.
Definition qgsgraph.h:44
int toVertex() const
Returns the index of the vertex at the end of this edge.
Definition qgsgraph.cpp:185
QVariant cost(int strategyIndex) const
Returns edge cost calculated using specified strategy.
Definition qgsgraph.cpp:170
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.
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
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.
void featureAddedToSink(const QString &output)
Reports that a feature was added to the the sink associated with the specified algorithm output.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
A numeric parameter for processing algorithms.
A point parameter for processing algorithms.
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:98
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:63