QGIS API Documentation 3.99.0-Master (a5475b57e34)
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( static_cast<int>( 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
213 const Qgis::WkbType wkbType = QgsWkbTypes::flatType( feature.geometry().wkbType() );
215 {
216 mpl = feature.geometry().asMultiPolyline();
217 }
219 {
220 mpl.push_back( feature.geometry().asPolyline() );
221 }
222
223 for ( const QgsPolylineXY &line : std::as_const( mpl ) )
224 {
225 QgsPointXY pt1, pt2;
226 bool isFirstPoint = true;
227 for ( const QgsPointXY &point : line )
228 {
229 pt2 = ct.transform( point );
230
231 int pt2Idx = findPointWithinTolerance( pt2 );
232 if ( pt2Idx == -1 )
233 {
234 // no vertex already exists within tolerance - add to points, and index
235 addPointToIndex( pt2, static_cast<int>( graphVertices.count() ) );
236 graphVertices.push_back( pt2 );
237 }
238 else
239 {
240 // vertex already exists within tolerance - use that
241 pt2 = graphVertices.at( pt2Idx );
242 }
243
244 if ( !isFirstPoint )
245 {
246 // check if this line segment is a candidate for being closest to each additional point
247 int i = 0;
248 for ( const QgsPointXY &additionalPoint : additionalPoints )
249 {
250 QgsPointXY snappedPoint;
251 double thisSegmentClosestDist = std::numeric_limits<double>::max();
252 if ( pt1 == pt2 )
253 {
254 thisSegmentClosestDist = additionalPoint.sqrDist( pt1 );
255 snappedPoint = pt1;
256 }
257 else
258 {
259 thisSegmentClosestDist = additionalPoint.sqrDistToSegment( pt1.x(), pt1.y(), pt2.x(), pt2.y(), snappedPoint, 0 );
260 }
261
262 if ( thisSegmentClosestDist < additionalTiePoints[i].mLength )
263 {
264 // found a closer segment for this additional point
265 TiePointInfo info( i, feature.id(), pt1, pt2 );
266 info.mLength = thisSegmentClosestDist;
267 info.mTiedPoint = snappedPoint;
268
269 additionalTiePoints[i] = info;
270 snappedPoints[i] = info.mTiedPoint;
271 }
272 i++;
273 }
274 }
275 pt1 = pt2;
276 isFirstPoint = false;
277 }
278 }
279 if ( feedback )
280 feedback->setProgress( 100.0 * static_cast<double>( ++step ) / static_cast<double>( featureCount ) );
281 }
282
283 // build a hash of feature ids to tie points which depend on this feature
284 QHash<QgsFeatureId, QList<int>> tiePointNetworkFeatures;
285 int i = 0;
286 for ( TiePointInfo &info : additionalTiePoints )
287 {
288 tiePointNetworkFeatures[info.mNetworkFeatureId] << i;
289 i++;
290 }
291
292 // add tied point to graph
293 for ( int i = 0; i < snappedPoints.size(); ++i )
294 {
295 // check index to see if vertex exists within tolerance of tie point
296 const QgsPointXY point = snappedPoints.at( i );
297 int ptIdx = findPointWithinTolerance( point );
298 if ( ptIdx == -1 )
299 {
300 // no vertex already within tolerance, add to index and network vertices
301 addPointToIndex( point, static_cast<int>( graphVertices.count() ) );
302 graphVertices.push_back( point );
303 }
304 else
305 {
306 // otherwise snap tie point to vertex
307 snappedPoints[i] = graphVertices.at( ptIdx );
308 }
309 }
310 // also need to update tie points - they need to be matched for snapped points
311 for ( int i = 0; i < additionalTiePoints.count(); ++i )
312 {
313 additionalTiePoints[i].mTiedPoint = snappedPoints.at( additionalTiePoints.at( i ).additionalPointId );
314 }
315
316 // begin graph construction
317
318 // add vertices to graph
319 {
320 int i = 0;
321 for ( const QgsPointXY &point : graphVertices )
322 {
323 builder->addVertex( i, point );
324 i++;
325 }
326 }
327
328 fit = mSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( requiredAttributes() ) );
329 while ( fit.nextFeature( feature ) )
330 {
331 if ( feedback && feedback->isCanceled() )
332 return;
333
334 Direction direction = directionForFeature( feature );
335
336 // begin features segments and add arc to the Graph;
338 const Qgis::WkbType wkbType = QgsWkbTypes::flatType( feature.geometry().wkbType() );
340 {
341 mpl = feature.geometry().asMultiPolyline();
342 }
344 {
345 mpl.push_back( feature.geometry().asPolyline() );
346 }
347
348 for ( const QgsPolylineXY &line : std::as_const( mpl ) )
349 {
350 QgsPointXY pt1, pt2;
351
352 bool isFirstPoint = true;
353 for ( const QgsPointXY &point : line )
354 {
355 pt2 = ct.transform( point );
356 int pPt2idx = findPointWithinTolerance( pt2 );
357 Q_ASSERT_X( pPt2idx >= 0, "QgsVectorLayerDirector::makeGraph", "encountered a vertex which was not present in graph" );
358 pt2 = graphVertices.at( pPt2idx );
359
360 if ( !isFirstPoint )
361 {
362 QMap<double, QgsPointXY> pointsOnArc;
363 pointsOnArc[0.0] = pt1;
364 pointsOnArc[pt1.sqrDist( pt2 )] = pt2;
365
366 const QList<int> tiePointsForCurrentFeature = tiePointNetworkFeatures.value( feature.id() );
367 for ( int tiePointIdx : tiePointsForCurrentFeature )
368 {
369 const TiePointInfo &t = additionalTiePoints.at( tiePointIdx );
370 if ( t.mFirstPoint == pt1 && t.mLastPoint == pt2 )
371 {
372 pointsOnArc[pt1.sqrDist( t.mTiedPoint )] = t.mTiedPoint;
373 }
374 }
375
376 QgsPointXY arcPt1;
377 QgsPointXY arcPt2;
378 int pt1idx = -1;
379 int pt2idx = -1;
380 bool isFirstPoint = true;
381 for ( auto arcPointIt = pointsOnArc.constBegin(); arcPointIt != pointsOnArc.constEnd(); ++arcPointIt )
382 {
383 arcPt2 = arcPointIt.value();
384
385 pt2idx = findPointWithinTolerance( arcPt2 );
386 Q_ASSERT_X( pt2idx >= 0, "QgsVectorLayerDirector::makeGraph", "encountered a vertex which was not present in graph" );
387 arcPt2 = graphVertices.at( pt2idx );
388
389 if ( !isFirstPoint && arcPt1 != arcPt2 )
390 {
391 double distance = 0;
392 try
393 {
394 distance = builder->distanceArea()->measureLine( arcPt1, arcPt2 );
395 }
396 catch ( QgsCsException & )
397 {
398 // TODO report errors to user
399 QgsDebugError( u"An error occurred while calculating length"_s );
400 }
401
402 QVector<QVariant> prop;
403 prop.reserve( mStrategies.size() );
404 for ( QgsNetworkStrategy *strategy : mStrategies )
405 {
406 prop.push_back( strategy->cost( distance, feature ) );
407 }
408
409 if ( direction == Direction::DirectionForward || direction == Direction::DirectionBoth )
410 {
411 builder->addEdge( pt1idx, arcPt1, pt2idx, arcPt2, prop );
412 }
413 if ( direction == Direction::DirectionBackward || direction == Direction::DirectionBoth )
414 {
415 builder->addEdge( pt2idx, arcPt2, pt1idx, arcPt1, prop );
416 }
417 }
418 pt1idx = pt2idx;
419 arcPt1 = arcPt2;
420 isFirstPoint = false;
421 }
422 }
423 pt1 = pt2;
424 isFirstPoint = false;
425 }
426 }
427 if ( feedback )
428 {
429 feedback->setProgress( 100.0 * static_cast<double>( ++step ) / static_cast<double>( featureCount ) );
430 }
431 }
432}
@ Line
Lines.
Definition qgis.h:378
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:291
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::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi 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