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