QGIS API Documentation  3.0.2-Girona (307d082)
qgssnappingutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssnappingutils.cpp
3  --------------------------------------
4  Date : November 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
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 
16 #include "qgssnappingutils.h"
17 
18 #include "qgsgeometry.h"
19 #include "qgsproject.h"
20 #include "qgsvectorlayer.h"
21 #include "qgslogger.h"
22 
24  : QObject( parent )
25  , mSnappingConfig( QgsProject::instance() )
26 {
27 }
28 
30 {
32 }
33 
34 
36 {
37  if ( !vl )
38  return nullptr;
39 
40  if ( !mLocators.contains( vl ) )
41  {
42  QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext() );
43  mLocators.insert( vl, vlpl );
44  }
45  return mLocators.value( vl );
46 }
47 
49 {
50  qDeleteAll( mLocators );
51  mLocators.clear();
52 
53  qDeleteAll( mTemporaryLocators );
54  mTemporaryLocators.clear();
55 }
56 
57 
58 QgsPointLocator *QgsSnappingUtils::locatorForLayerUsingStrategy( QgsVectorLayer *vl, const QgsPointXY &pointMap, double tolerance )
59 {
60  QgsRectangle aoi( pointMap.x() - tolerance, pointMap.y() - tolerance,
61  pointMap.x() + tolerance, pointMap.y() + tolerance );
62  if ( isIndexPrepared( vl, aoi ) )
63  return locatorForLayer( vl );
64  else
65  return temporaryLocatorForLayer( vl, pointMap, tolerance );
66 }
67 
68 QgsPointLocator *QgsSnappingUtils::temporaryLocatorForLayer( QgsVectorLayer *vl, const QgsPointXY &pointMap, double tolerance )
69 {
70  if ( mTemporaryLocators.contains( vl ) )
71  delete mTemporaryLocators.take( vl );
72 
73  QgsRectangle rect( pointMap.x() - tolerance, pointMap.y() - tolerance,
74  pointMap.x() + tolerance, pointMap.y() + tolerance );
75  QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), &rect );
76  mTemporaryLocators.insert( vl, vlpl );
77  return mTemporaryLocators.value( vl );
78 }
79 
80 bool QgsSnappingUtils::isIndexPrepared( QgsVectorLayer *vl, const QgsRectangle &areaOfInterest )
81 {
82  if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
83  return false;
84 
85  QgsPointLocator *loc = locatorForLayer( vl );
86 
87  if ( mStrategy == IndexAlwaysFull && loc->hasIndex() )
88  return true;
89 
90  QgsRectangle aoi( areaOfInterest );
91  aoi.scale( 0.999 );
92  return ( mStrategy == IndexHybrid || mStrategy == IndexExtent ) && loc->hasIndex() && ( !loc->extent() || loc->extent()->contains( aoi ) ); // the index - even if it exists - is not suitable
93 }
94 
95 
96 static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY &pt, const QgsPointLocator::MatchList &segments )
97 {
98  if ( segments.isEmpty() )
99  return QgsPointLocator::Match();
100 
101  QSet<QgsPointXY> endpoints;
102 
103  // make a geometry
104  QVector<QgsGeometry> geoms;
105  Q_FOREACH ( const QgsPointLocator::Match &m, segments )
106  {
107  if ( m.hasEdge() )
108  {
109  QgsPolylineXY pl( 2 );
110  m.edgePoints( pl[0], pl[1] );
111  geoms << QgsGeometry::fromPolylineXY( pl );
112  endpoints << pl[0] << pl[1];
113  }
114  }
115 
117 
118  // get intersection points
119  QList<QgsPointXY> newPoints;
120  if ( g.wkbType() == QgsWkbTypes::LineString )
121  {
122  Q_FOREACH ( const QgsPointXY &p, g.asPolyline() )
123  {
124  if ( !endpoints.contains( p ) )
125  newPoints << p;
126  }
127  }
129  {
130  Q_FOREACH ( const QgsPolylineXY &pl, g.asMultiPolyline() )
131  {
132  Q_FOREACH ( const QgsPointXY &p, pl )
133  {
134  if ( !endpoints.contains( p ) )
135  newPoints << p;
136  }
137  }
138  }
139 
140  if ( newPoints.isEmpty() )
141  return QgsPointLocator::Match();
142 
143  // find the closest points
144  QgsPointXY minP;
145  double minSqrDist = 1e20; // "infinity"
146  Q_FOREACH ( const QgsPointXY &p, newPoints )
147  {
148  double sqrDist = pt.sqrDist( p.x(), p.y() );
149  if ( sqrDist < minSqrDist )
150  {
151  minSqrDist = sqrDist;
152  minP = p;
153  }
154  }
155 
156  return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP );
157 }
158 
159 
160 static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointLocator::Match &candidateMatch, double maxDistance )
161 {
162  // is candidate match relevant?
163  if ( !candidateMatch.isValid() || candidateMatch.distance() > maxDistance )
164  return;
165 
166  // is candidate match actually better?
167  if ( bestMatch.isValid() && bestMatch.type() == candidateMatch.type() && bestMatch.distance() - 10e-6 < candidateMatch.distance() )
168  return;
169 
170  // prefer vertex matches over edge matches (even if they are closer)
171  if ( bestMatch.type() == QgsPointLocator::Vertex && candidateMatch.type() == QgsPointLocator::Edge )
172  return;
173 
174  bestMatch = candidateMatch; // the other match is better!
175 }
176 
177 
178 static void _updateBestMatch( QgsPointLocator::Match &bestMatch, const QgsPointXY &pointMap, QgsPointLocator *loc, QgsPointLocator::Types type, double tolerance, QgsPointLocator::MatchFilter *filter )
179 {
180  if ( type & QgsPointLocator::Vertex )
181  {
182  _replaceIfBetter( bestMatch, loc->nearestVertex( pointMap, tolerance, filter ), tolerance );
183  }
184  if ( bestMatch.type() != QgsPointLocator::Vertex && ( type & QgsPointLocator::Edge ) )
185  {
186  _replaceIfBetter( bestMatch, loc->nearestEdge( pointMap, tolerance, filter ), tolerance );
187  }
188  if ( bestMatch.type() != QgsPointLocator::Vertex && bestMatch.type() != QgsPointLocator::Edge && ( type & QgsPointLocator::Area ) )
189  {
190  // if edges were detected, set tolerance to 0 to only do pointInPolygon (and avoid redo nearestEdge)
191  if ( type & QgsPointLocator::Edge )
192  tolerance = 0;
193  _replaceIfBetter( bestMatch, loc->nearestArea( pointMap, tolerance, filter ), tolerance );
194  }
195 }
196 
197 
198 static QgsPointLocator::Types _snappingTypeToPointLocatorType( QgsSnappingConfig::SnappingType type )
199 {
200  // watch out: vertex+segment vs segment are in different order in the two enums
201  switch ( type )
202  {
206  return QgsPointLocator::Types( QgsPointLocator::Vertex | QgsPointLocator::Edge );
208  return QgsPointLocator::Edge;
209  default:
211  }
212 }
213 
214 
216 {
217  return snapToMap( mMapSettings.mapToPixel().toMapCoordinates( point ), filter );
218 }
219 
220 inline QgsRectangle _areaOfInterest( const QgsPointXY &point, double tolerance )
221 {
222  return QgsRectangle( point.x() - tolerance, point.y() - tolerance,
223  point.x() + tolerance, point.y() + tolerance );
224 }
225 
227 {
228  if ( !mMapSettings.hasValidSettings() || !mSnappingConfig.enabled() )
229  {
230  return QgsPointLocator::Match();
231  }
232 
233  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
234  {
235  if ( !mCurrentLayer || mSnappingConfig.type() == 0 )
236  return QgsPointLocator::Match();
237 
238  // data from project
239  double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), mCurrentLayer, mMapSettings, mSnappingConfig.units() );
240  QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.type() );
241 
242  prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer, _areaOfInterest( pointMap, tolerance ) ) );
243 
244  // use ad-hoc locator
245  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
246  if ( !loc )
247  return QgsPointLocator::Match();
248 
249  QgsPointLocator::Match bestMatch;
250  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
251 
252  if ( mSnappingConfig.intersectionSnapping() )
253  {
254  QgsPointLocator *locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
255  QgsPointLocator::MatchList edges = locEdges->edgesInRect( pointMap, tolerance );
256  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
257  }
258 
259  return bestMatch;
260  }
261  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
262  {
263  QList<LayerAndAreaOfInterest> layers;
264  Q_FOREACH ( const LayerConfig &layerConfig, mLayers )
265  {
266  double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit );
267  layers << qMakePair( layerConfig.layer, _areaOfInterest( pointMap, tolerance ) );
268  }
269  prepareIndex( layers );
270 
271  QgsPointLocator::Match bestMatch;
272  QgsPointLocator::MatchList edges; // for snap on intersection
273  double maxSnapIntTolerance = 0;
274 
275  Q_FOREACH ( const LayerConfig &layerConfig, mLayers )
276  {
277  double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit );
278  if ( QgsPointLocator *loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
279  {
280  _updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter );
281 
282  if ( mSnappingConfig.intersectionSnapping() )
283  {
284  edges << loc->edgesInRect( pointMap, tolerance );
285  maxSnapIntTolerance = std::max( maxSnapIntTolerance, tolerance );
286  }
287  }
288  }
289 
290  if ( mSnappingConfig.intersectionSnapping() )
291  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxSnapIntTolerance );
292 
293  return bestMatch;
294  }
295  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
296  {
297  // data from project
298  double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), nullptr, mMapSettings, mSnappingConfig.units() );
299  QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.type() );
300  QgsRectangle aoi = _areaOfInterest( pointMap, tolerance );
301 
302  QList<LayerAndAreaOfInterest> layers;
303  Q_FOREACH ( QgsMapLayer *layer, mMapSettings.layers() )
304  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
305  layers << qMakePair( vl, aoi );
306  prepareIndex( layers );
307 
308  QgsPointLocator::MatchList edges; // for snap on intersection
309  QgsPointLocator::Match bestMatch;
310 
311  Q_FOREACH ( const LayerAndAreaOfInterest &entry, layers )
312  {
313  QgsVectorLayer *vl = entry.first;
314  if ( QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance ) )
315  {
316  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
317 
318  if ( mSnappingConfig.intersectionSnapping() )
319  edges << loc->edgesInRect( pointMap, tolerance );
320  }
321  }
322 
323  if ( mSnappingConfig.intersectionSnapping() )
324  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
325 
326  return bestMatch;
327  }
328 
329  return QgsPointLocator::Match();
330 }
331 
332 
333 void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers )
334 {
335  if ( mIsIndexing )
336  return;
337  mIsIndexing = true;
338 
339  // check if we need to build any index
340  QList<LayerAndAreaOfInterest> layersToIndex;
341  Q_FOREACH ( const LayerAndAreaOfInterest &entry, layers )
342  {
343  QgsVectorLayer *vl = entry.first;
344  if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
345  continue;
346 
347  if ( !isIndexPrepared( vl, entry.second ) )
348  layersToIndex << entry;
349  }
350  if ( !layersToIndex.isEmpty() )
351  {
352  // build indexes
353  QTime t;
354  t.start();
355  int i = 0;
356  prepareIndexStarting( layersToIndex.count() );
357  Q_FOREACH ( const LayerAndAreaOfInterest &entry, layersToIndex )
358  {
359  QgsVectorLayer *vl = entry.first;
360  QTime tt;
361  tt.start();
362  QgsPointLocator *loc = locatorForLayer( vl );
363  if ( mStrategy == IndexExtent )
364  {
365  QgsRectangle rect( mMapSettings.extent() );
366  loc->setExtent( &rect );
367  loc->init();
368  }
369  else if ( mStrategy == IndexHybrid )
370  {
371  // first time the layer is used? - let's set an initial guess about indexing
372  if ( !mHybridMaxAreaPerLayer.contains( vl->id() ) )
373  {
374  int totalFeatureCount = vl->featureCount();
375  if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
376  {
377  // index the whole layer
378  mHybridMaxAreaPerLayer[vl->id()] = -1;
379  }
380  else
381  {
382  // estimate for how big area it probably makes sense to build partial index to not exceed the limit
383  // (we may change the limit later)
384  QgsRectangle layerExtent = mMapSettings.layerExtentToOutputExtent( vl, vl->extent() );
385  double totalArea = layerExtent.width() * layerExtent.height();
386  mHybridMaxAreaPerLayer[vl->id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
387  }
388  }
389 
390  double indexReasonableArea = mHybridMaxAreaPerLayer[vl->id()];
391  if ( indexReasonableArea == -1 )
392  {
393  // we can safely index the whole layer
394  loc->init();
395  }
396  else
397  {
398  // use area as big as we think may fit into our limit
399  QgsPointXY c = entry.second.center();
400  double halfSide = std::sqrt( indexReasonableArea ) / 2;
401  QgsRectangle rect( c.x() - halfSide, c.y() - halfSide,
402  c.x() + halfSide, c.y() + halfSide );
403  loc->setExtent( &rect );
404 
405  // see if it's possible build index for this area
406  if ( !loc->init( mHybridPerLayerFeatureLimit ) )
407  {
408  // hmm that didn't work out - too many features!
409  // let's make the allowed area smaller for the next time
410  mHybridMaxAreaPerLayer[vl->id()] /= 4;
411  }
412  }
413 
414  }
415  else // full index strategy
416  loc->init();
417 
418  QgsDebugMsg( QString( "Index init: %1 ms (%2)" ).arg( tt.elapsed() ).arg( vl->id() ) );
419  prepareIndexProgress( ++i );
420  }
421  QgsDebugMsg( QString( "Prepare index total: %1 ms" ).arg( t.elapsed() ) );
422  }
423  mIsIndexing = false;
424 }
425 
427 {
428  return mSnappingConfig;
429 }
430 
432 {
433  if ( mSnappingConfig == config )
434  return;
435 
436  if ( mSnappingConfig.individualLayerSettings() != config.individualLayerSettings() )
437  onIndividualLayerSettingsChanged( config.individualLayerSettings() );
438 
439  mSnappingConfig = config;
440 
441  emit configChanged( mSnappingConfig );
442 }
443 
445 {
446  mSnappingConfig.setEnabled( !mSnappingConfig.enabled() );
447  emit configChanged( mSnappingConfig );
448 }
449 
451 {
452  if ( !mCurrentLayer )
453  return QgsPointLocator::Match();
454 
455  QgsPointXY pointMap = mMapSettings.mapToPixel().toMapCoordinates( point );
456  double tolerance = QgsTolerance::vertexSearchRadius( mMapSettings );
457 
458  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
459  if ( !loc )
460  return QgsPointLocator::Match();
461 
462  QgsPointLocator::Match bestMatch;
463  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
464  return bestMatch;
465 }
466 
468 {
469  QString oldDestCRS = mMapSettings.destinationCrs().authid();
470  QString newDestCRS = settings.destinationCrs().authid();
471  mMapSettings = settings;
472 
473  if ( newDestCRS != oldDestCRS )
475 }
476 
478 {
479  mCurrentLayer = layer;
480 }
481 
483 {
484  QString msg = QStringLiteral( "--- SNAPPING UTILS DUMP ---\n" );
485 
486  if ( !mMapSettings.hasValidSettings() )
487  {
488  msg += QLatin1String( "invalid map settings!" );
489  return msg;
490  }
491 
492  QList<LayerConfig> layers;
493 
494  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
495  {
496  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer && !mCurrentLayer )
497  {
498  msg += QLatin1String( "no current layer!" );
499  return msg;
500  }
501 
502  layers << LayerConfig( mCurrentLayer, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
503  }
504  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
505  {
506  Q_FOREACH ( QgsMapLayer *layer, mMapSettings.layers() )
507  {
508  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
509  layers << LayerConfig( vl, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
510  }
511  }
512  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
513  {
514  layers = mLayers;
515  }
516 
517  Q_FOREACH ( const LayerConfig &layer, layers )
518  {
519  msg += QString( "layer : %1\n"
520  "config: %2 tolerance %3 %4\n" )
521  .arg( layer.layer->name() )
522  .arg( layer.type ).arg( layer.tolerance ).arg( layer.unit );
523 
524  if ( mStrategy == IndexAlwaysFull || mStrategy == IndexHybrid || mStrategy == IndexExtent )
525  {
526  if ( QgsPointLocator *loc = locatorForLayer( layer.layer ) )
527  {
528  QString extentStr, cachedGeoms, limit( QStringLiteral( "no max area" ) );
529  if ( const QgsRectangle *r = loc->extent() )
530  {
531  extentStr = QStringLiteral( " extent %1" ).arg( r->toString() );
532  }
533  else
534  extentStr = QStringLiteral( "full extent" );
535  if ( loc->hasIndex() )
536  cachedGeoms = QStringLiteral( "%1 feats" ).arg( loc->cachedGeometryCount() );
537  else
538  cachedGeoms = QStringLiteral( "not initialized" );
539  if ( mStrategy == IndexHybrid )
540  {
541  if ( mHybridMaxAreaPerLayer.contains( layer.layer->id() ) )
542  {
543  double maxArea = mStrategy == IndexHybrid ? mHybridMaxAreaPerLayer[layer.layer->id()] : -1;
544  if ( maxArea != -1 )
545  limit = QStringLiteral( "max area %1" ).arg( maxArea );
546  }
547  else
548  limit = QStringLiteral( "not evaluated" );
549  }
550  msg += QStringLiteral( "index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
551  }
552  else
553  msg += QStringLiteral( "index : ???\n" ); // should not happen
554  }
555  else
556  msg += QLatin1String( "index : NO\n" );
557  msg += QLatin1String( "-\n" );
558  }
559 
560  return msg;
561 }
562 
563 QgsCoordinateReferenceSystem QgsSnappingUtils::destinationCrs() const
564 {
565  return mMapSettings.destinationCrs();
566 }
567 
568 void QgsSnappingUtils::onIndividualLayerSettingsChanged( const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
569 {
570  mLayers.clear();
571 
572  QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
573 
574  for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
575  {
576  if ( i->enabled() )
577  {
578  mLayers.append( LayerConfig( i.key(), _snappingTypeToPointLocatorType( i->type() ), i->tolerance(), i->units() ) );
579  }
580  }
581 }
void setEnabled(bool enabled)
enables the snapping
The class defines interface for querying point location:
bool contains(const QgsRectangle &rect) const
Return true when rectangle contains other rectangle.
A rectangle specified with double values.
Definition: qgsrectangle.h:39
Base class for all map layer types.
Definition: qgsmaplayer.h:56
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
SnappingType type() const
return the type (vertices and/or segments)
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
SnappingMode mode() const
return the mode (all layers, active layer, per layer settings)
void setCurrentLayer(QgsVectorLayer *layer)
Set current layer so that if mode is SnapCurrentLayer we know which layer to use. ...
~QgsSnappingUtils() override
virtual void prepareIndexProgress(int index)
Called when finished indexing a layer. When index == count the indexing is complete.
QgsPointLocator::Type type() const
Both on vertices and segments.
bool enabled() const
return if snapping is enabled
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool hasIndex() const
Indicate whether the data have been already indexed.
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
For all layer build index of extent given in map settings.
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
Match nearestEdge(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest edge to the specified point - up to distance specified by tolerance Optional filter may ...
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
class QList< QgsPointLocator::Match > MatchList
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
QgsTolerance::UnitType units() const
return the type of units
Match nearestArea(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest area to the specified point - up to distance specified by tolerance Optional filter may ...
On all vector layers.
QList< QgsMapLayer * > layers() const
Get list of layers for map rendering The layers are stored in the reverse order of how they are rende...
Interface that allows rejection of some matches in intersection queries (e.g.
bool intersectionSnapping() const
return if the snapping on intersection is enabled
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.cpp:68
QgsMultiPolylineXY asMultiPolyline() const
Returns contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
For all layers build index of full extent. Uses more memory, but queries are faster.
QgsRectangle extent() const
Return geographical coordinates of the rectangle that should be rendered.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QString dump()
Get extra information about the instance.
For "big" layers using IndexNeverFull, for the rest IndexAlwaysFull. Compromise between speed and mem...
QgsRectangle extent() const override
Returns the extent of the layer.
void clearAllLocators()
Deletes all existing locators (e.g. when destination CRS has changed and we need to reindex) ...
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:142
QList< QgsSnappingUtils::LayerConfig > layers() const
Query layers used for snapping.
QgsSnappingUtils(QObject *parent=nullptr)
Constructor for QgsSnappingUtils.
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer&#39;s CRS to output CRS
Reads and writes project states.
Definition: qgsproject.h:82
On a per layer configuration basis.
void setExtent(const QgsRectangle *extent)
Configure extent - if not null, it will index only that area.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
QgsTolerance::UnitType unit
The units in which the tolerance is specified.
void toggleEnabled()
Toggles the state of snapping.
void setMapSettings(const QgsMapSettings &settings)
Assign current map settings to the utils - used for conversion between screen coords to map coords...
const QgsMapToPixel & mapToPixel() const
double tolerance
The range around snapping targets in which snapping should occur.
const QgsRectangle * extent() const
Get extent of the area point locator covers - if null then it caches the whole layer.
QgsSnappingConfig config() const
The snapping configuration controls the behavior of this object.
double x
Definition: qgspointxy.h:47
For all layers only create temporary indexes of small extent. Low memory usage, slower queries...
void configChanged(const QgsSnappingConfig &snappingConfig)
Emitted when the snapping settings object changes.
static double vertexSearchRadius(const QgsMapSettings &mapSettings)
Static function to get vertex tolerance value.
int cachedGeometryCount() const
Return how many geometries are cached in the index.
virtual void prepareIndexStarting(int count)
Called when starting to index - can be overridden and e.g. progress dialog can be provided...
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:49
QgsPointLocator * locatorForLayer(QgsVectorLayer *vl)
Get a point locator for the given layer. If such locator does not exist, it will be created...
SnappingType
SnappingType defines on what object the snapping is performed.
static double toleranceInProjectUnits(double tolerance, QgsMapLayer *layer, const QgsMapSettings &mapSettings, QgsTolerance::UnitType units)
Static function to translate tolerance value into map units.
QgsVectorLayer * layer
The layer to configure.
Configures how a certain layer should be handled in a snapping operation.
bool init(int maxFeaturesToIndex=-1)
Prepare the index for queries.
This class represents a coordinate reference system (CRS).
Match nearestVertex(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest vertex to the specified point - up to distance specified by tolerance Optional filter ma...
QgsPointLocator::Types type
To which geometry properties of this layers a snapping should happen.
Snapped to an edge.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units...
QString name
Definition: qgsmaplayer.h:60
QgsPolylineXY asPolyline() const
Returns contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
double tolerance() const
return the tolerance
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr)
Find edges within a specified recangle Optional filter may discard unwanted matches.
This is a container for configuration of the snapping of the project.
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to map according to the current configuration. Optional filter allows discarding unwanted matche...
QHash< QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings > individualLayerSettings() const
return individual snapping settings for all layers
Represents a vector layer which manages a vector based data sets.
QgsRectangle _areaOfInterest(const QgsPointXY &point, double tolerance)
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
Snapped to an area.
QgsPointXY toMapCoordinates(int x, int y) const
QString authid() const
Returns the authority identifier for the CRS.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:149
QgsPointLocator::Match snapToCurrentLayer(QPoint point, QgsPointLocator::Types type, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to current layer.