QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsvectorlayerdirector.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslinevectorlayerdirector.cpp
3 --------------------------------------
4 Date : 2010-10-20
5 Copyright : (C) 2010 by Yakushev Sergey
7****************************************************************************
8* *
9* This program is free software; you can redistribute it and/or modify *
10* it under the terms of the GNU General Public License as published by *
11* the Free Software Foundation; either version 2 of the License, or *
12* (at your option) any later version. *
13* *
14***************************************************************************/
15
20
22
23#include <spatialindex/SpatialIndex.h>
24
25#include "qgsdistancearea.h"
26#include "qgsfeatureiterator.h"
27#include "qgsfeaturesource.h"
28#include "qgsgeometry.h"
30#include "qgslogger.h"
32#include "qgswkbtypes.h"
33
34#include <QString>
35#include <QtAlgorithms>
36
37#include "moc_qgsvectorlayerdirector.cpp"
38
39using namespace SpatialIndex;
40
42{
43 TiePointInfo() = default;
44 TiePointInfo( int additionalPointId, QgsFeatureId featureId, const QgsPointXY &start, const QgsPointXY &end )
46 , mNetworkFeatureId( featureId )
47 , mFirstPoint( start )
48 , mLastPoint( end )
49 {}
50
53 double mLength = std::numeric_limits<double>::max();
57};
58
59QgsVectorLayerDirector::QgsVectorLayerDirector( QgsFeatureSource *source, int directionFieldId, const QString &directDirectionValue, const QString &reverseDirectionValue, const QString &bothDirectionValue, const Direction defaultDirection )
60 : mSource( source )
61 , mDirectionFieldId( directionFieldId )
62 , mDirectDirectionValue( directDirectionValue )
63 , mReverseDirectionValue( reverseDirectionValue )
64 , mBothDirectionValue( bothDirectionValue )
65 , mDefaultDirection( defaultDirection )
66{
67}
68
70{
71 return QStringLiteral( "Vector line" );
72}
73
74QgsAttributeList QgsVectorLayerDirector::requiredAttributes() const
75{
76 QSet<int> attrs;
77
78 if ( mDirectionFieldId != -1 )
79 attrs.insert( mDirectionFieldId );
80
81 for ( const QgsNetworkStrategy *strategy : mStrategies )
82 {
83 attrs.unite( strategy->requiredAttributes() );
84 }
85 return qgis::setToList( attrs );
86}
87
88QgsVectorLayerDirector::Direction QgsVectorLayerDirector::directionForFeature( const QgsFeature &feature ) const
89{
90 if ( mDirectionFieldId < 0 )
91 return mDefaultDirection;
92
93 QString str = feature.attribute( mDirectionFieldId ).toString();
94 if ( str == mBothDirectionValue )
95 {
97 }
98 else if ( str == mDirectDirectionValue )
99 {
101 }
102 else if ( str == mReverseDirectionValue )
103 {
105 }
106 else
107 {
108 return mDefaultDirection;
109 }
110}
111
113class QgsNetworkVisitor : public SpatialIndex::IVisitor
114{
115 public:
116 explicit QgsNetworkVisitor( QVector<int> &pointIndexes )
117 : mPoints( pointIndexes ) {}
118
119 void visitNode( const INode &n ) override
120 {
121 Q_UNUSED( n )
122 }
123
124 void visitData( const IData &d ) override
125 {
126 mPoints.append( d.getIdentifier() );
127 }
128
129 void visitData( std::vector<const IData *> &v ) override
130 {
131 Q_UNUSED( v )
132 }
133
134 private:
135 QVector<int> &mPoints;
136};
137
139
140std::unique_ptr<SpatialIndex::ISpatialIndex> createVertexSpatialIndex( SpatialIndex::IStorageManager &storageManager )
141{
142 // R-Tree parameters
143 double fillFactor = 0.7;
144 unsigned long indexCapacity = 10;
145 unsigned long leafCapacity = 10;
146 unsigned long dimension = 2;
147 RTree::RTreeVariant variant = RTree::RV_RSTAR;
148
149 // create R-tree
150 SpatialIndex::id_type indexId;
151 std::unique_ptr<SpatialIndex::ISpatialIndex> iRTree( RTree::createNewRTree( storageManager, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId ) );
152 return iRTree;
153}
154
155int findClosestVertex( const QgsPointXY &point, SpatialIndex::ISpatialIndex *index, double tolerance )
156{
157 QVector<int> matching;
158 QgsNetworkVisitor visitor( matching );
159
160 double pt1[2] = { point.x() - tolerance, point.y() - tolerance },
161 pt2[2] = { point.x() + tolerance, point.y() + tolerance };
162 SpatialIndex::Region searchRegion( pt1, pt2, 2 );
163
164 index->intersectsWithQuery( searchRegion, visitor );
165
166 return matching.empty() ? -1 : matching.at( 0 );
167}
168
169void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const QVector<QgsPointXY> &additionalPoints, QVector<QgsPointXY> &snappedPoints, QgsFeedback *feedback ) const
170{
171 long featureCount = mSource->featureCount() * 2;
172 int step = 0;
173
175 ct.setSourceCrs( mSource->sourceCrs() );
176 if ( builder->coordinateTransformationEnabled() )
177 {
178 ct.setDestinationCrs( builder->destinationCrs() );
179 }
180
181 // clear existing snapped points list, and resize to length of provided additional points
182 snappedPoints = QVector<QgsPointXY>( additionalPoints.size(), QgsPointXY( 0.0, 0.0 ) );
183 // tie points = snapped location of specified additional points to network lines
184 QVector<TiePointInfo> additionalTiePoints( additionalPoints.size() );
185
186 // graph's vertices = all vertices in graph, with vertices within builder's tolerance collapsed together
187 QVector<QgsPointXY> graphVertices;
188
189 // spatial index for graph vertices
190 std::unique_ptr<SpatialIndex::IStorageManager> iStorage( StorageManager::createNewMemoryStorageManager() );
191 std::unique_ptr<SpatialIndex::ISpatialIndex> iRTree = createVertexSpatialIndex( *iStorage );
192
193 double tolerance = std::max( builder->topologyTolerance(), 1e-10 );
194 auto findPointWithinTolerance = [&iRTree, tolerance]( const QgsPointXY &point ) -> int {
195 return findClosestVertex( point, iRTree.get(), tolerance );
196 };
197 auto addPointToIndex = [&iRTree]( const QgsPointXY &point, int index ) {
198 double coords[] = { point.x(), point.y() };
199 iRTree->insertData( 0, nullptr, SpatialIndex::Point( coords, 2 ), index );
200 };
201
202 // first iteration - get all nodes from network, and snap additional points to network
203 QgsFeatureIterator fit = mSource->getFeatures( QgsFeatureRequest().setNoAttributes() );
204 QgsFeature feature;
205 while ( fit.nextFeature( feature ) )
206 {
207 if ( feedback && feedback->isCanceled() )
208 return;
209
212 mpl = feature.geometry().asMultiPolyline();
214 mpl.push_back( feature.geometry().asPolyline() );
215
216 for ( const QgsPolylineXY &line : std::as_const( mpl ) )
217 {
218 QgsPointXY pt1, pt2;
219 bool isFirstPoint = true;
220 for ( const QgsPointXY &point : line )
221 {
222 pt2 = ct.transform( point );
223
224 int pt2Idx = findPointWithinTolerance( pt2 );
225 if ( pt2Idx == -1 )
226 {
227 // no vertex already exists within tolerance - add to points, and index
228 addPointToIndex( pt2, graphVertices.count() );
229 graphVertices.push_back( pt2 );
230 }
231 else
232 {
233 // vertex already exists within tolerance - use that
234 pt2 = graphVertices.at( pt2Idx );
235 }
236
237 if ( !isFirstPoint )
238 {
239 // check if this line segment is a candidate for being closest to each additional point
240 int i = 0;
241 for ( const QgsPointXY &additionalPoint : additionalPoints )
242 {
243 QgsPointXY snappedPoint;
244 double thisSegmentClosestDist = std::numeric_limits<double>::max();
245 if ( pt1 == pt2 )
246 {
247 thisSegmentClosestDist = additionalPoint.sqrDist( pt1 );
248 snappedPoint = pt1;
249 }
250 else
251 {
252 thisSegmentClosestDist = additionalPoint.sqrDistToSegment( pt1.x(), pt1.y(), pt2.x(), pt2.y(), snappedPoint, 0 );
253 }
254
255 if ( thisSegmentClosestDist < additionalTiePoints[i].mLength )
256 {
257 // found a closer segment for this additional point
258 TiePointInfo info( i, feature.id(), pt1, pt2 );
259 info.mLength = thisSegmentClosestDist;
260 info.mTiedPoint = snappedPoint;
261
262 additionalTiePoints[i] = info;
263 snappedPoints[i] = info.mTiedPoint;
264 }
265 i++;
266 }
267 }
268 pt1 = pt2;
269 isFirstPoint = false;
270 }
271 }
272 if ( feedback )
273 feedback->setProgress( 100.0 * static_cast<double>( ++step ) / featureCount );
274 }
275
276 // build a hash of feature ids to tie points which depend on this feature
277 QHash<QgsFeatureId, QList<int>> tiePointNetworkFeatures;
278 int i = 0;
279 for ( TiePointInfo &info : additionalTiePoints )
280 {
281 tiePointNetworkFeatures[info.mNetworkFeatureId] << i;
282 i++;
283 }
284
285 // add tied point to graph
286 for ( int i = 0; i < snappedPoints.size(); ++i )
287 {
288 // check index to see if vertex exists within tolerance of tie point
289 const QgsPointXY point = snappedPoints.at( i );
290 int ptIdx = findPointWithinTolerance( point );
291 if ( ptIdx == -1 )
292 {
293 // no vertex already within tolerance, add to index and network vertices
294 addPointToIndex( point, graphVertices.count() );
295 graphVertices.push_back( point );
296 }
297 else
298 {
299 // otherwise snap tie point to vertex
300 snappedPoints[i] = graphVertices.at( ptIdx );
301 }
302 }
303 // also need to update tie points - they need to be matched for snapped points
304 for ( int i = 0; i < additionalTiePoints.count(); ++i )
305 {
306 additionalTiePoints[i].mTiedPoint = snappedPoints.at( additionalTiePoints.at( i ).additionalPointId );
307 }
308
309
310 // begin graph construction
311
312 // add vertices to graph
313 {
314 int i = 0;
315 for ( const QgsPointXY &point : graphVertices )
316 {
317 builder->addVertex( i, point );
318 i++;
319 }
320 }
321
322 fit = mSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( requiredAttributes() ) );
323 while ( fit.nextFeature( feature ) )
324 {
325 if ( feedback && feedback->isCanceled() )
326 return;
327
328 Direction direction = directionForFeature( feature );
329
330 // begin features segments and add arc to the Graph;
333 mpl = feature.geometry().asMultiPolyline();
335 mpl.push_back( feature.geometry().asPolyline() );
336
337 for ( const QgsPolylineXY &line : std::as_const( mpl ) )
338 {
339 QgsPointXY pt1, pt2;
340
341 bool isFirstPoint = true;
342 for ( const QgsPointXY &point : line )
343 {
344 pt2 = ct.transform( point );
345 int pPt2idx = findPointWithinTolerance( pt2 );
346 Q_ASSERT_X( pPt2idx >= 0, "QgsVectorLayerDirectory::makeGraph", "encountered a vertex which was not present in graph" );
347 pt2 = graphVertices.at( pPt2idx );
348
349 if ( !isFirstPoint )
350 {
351 QMap<double, QgsPointXY> pointsOnArc;
352 pointsOnArc[0.0] = pt1;
353 pointsOnArc[pt1.sqrDist( pt2 )] = pt2;
354
355 const QList<int> tiePointsForCurrentFeature = tiePointNetworkFeatures.value( feature.id() );
356 for ( int tiePointIdx : tiePointsForCurrentFeature )
357 {
358 const TiePointInfo &t = additionalTiePoints.at( tiePointIdx );
359 if ( t.mFirstPoint == pt1 && t.mLastPoint == pt2 )
360 {
361 pointsOnArc[pt1.sqrDist( t.mTiedPoint )] = t.mTiedPoint;
362 }
363 }
364
365 QgsPointXY arcPt1;
366 QgsPointXY arcPt2;
367 int pt1idx = -1;
368 int pt2idx = -1;
369 bool isFirstPoint = true;
370 for ( auto arcPointIt = pointsOnArc.constBegin(); arcPointIt != pointsOnArc.constEnd(); ++arcPointIt )
371 {
372 arcPt2 = arcPointIt.value();
373
374 pt2idx = findPointWithinTolerance( arcPt2 );
375 Q_ASSERT_X( pt2idx >= 0, "QgsVectorLayerDirectory::makeGraph", "encountered a vertex which was not present in graph" );
376 arcPt2 = graphVertices.at( pt2idx );
377
378 if ( !isFirstPoint && arcPt1 != arcPt2 )
379 {
380 double distance = 0;
381 try
382 {
383 distance = builder->distanceArea()->measureLine( arcPt1, arcPt2 );
384 }
385 catch ( QgsCsException & )
386 {
387 // TODO report errors to user
388 QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
389 }
390
391 QVector<QVariant> prop;
392 prop.reserve( mStrategies.size() );
393 for ( QgsNetworkStrategy *strategy : mStrategies )
394 {
395 prop.push_back( strategy->cost( distance, feature ) );
396 }
397
398 if ( direction == Direction::DirectionForward || direction == Direction::DirectionBoth )
399 {
400 builder->addEdge( pt1idx, arcPt1, pt2idx, arcPt2, prop );
401 }
402 if ( direction == Direction::DirectionBackward || direction == Direction::DirectionBoth )
403 {
404 builder->addEdge( pt2idx, arcPt2, pt1idx, arcPt1, prop );
405 }
406 }
407 pt1idx = pt2idx;
408 arcPt1 = arcPt2;
409 isFirstPoint = false;
410 }
411 }
412 pt1 = pt2;
413 isFirstPoint = false;
414 }
415 }
416 if ( feedback )
417 {
418 feedback->setProgress( 100.0 * static_cast<double>( ++step ) / featureCount );
419 }
420 }
421}
@ LineString
LineString.
Definition qgis.h:280
@ MultiLineString
MultiLineString.
Definition qgis.h:284
Handles coordinate transforms between two coordinate systems.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
double measureLine(const QVector< QgsPointXY > &points) const
Measures the length of a line with multiple segments.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
An interface for objects which provide features via a getFeatures method.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
Interface for creating a graph.
virtual void addVertex(int id, const QgsPointXY &pt)
Add vertex to the graph.
QgsCoordinateReferenceSystem destinationCrs() const
Returns destinaltion CRS.
QgsDistanceArea * distanceArea()
Returns measurement tool.
virtual void addEdge(int pt1id, const QgsPointXY &pt1, int pt2id, const QgsPointXY &pt2, const QVector< QVariant > &strategies)
Add edge to the graph.
double topologyTolerance() const
Returns topology tolerance.
bool coordinateTransformationEnabled() const
Returns coordinate transformation enabled.
QList< QgsNetworkStrategy * > mStrategies
Defines strategy used for calculation of the edge cost in networks.
Represents a 2D point.
Definition qgspointxy.h:60
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition qgspointxy.h:186
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
QString name() const override
Returns director name.
void makeGraph(QgsGraphBuilderInterface *builder, const QVector< QgsPointXY > &additionalPoints, QVector< QgsPointXY > &snappedPoints, QgsFeedback *feedback=nullptr) const override
Make a graph using QgsGraphBuilder.
Direction
Edge direction Edge can be one-way with direct flow (one can move only from the start point to the en...
@ DirectionBackward
One-way reversed.
QgsVectorLayerDirector(QgsFeatureSource *source, int directionFieldId, const QString &directDirectionValue, const QString &reverseDirectionValue, const QString &bothDirectionValue, Direction defaultDirection)
Default constructor.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:28
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
std::unique_ptr< SpatialIndex::ISpatialIndex > createVertexSpatialIndex(SpatialIndex::IStorageManager &storageManager)
int findClosestVertex(const QgsPointXY &point, SpatialIndex::ISpatialIndex *index, double tolerance)
TiePointInfo(int additionalPointId, QgsFeatureId featureId, const QgsPointXY &start, const QgsPointXY &end)
TiePointInfo()=default
QgsFeatureId mNetworkFeatureId