QGIS API Documentation 3.41.0-Master (af5edcb665c)
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
22#include "moc_qgsvectorlayerdirector.cpp"
24
25#include "qgsfeatureiterator.h"
26#include "qgsfeaturesource.h"
28#include "qgsgeometry.h"
29#include "qgsdistancearea.h"
30#include "qgswkbtypes.h"
31#include "qgslogger.h"
32#include <QString>
33#include <QtAlgorithms>
34
35#include <spatialindex/SpatialIndex.h>
36
37using namespace SpatialIndex;
38
40{
41 TiePointInfo() = default;
42 TiePointInfo( int additionalPointId, QgsFeatureId featureId, const QgsPointXY &start, const QgsPointXY &end )
44 , mNetworkFeatureId( featureId )
45 , mFirstPoint( start )
46 , mLastPoint( end )
47 {}
48
51 double mLength = std::numeric_limits<double>::max();
55};
56
57QgsVectorLayerDirector::QgsVectorLayerDirector( QgsFeatureSource *source, int directionFieldId, const QString &directDirectionValue, const QString &reverseDirectionValue, const QString &bothDirectionValue, const Direction defaultDirection )
58 : mSource( source )
59 , mDirectionFieldId( directionFieldId )
60 , mDirectDirectionValue( directDirectionValue )
61 , mReverseDirectionValue( reverseDirectionValue )
62 , mBothDirectionValue( bothDirectionValue )
63 , mDefaultDirection( defaultDirection )
64{
65}
66
68{
69 return QStringLiteral( "Vector line" );
70}
71
72QgsAttributeList QgsVectorLayerDirector::requiredAttributes() const
73{
74 QSet<int> attrs;
75
76 if ( mDirectionFieldId != -1 )
77 attrs.insert( mDirectionFieldId );
78
79 for ( const QgsNetworkStrategy *strategy : mStrategies )
80 {
81 attrs.unite( strategy->requiredAttributes() );
82 }
83 return qgis::setToList( attrs );
84}
85
86QgsVectorLayerDirector::Direction QgsVectorLayerDirector::directionForFeature( const QgsFeature &feature ) const
87{
88 if ( mDirectionFieldId < 0 )
89 return mDefaultDirection;
90
91 QString str = feature.attribute( mDirectionFieldId ).toString();
92 if ( str == mBothDirectionValue )
93 {
95 }
96 else if ( str == mDirectDirectionValue )
97 {
99 }
100 else if ( str == mReverseDirectionValue )
101 {
103 }
104 else
105 {
106 return mDefaultDirection;
107 }
108}
109
111class QgsNetworkVisitor : public SpatialIndex::IVisitor
112{
113 public:
114 explicit QgsNetworkVisitor( QVector<int> &pointIndexes )
115 : mPoints( pointIndexes ) {}
116
117 void visitNode( const INode &n ) override
118 {
119 Q_UNUSED( n )
120 }
121
122 void visitData( const IData &d ) override
123 {
124 mPoints.append( d.getIdentifier() );
125 }
126
127 void visitData( std::vector<const IData *> &v ) override
128 {
129 Q_UNUSED( v )
130 }
131
132 private:
133 QVector<int> &mPoints;
134};
135
137
138std::unique_ptr<SpatialIndex::ISpatialIndex> createVertexSpatialIndex( SpatialIndex::IStorageManager &storageManager )
139{
140 // R-Tree parameters
141 double fillFactor = 0.7;
142 unsigned long indexCapacity = 10;
143 unsigned long leafCapacity = 10;
144 unsigned long dimension = 2;
145 RTree::RTreeVariant variant = RTree::RV_RSTAR;
146
147 // create R-tree
148 SpatialIndex::id_type indexId;
149 std::unique_ptr<SpatialIndex::ISpatialIndex> iRTree( RTree::createNewRTree( storageManager, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId ) );
150 return iRTree;
151}
152
153int findClosestVertex( const QgsPointXY &point, SpatialIndex::ISpatialIndex *index, double tolerance )
154{
155 QVector<int> matching;
156 QgsNetworkVisitor visitor( matching );
157
158 double pt1[2] = { point.x() - tolerance, point.y() - tolerance },
159 pt2[2] = { point.x() + tolerance, point.y() + tolerance };
160 SpatialIndex::Region searchRegion( pt1, pt2, 2 );
161
162 index->intersectsWithQuery( searchRegion, visitor );
163
164 return matching.empty() ? -1 : matching.at( 0 );
165}
166
167void QgsVectorLayerDirector::makeGraph( QgsGraphBuilderInterface *builder, const QVector<QgsPointXY> &additionalPoints, QVector<QgsPointXY> &snappedPoints, QgsFeedback *feedback ) const
168{
169 long featureCount = mSource->featureCount() * 2;
170 int step = 0;
171
173 ct.setSourceCrs( mSource->sourceCrs() );
174 if ( builder->coordinateTransformationEnabled() )
175 {
176 ct.setDestinationCrs( builder->destinationCrs() );
177 }
178
179 // clear existing snapped points list, and resize to length of provided additional points
180 snappedPoints = QVector<QgsPointXY>( additionalPoints.size(), QgsPointXY( 0.0, 0.0 ) );
181 // tie points = snapped location of specified additional points to network lines
182 QVector<TiePointInfo> additionalTiePoints( additionalPoints.size() );
183
184 // graph's vertices = all vertices in graph, with vertices within builder's tolerance collapsed together
185 QVector<QgsPointXY> graphVertices;
186
187 // spatial index for graph vertices
188 std::unique_ptr<SpatialIndex::IStorageManager> iStorage( StorageManager::createNewMemoryStorageManager() );
189 std::unique_ptr<SpatialIndex::ISpatialIndex> iRTree = createVertexSpatialIndex( *iStorage );
190
191 double tolerance = std::max( builder->topologyTolerance(), 1e-10 );
192 auto findPointWithinTolerance = [&iRTree, tolerance]( const QgsPointXY &point ) -> int {
193 return findClosestVertex( point, iRTree.get(), tolerance );
194 };
195 auto addPointToIndex = [&iRTree]( const QgsPointXY &point, int index ) {
196 double coords[] = { point.x(), point.y() };
197 iRTree->insertData( 0, nullptr, SpatialIndex::Point( coords, 2 ), index );
198 };
199
200 // first iteration - get all nodes from network, and snap additional points to network
202 QgsFeature feature;
203 while ( fit.nextFeature( feature ) )
204 {
205 if ( feedback && feedback->isCanceled() )
206 return;
207
210 mpl = feature.geometry().asMultiPolyline();
212 mpl.push_back( feature.geometry().asPolyline() );
213
214 for ( const QgsPolylineXY &line : std::as_const( mpl ) )
215 {
216 QgsPointXY pt1, pt2;
217 bool isFirstPoint = true;
218 for ( const QgsPointXY &point : line )
219 {
220 pt2 = ct.transform( point );
221
222 int pt2Idx = findPointWithinTolerance( pt2 );
223 if ( pt2Idx == -1 )
224 {
225 // no vertex already exists within tolerance - add to points, and index
226 addPointToIndex( pt2, graphVertices.count() );
227 graphVertices.push_back( pt2 );
228 }
229 else
230 {
231 // vertex already exists within tolerance - use that
232 pt2 = graphVertices.at( pt2Idx );
233 }
234
235 if ( !isFirstPoint )
236 {
237 // check if this line segment is a candidate for being closest to each additional point
238 int i = 0;
239 for ( const QgsPointXY &additionalPoint : additionalPoints )
240 {
241 QgsPointXY snappedPoint;
242 double thisSegmentClosestDist = std::numeric_limits<double>::max();
243 if ( pt1 == pt2 )
244 {
245 thisSegmentClosestDist = additionalPoint.sqrDist( pt1 );
246 snappedPoint = pt1;
247 }
248 else
249 {
250 thisSegmentClosestDist = additionalPoint.sqrDistToSegment( pt1.x(), pt1.y(), pt2.x(), pt2.y(), snappedPoint, 0 );
251 }
252
253 if ( thisSegmentClosestDist < additionalTiePoints[i].mLength )
254 {
255 // found a closer segment for this additional point
256 TiePointInfo info( i, feature.id(), pt1, pt2 );
257 info.mLength = thisSegmentClosestDist;
258 info.mTiedPoint = snappedPoint;
259
260 additionalTiePoints[i] = info;
261 snappedPoints[i] = info.mTiedPoint;
262 }
263 i++;
264 }
265 }
266 pt1 = pt2;
267 isFirstPoint = false;
268 }
269 }
270 if ( feedback )
271 feedback->setProgress( 100.0 * static_cast<double>( ++step ) / featureCount );
272 }
273
274 // build a hash of feature ids to tie points which depend on this feature
275 QHash<QgsFeatureId, QList<int>> tiePointNetworkFeatures;
276 int i = 0;
277 for ( TiePointInfo &info : additionalTiePoints )
278 {
279 tiePointNetworkFeatures[info.mNetworkFeatureId] << i;
280 i++;
281 }
282
283 // add tied point to graph
284 for ( int i = 0; i < snappedPoints.size(); ++i )
285 {
286 // check index to see if vertex exists within tolerance of tie point
287 const QgsPointXY point = snappedPoints.at( i );
288 int ptIdx = findPointWithinTolerance( point );
289 if ( ptIdx == -1 )
290 {
291 // no vertex already within tolerance, add to index and network vertices
292 addPointToIndex( point, graphVertices.count() );
293 graphVertices.push_back( point );
294 }
295 else
296 {
297 // otherwise snap tie point to vertex
298 snappedPoints[i] = graphVertices.at( ptIdx );
299 }
300 }
301 // also need to update tie points - they need to be matched for snapped points
302 for ( int i = 0; i < additionalTiePoints.count(); ++i )
303 {
304 additionalTiePoints[i].mTiedPoint = snappedPoints.at( additionalTiePoints.at( i ).additionalPointId );
305 }
306
307
308 // begin graph construction
309
310 // add vertices to graph
311 {
312 int i = 0;
313 for ( const QgsPointXY &point : graphVertices )
314 {
315 builder->addVertex( i, point );
316 i++;
317 }
318 }
319
320 fit = mSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( requiredAttributes() ) );
321 while ( fit.nextFeature( feature ) )
322 {
323 if ( feedback && feedback->isCanceled() )
324 return;
325
326 Direction direction = directionForFeature( feature );
327
328 // begin features segments and add arc to the Graph;
331 mpl = feature.geometry().asMultiPolyline();
333 mpl.push_back( feature.geometry().asPolyline() );
334
335 for ( const QgsPolylineXY &line : std::as_const( mpl ) )
336 {
337 QgsPointXY pt1, pt2;
338
339 bool isFirstPoint = true;
340 for ( const QgsPointXY &point : line )
341 {
342 pt2 = ct.transform( point );
343 int pPt2idx = findPointWithinTolerance( pt2 );
344 Q_ASSERT_X( pPt2idx >= 0, "QgsVectorLayerDirectory::makeGraph", "encountered a vertex which was not present in graph" );
345 pt2 = graphVertices.at( pPt2idx );
346
347 if ( !isFirstPoint )
348 {
349 QMap<double, QgsPointXY> pointsOnArc;
350 pointsOnArc[0.0] = pt1;
351 pointsOnArc[pt1.sqrDist( pt2 )] = pt2;
352
353 const QList<int> tiePointsForCurrentFeature = tiePointNetworkFeatures.value( feature.id() );
354 for ( int tiePointIdx : tiePointsForCurrentFeature )
355 {
356 const TiePointInfo &t = additionalTiePoints.at( tiePointIdx );
357 if ( t.mFirstPoint == pt1 && t.mLastPoint == pt2 )
358 {
359 pointsOnArc[pt1.sqrDist( t.mTiedPoint )] = t.mTiedPoint;
360 }
361 }
362
363 QgsPointXY arcPt1;
364 QgsPointXY arcPt2;
365 int pt1idx = -1;
366 int pt2idx = -1;
367 bool isFirstPoint = true;
368 for ( auto arcPointIt = pointsOnArc.constBegin(); arcPointIt != pointsOnArc.constEnd(); ++arcPointIt )
369 {
370 arcPt2 = arcPointIt.value();
371
372 pt2idx = findPointWithinTolerance( arcPt2 );
373 Q_ASSERT_X( pt2idx >= 0, "QgsVectorLayerDirectory::makeGraph", "encountered a vertex which was not present in graph" );
374 arcPt2 = graphVertices.at( pt2idx );
375
376 if ( !isFirstPoint && arcPt1 != arcPt2 )
377 {
378 double distance = 0;
379 try
380 {
381 distance = builder->distanceArea()->measureLine( arcPt1, arcPt2 );
382 }
383 catch ( QgsCsException & )
384 {
385 // TODO report errors to user
386 QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
387 }
388
389 QVector<QVariant> prop;
390 prop.reserve( mStrategies.size() );
391 for ( QgsNetworkStrategy *strategy : mStrategies )
392 {
393 prop.push_back( strategy->cost( distance, feature ) );
394 }
395
396 if ( direction == Direction::DirectionForward || direction == Direction::DirectionBoth )
397 {
398 builder->addEdge( pt1idx, arcPt1, pt2idx, arcPt2, prop );
399 }
400 if ( direction == Direction::DirectionBackward || direction == Direction::DirectionBoth )
401 {
402 builder->addEdge( pt2idx, arcPt2, pt1idx, arcPt1, prop );
403 }
404 }
405 pt1idx = pt2idx;
406 arcPt1 = arcPt2;
407 isFirstPoint = false;
408 }
409 }
410 pt1 = pt2;
411 isFirstPoint = false;
412 }
413 }
414 if ( feedback )
415 {
416 feedback->setProgress( 100.0 * static_cast<double>( ++step ) / featureCount );
417 }
418 }
419}
@ LineString
LineString.
@ MultiLineString
MultiLineString.
Class for doing transforms between two map 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.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
An interface for objects which provide features via a getFeatures method.
virtual QgsCoordinateReferenceSystem sourceCrs() const =0
Returns the coordinate reference system for features in the source.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.
virtual long long featureCount() const =0
Returns the number of features contained in the source, or -1 if the feature count is unknown.
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.)
Determine 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
QgsNetworkStrategy defines strategy used for calculation of the edge cost.
A class to represent 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 sqrDistToSegment(double x1, double y1, double x2, double y2, QgsPointXY &minDistPoint, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Returns the minimum distance between this point and a segment.
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:27
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition qgsgeometry.h:84
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:62
#define QgsDebugError(str)
Definition qgslogger.h:38
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