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