QGIS API Documentation  3.12.1-București (121cc00ff0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsalgorithmserviceareafromlayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmserviceareafromlayer.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 
24 
25 QString QgsServiceAreaFromLayerAlgorithm::name() const
26 {
27  return QStringLiteral( "serviceareafromlayer" );
28 }
29 
30 QString QgsServiceAreaFromLayerAlgorithm::displayName() const
31 {
32  return QObject::tr( "Service area (from layer)" );
33 }
34 
35 QStringList QgsServiceAreaFromLayerAlgorithm::tags() const
36 {
37  return QObject::tr( "network,service,area,shortest,fastest" ).split( ',' );
38 }
39 
40 QString QgsServiceAreaFromLayerAlgorithm::shortHelpString() const
41 {
42  return QObject::tr( "This algorithm creates a new vector with all the edges or parts of edges of a network line layer that can be reached within a distance or a time, starting from features of a point layer. The distance and the time (both referred to as \"travel cost\") must be specified respectively in the network layer units or in seconds." );
43 }
44 
45 QgsServiceAreaFromLayerAlgorithm *QgsServiceAreaFromLayerAlgorithm::createInstance() const
46 {
47  return new QgsServiceAreaFromLayerAlgorithm();
48 }
49 
50 void QgsServiceAreaFromLayerAlgorithm::initAlgorithm( const QVariantMap & )
51 {
52  addCommonParams();
53  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "START_POINTS" ), QObject::tr( "Vector layer with start points" ), QList< int >() << QgsProcessing::TypeVectorPoint ) );
54  addParameter( new QgsProcessingParameterNumber( QStringLiteral( "TRAVEL_COST" ), QObject::tr( "Travel cost (distance for 'Shortest', time for 'Fastest')" ),
55  QgsProcessingParameterNumber::Double, 0, false, 0 ) );
56 
57  std::unique_ptr< QgsProcessingParameterBoolean > includeBounds = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "INCLUDE_BOUNDS" ), QObject::tr( "Include upper/lower bound points" ), false, true );
58  includeBounds->setFlags( includeBounds->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
59  addParameter( includeBounds.release() );
60 
61  std::unique_ptr< QgsProcessingParameterFeatureSink > outputLines = qgis::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral( "OUTPUT_LINES" ), QObject::tr( "Service area (lines)" ),
62  QgsProcessing::TypeVectorLine, QVariant(), true );
63  outputLines->setCreateByDefault( true );
64  addParameter( outputLines.release() );
65 
66  std::unique_ptr< QgsProcessingParameterFeatureSink > outputPoints = qgis::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral( "OUTPUT" ), QObject::tr( "Service area (boundary nodes)" ),
67  QgsProcessing::TypeVectorPoint, QVariant(), true );
68  outputPoints->setCreateByDefault( false );
69  addParameter( outputPoints.release() );
70 }
71 
72 QVariantMap QgsServiceAreaFromLayerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
73 {
74  loadCommonParams( parameters, context, feedback );
75 
76  std::unique_ptr< QgsFeatureSource > startPoints( parameterAsSource( parameters, QStringLiteral( "START_POINTS" ), context ) );
77  if ( !startPoints )
78  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "START_POINTS" ) ) );
79 
80  double travelCost = parameterAsDouble( parameters, QStringLiteral( "TRAVEL_COST" ), context );
81 
82  bool includeBounds = true; // default to true to maintain 3.0 API
83  if ( parameters.contains( QStringLiteral( "INCLUDE_BOUNDS" ) ) )
84  {
85  includeBounds = parameterAsBool( parameters, QStringLiteral( "INCLUDE_BOUNDS" ), context );
86  }
87 
88  QVector< QgsPointXY > points;
89  QHash< int, QgsAttributes > sourceAttributes;
90  loadPoints( startPoints.get(), points, sourceAttributes, context, feedback );
91 
92  feedback->pushInfo( QObject::tr( "Building graph…" ) );
93  QVector< QgsPointXY > snappedPoints;
94  mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback );
95 
96  feedback->pushInfo( QObject::tr( "Calculating service areas…" ) );
97  QgsGraph *graph = mBuilder->graph();
98 
99  QgsFields fields = startPoints->fields();
100  fields.append( QgsField( QStringLiteral( "type" ), QVariant::String ) );
101  fields.append( QgsField( QStringLiteral( "start" ), QVariant::String ) );
102 
103  QString pointsSinkId;
104  std::unique_ptr< QgsFeatureSink > pointsSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, pointsSinkId, fields,
105  QgsWkbTypes::MultiPoint, mNetwork->sourceCrs() ) );
106 
107  QString linesSinkId;
108  std::unique_ptr< QgsFeatureSink > linesSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_LINES" ), context, linesSinkId, fields,
109  QgsWkbTypes::MultiLineString, mNetwork->sourceCrs() ) );
110 
111  int idxStart;
112  QString origPoint;
113  QVector< int > tree;
114  QVector< double > costs;
115 
116  int inboundEdgeIndex;
117  double startVertexCost, endVertexCost;
118  QgsPointXY startPoint, endPoint;
119  QgsGraphEdge edge;
120 
121  QgsFeature feat;
122  QgsAttributes attributes;
123 
124  int step = snappedPoints.size() > 0 ? 100.0 / snappedPoints.size() : 1;
125  for ( int i = 0; i < snappedPoints.size(); i++ )
126  {
127  if ( feedback->isCanceled() )
128  {
129  break;
130  }
131 
132  idxStart = graph->findVertex( snappedPoints.at( i ) );
133  origPoint = points.at( i ).toString();
134 
135  QgsGraphAnalyzer::dijkstra( graph, idxStart, 0, &tree, &costs );
136 
137  QgsMultiPointXY areaPoints;
138  QgsMultiPolylineXY lines;
139  QSet< int > vertices;
140 
141  for ( int j = 0; j < costs.size(); j++ )
142  {
143  inboundEdgeIndex = tree.at( j );
144 
145  if ( inboundEdgeIndex == -1 && j != idxStart )
146  {
147  // unreachable vertex
148  continue;
149  }
150 
151  startVertexCost = costs.at( j );
152  if ( startVertexCost > travelCost )
153  {
154  // vertex is too expensive, discard
155  continue;
156  }
157 
158  vertices.insert( j );
159  startPoint = graph->vertex( j ).point();
160 
161  // find all edges coming from this vertex
162  const QList< int > outgoingEdges = graph->vertex( j ).outgoingEdges() ;
163  for ( int edgeId : outgoingEdges )
164  {
165  edge = graph->edge( edgeId );
166  endVertexCost = startVertexCost + edge.cost( 0 ).toDouble();
167  endPoint = graph->vertex( edge.toVertex() ).point();
168  if ( endVertexCost <= travelCost )
169  {
170  // end vertex is cheap enough to include
171  vertices.insert( edge.toVertex() );
172  lines.push_back( QgsPolylineXY() << startPoint << endPoint );
173  }
174  else
175  {
176  // travelCost sits somewhere on this edge, interpolate position
177  QgsPointXY interpolatedEndPoint = QgsGeometryUtils::interpolatePointOnLineByValue( startPoint.x(), startPoint.y(), startVertexCost,
178  endPoint.x(), endPoint.y(), endVertexCost, travelCost );
179 
180  areaPoints.push_back( interpolatedEndPoint );
181  lines.push_back( QgsPolylineXY() << startPoint << interpolatedEndPoint );
182  }
183  } // edges
184  } // costs
185 
186  // convert to list and sort to maintain same order of points between algorithm runs
187  QList< int > verticesList = vertices.toList();
188  areaPoints.reserve( verticesList.size() );
189  std::sort( verticesList.begin(), verticesList.end() );
190  for ( int v : verticesList )
191  {
192  areaPoints.push_back( graph->vertex( v ).point() );
193  }
194 
195  if ( pointsSink )
196  {
197  QgsGeometry geomPoints = QgsGeometry::fromMultiPointXY( areaPoints );
198  feat.setGeometry( geomPoints );
199  attributes = sourceAttributes.value( i + 1 );
200  attributes << QStringLiteral( "within" ) << origPoint;
201  feat.setAttributes( attributes );
202  pointsSink->addFeature( feat, QgsFeatureSink::FastInsert );
203 
204  if ( includeBounds )
205  {
206  QgsMultiPointXY upperBoundary, lowerBoundary;
207  QVector< int > nodes;
208  nodes.reserve( costs.size() );
209 
210  int vertexId;
211  for ( int v = 0; v < costs.size(); v++ )
212  {
213  if ( costs.at( v ) > travelCost && tree.at( v ) != -1 )
214  {
215  vertexId = graph->edge( tree.at( v ) ).fromVertex();
216  if ( costs.at( vertexId ) <= travelCost )
217  {
218  nodes.push_back( v );
219  }
220  }
221  } // costs
222 
223  for ( int n : qgis::as_const( nodes ) )
224  {
225  upperBoundary.push_back( graph->vertex( graph->edge( tree.at( n ) ).toVertex() ).point() );
226  lowerBoundary.push_back( graph->vertex( graph->edge( tree.at( n ) ).fromVertex() ).point() );
227  } // nodes
228 
229  QgsGeometry geomUpper = QgsGeometry::fromMultiPointXY( upperBoundary );
230  QgsGeometry geomLower = QgsGeometry::fromMultiPointXY( lowerBoundary );
231 
232  feat.setGeometry( geomUpper );
233  attributes = sourceAttributes.value( i );
234  attributes << QStringLiteral( "upper" ) << origPoint;
235  feat.setAttributes( attributes );
236  pointsSink->addFeature( feat, QgsFeatureSink::FastInsert );
237 
238  feat.setGeometry( geomLower );
239  attributes = sourceAttributes.value( i );
240  attributes << QStringLiteral( "lower" ) << origPoint;
241  feat.setAttributes( attributes );
242  pointsSink->addFeature( feat, QgsFeatureSink::FastInsert );
243  } // includeBounds
244  }
245 
246  if ( linesSink )
247  {
248  QgsGeometry geomLines = QgsGeometry::fromMultiPolylineXY( lines );
249  feat.setGeometry( geomLines );
250  attributes = sourceAttributes.value( i );
251  attributes << QStringLiteral( "lines" ) << origPoint;
252  feat.setAttributes( attributes );
253  linesSink->addFeature( feat, QgsFeatureSink::FastInsert );
254  }
255 
256  feedback->setProgress( i * step );
257  } // snappedPoints
258 
259  QVariantMap outputs;
260  if ( pointsSink )
261  {
262  outputs.insert( QStringLiteral( "OUTPUT" ), pointsSinkId );
263  }
264  if ( linesSink )
265  {
266  outputs.insert( QStringLiteral( "OUTPUT_LINES" ), linesSinkId );
267  }
268 
269  return outputs;
270 }
271 
const QgsGraphEdge & edge(int idx) const
Returns edge at given index.
Definition: qgsgraph.cpp:50
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Base class for providing feedback from a processing algorithm.
Parameter is an advanced parameter which should be hidden from users by default.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:64
Container of fields for a vector layer.
Definition: qgsfields.h:42
QgsPointXY point() const
Returns point associated with graph vertex.
Definition: qgsgraph.cpp:114
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:80
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:84
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
QgsGraphEdgeIds outgoingEdges() const
Returns outgoing edge ids, i.e.
Definition: qgsgraph.cpp:109
int findVertex(const QgsPointXY &pt) const
Find vertex by associated point.
Definition: qgsgraph.cpp:65
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:49
A numeric parameter for processing algorithms.
QVariant cost(int strategyIndex) const
Returns edge cost calculated using specified strategy.
Definition: qgsgraph.cpp:78
Mathematical graph representation.
Definition: qgsgraph.h:141
double x
Definition: qgspointxy.h:47
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:50
int toVertex() const
Returns the index of the vertex at the end of this edge.
Definition: qgsgraph.cpp:93
const QgsGraphVertex & vertex(int idx) const
Returns vertex at given index.
Definition: qgsgraph.cpp:45
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:55
Vector point layers.
Definition: qgsprocessing.h:48
An input feature source (such as vector layers) parameter for processing algorithms.
Vector line layers.
Definition: qgsprocessing.h:49
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
A vector of attributes.
Definition: qgsattributes.h:57
Contains information about the context in which a processing algorithm is executed.
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).
This class implements a graph edge.
Definition: qgsgraph.h:43
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.