QGIS API Documentation  3.2.0-Bonn (bc43194)
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 #include "qgsrenderer.h"
23 
24 QgsSnappingUtils::QgsSnappingUtils( QObject *parent, bool enableSnappingForInvisibleFeature )
25  : QObject( parent )
26  , mSnappingConfig( QgsProject::instance() )
27  , mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature )
28 {
29 }
30 
32 {
34 }
35 
36 
38 {
39  if ( !vl )
40  return nullptr;
41 
42  if ( !mLocators.contains( vl ) )
43  {
44  QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext() );
45  mLocators.insert( vl, vlpl );
46  }
47  return mLocators.value( vl );
48 }
49 
51 {
52  qDeleteAll( mLocators );
53  mLocators.clear();
54 
55  qDeleteAll( mTemporaryLocators );
56  mTemporaryLocators.clear();
57 }
58 
59 
60 QgsPointLocator *QgsSnappingUtils::locatorForLayerUsingStrategy( QgsVectorLayer *vl, const QgsPointXY &pointMap, double tolerance )
61 {
62  QgsRectangle aoi( pointMap.x() - tolerance, pointMap.y() - tolerance,
63  pointMap.x() + tolerance, pointMap.y() + tolerance );
64  if ( isIndexPrepared( vl, aoi ) )
65  return locatorForLayer( vl );
66  else
67  return temporaryLocatorForLayer( vl, pointMap, tolerance );
68 }
69 
70 QgsPointLocator *QgsSnappingUtils::temporaryLocatorForLayer( QgsVectorLayer *vl, const QgsPointXY &pointMap, double tolerance )
71 {
72  if ( mTemporaryLocators.contains( vl ) )
73  delete mTemporaryLocators.take( vl );
74 
75  QgsRectangle rect( pointMap.x() - tolerance, pointMap.y() - tolerance,
76  pointMap.x() + tolerance, pointMap.y() + tolerance );
77  QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), &rect );
78  mTemporaryLocators.insert( vl, vlpl );
79  return mTemporaryLocators.value( vl );
80 }
81 
82 bool QgsSnappingUtils::isIndexPrepared( QgsVectorLayer *vl, const QgsRectangle &areaOfInterest )
83 {
84  if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
85  return false;
86 
87  QgsPointLocator *loc = locatorForLayer( vl );
88 
89  if ( mStrategy == IndexAlwaysFull && loc->hasIndex() )
90  return true;
91 
92  QgsRectangle aoi( areaOfInterest );
93  aoi.scale( 0.999 );
94  return ( mStrategy == IndexHybrid || mStrategy == IndexExtent ) && loc->hasIndex() && ( !loc->extent() || loc->extent()->contains( aoi ) ); // the index - even if it exists - is not suitable
95 }
96 
97 static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY &pt, const QgsPointLocator::MatchList &segments )
98 {
99  if ( segments.isEmpty() )
100  return QgsPointLocator::Match();
101 
102  QSet<QgsPointXY> endpoints;
103 
104  // make a geometry
105  QVector<QgsGeometry> geoms;
106  Q_FOREACH ( const QgsPointLocator::Match &m, segments )
107  {
108  if ( m.hasEdge() )
109  {
110  QgsPolylineXY pl( 2 );
111  m.edgePoints( pl[0], pl[1] );
112  geoms << QgsGeometry::fromPolylineXY( pl );
113  endpoints << pl[0] << pl[1];
114  }
115  }
116 
118 
119  // get intersection points
120  QList<QgsPointXY> newPoints;
121  if ( g.wkbType() == QgsWkbTypes::LineString )
122  {
123  Q_FOREACH ( const QgsPointXY &p, g.asPolyline() )
124  {
125  if ( !endpoints.contains( p ) )
126  newPoints << p;
127  }
128  }
130  {
131  Q_FOREACH ( const QgsPolylineXY &pl, g.asMultiPolyline() )
132  {
133  Q_FOREACH ( const QgsPointXY &p, pl )
134  {
135  if ( !endpoints.contains( p ) )
136  newPoints << p;
137  }
138  }
139  }
140 
141  if ( newPoints.isEmpty() )
142  return QgsPointLocator::Match();
143 
144  // find the closest points
145  QgsPointXY minP;
146  double minSqrDist = 1e20; // "infinity"
147  Q_FOREACH ( const QgsPointXY &p, newPoints )
148  {
149  double sqrDist = pt.sqrDist( p.x(), p.y() );
150  if ( sqrDist < minSqrDist )
151  {
152  minSqrDist = sqrDist;
153  minP = p;
154  }
155  }
156 
157  return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP );
158 }
159 
160 static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointLocator::Match &candidateMatch, double maxDistance )
161 {
162 
163  // is candidate match relevant?
164  if ( !candidateMatch.isValid() || candidateMatch.distance() > maxDistance )
165  return;
166 
167  // is candidate match actually better?
168  if ( bestMatch.isValid() && bestMatch.type() == candidateMatch.type() && bestMatch.distance() - 10e-6 < candidateMatch.distance() )
169  return;
170 
171  // prefer vertex matches over edge matches (even if they are closer)
172  if ( bestMatch.type() == QgsPointLocator::Vertex && candidateMatch.type() == QgsPointLocator::Edge )
173  return;
174 
175  bestMatch = candidateMatch; // the other match is better!
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 void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers )
333 {
334  if ( mIsIndexing )
335  return;
336  mIsIndexing = true;
337 
338  // check if we need to build any index
339  QList<LayerAndAreaOfInterest> layersToIndex;
340  Q_FOREACH ( const LayerAndAreaOfInterest &entry, layers )
341  {
342  QgsVectorLayer *vl = entry.first;
343 
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 
363  QgsPointLocator *loc = locatorForLayer( vl );
364 
365  if ( !mEnableSnappingForInvisibleFeature )
366  {
368  loc->setRenderContext( &ctx );
369  }
370 
371  if ( mStrategy == IndexExtent )
372  {
373  QgsRectangle rect( mMapSettings.extent() );
374  loc->setExtent( &rect );
375  loc->init();
376  }
377  else if ( mStrategy == IndexHybrid )
378  {
379  // first time the layer is used? - let's set an initial guess about indexing
380  if ( !mHybridMaxAreaPerLayer.contains( vl->id() ) )
381  {
382  int totalFeatureCount = vl->featureCount();
383  if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
384  {
385  // index the whole layer
386  mHybridMaxAreaPerLayer[vl->id()] = -1;
387  }
388  else
389  {
390  // estimate for how big area it probably makes sense to build partial index to not exceed the limit
391  // (we may change the limit later)
392  QgsRectangle layerExtent = mMapSettings.layerExtentToOutputExtent( vl, vl->extent() );
393  double totalArea = layerExtent.width() * layerExtent.height();
394  mHybridMaxAreaPerLayer[vl->id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
395  }
396  }
397 
398  double indexReasonableArea = mHybridMaxAreaPerLayer[vl->id()];
399  if ( indexReasonableArea == -1 )
400  {
401  // we can safely index the whole layer
402  loc->init();
403  }
404  else
405  {
406  // use area as big as we think may fit into our limit
407  QgsPointXY c = entry.second.center();
408  double halfSide = std::sqrt( indexReasonableArea ) / 2;
409  QgsRectangle rect( c.x() - halfSide, c.y() - halfSide,
410  c.x() + halfSide, c.y() + halfSide );
411  loc->setExtent( &rect );
412 
413  // see if it's possible build index for this area
414  if ( !loc->init( mHybridPerLayerFeatureLimit ) )
415  {
416  // hmm that didn't work out - too many features!
417  // let's make the allowed area smaller for the next time
418  mHybridMaxAreaPerLayer[vl->id()] /= 4;
419  }
420  }
421 
422  }
423  else // full index strategy
424  loc->init();
425 
426  QgsDebugMsg( QString( "Index init: %1 ms (%2)" ).arg( tt.elapsed() ).arg( vl->id() ) );
427  prepareIndexProgress( ++i );
428  }
429  QgsDebugMsg( QString( "Prepare index total: %1 ms" ).arg( t.elapsed() ) );
430  }
431  mIsIndexing = false;
432 }
433 
435 {
436  return mSnappingConfig;
437 }
438 
440 {
441  mEnableSnappingForInvisibleFeature = enable;
442 }
443 
445 {
446  if ( mSnappingConfig == config )
447  return;
448 
449  if ( mSnappingConfig.individualLayerSettings() != config.individualLayerSettings() )
450  onIndividualLayerSettingsChanged( config.individualLayerSettings() );
451 
452  mSnappingConfig = config;
453 
454  emit configChanged( mSnappingConfig );
455 }
456 
458 {
459  mSnappingConfig.setEnabled( !mSnappingConfig.enabled() );
460  emit configChanged( mSnappingConfig );
461 }
462 
464 {
465  if ( !mCurrentLayer )
466  return QgsPointLocator::Match();
467 
468  QgsPointXY pointMap = mMapSettings.mapToPixel().toMapCoordinates( point );
469  double tolerance = QgsTolerance::vertexSearchRadius( mMapSettings );
470 
471  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
472  if ( !loc )
473  return QgsPointLocator::Match();
474 
475  QgsPointLocator::Match bestMatch;
476  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
477  return bestMatch;
478 }
479 
481 {
482  QString oldDestCRS = mMapSettings.destinationCrs().authid();
483  QString newDestCRS = settings.destinationCrs().authid();
484  mMapSettings = settings;
485 
486  if ( newDestCRS != oldDestCRS )
488 }
489 
491 {
492  mCurrentLayer = layer;
493 }
494 
496 {
497  QString msg = QStringLiteral( "--- SNAPPING UTILS DUMP ---\n" );
498 
499  if ( !mMapSettings.hasValidSettings() )
500  {
501  msg += QLatin1String( "invalid map settings!" );
502  return msg;
503  }
504 
505  QList<LayerConfig> layers;
506 
507  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
508  {
509  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer && !mCurrentLayer )
510  {
511  msg += QLatin1String( "no current layer!" );
512  return msg;
513  }
514 
515  layers << LayerConfig( mCurrentLayer, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
516  }
517  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
518  {
519  Q_FOREACH ( QgsMapLayer *layer, mMapSettings.layers() )
520  {
521  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
522  layers << LayerConfig( vl, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
523  }
524  }
525  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
526  {
527  layers = mLayers;
528  }
529 
530  Q_FOREACH ( const LayerConfig &layer, layers )
531  {
532  msg += QString( "layer : %1\n"
533  "config: %2 tolerance %3 %4\n" )
534  .arg( layer.layer->name() )
535  .arg( layer.type ).arg( layer.tolerance ).arg( layer.unit );
536 
537  if ( mStrategy == IndexAlwaysFull || mStrategy == IndexHybrid || mStrategy == IndexExtent )
538  {
539  if ( QgsPointLocator *loc = locatorForLayer( layer.layer ) )
540  {
541  QString extentStr, cachedGeoms, limit( QStringLiteral( "no max area" ) );
542  if ( const QgsRectangle *r = loc->extent() )
543  {
544  extentStr = QStringLiteral( " extent %1" ).arg( r->toString() );
545  }
546  else
547  extentStr = QStringLiteral( "full extent" );
548  if ( loc->hasIndex() )
549  cachedGeoms = QStringLiteral( "%1 feats" ).arg( loc->cachedGeometryCount() );
550  else
551  cachedGeoms = QStringLiteral( "not initialized" );
552  if ( mStrategy == IndexHybrid )
553  {
554  if ( mHybridMaxAreaPerLayer.contains( layer.layer->id() ) )
555  {
556  double maxArea = mStrategy == IndexHybrid ? mHybridMaxAreaPerLayer[layer.layer->id()] : -1;
557  if ( maxArea != -1 )
558  limit = QStringLiteral( "max area %1" ).arg( maxArea );
559  }
560  else
561  limit = QStringLiteral( "not evaluated" );
562  }
563  msg += QStringLiteral( "index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
564  }
565  else
566  msg += QStringLiteral( "index : ???\n" ); // should not happen
567  }
568  else
569  msg += QLatin1String( "index : NO\n" );
570  msg += QLatin1String( "-\n" );
571  }
572 
573  return msg;
574 }
575 
576 QgsCoordinateReferenceSystem QgsSnappingUtils::destinationCrs() const
577 {
578  return mMapSettings.destinationCrs();
579 }
580 
581 void QgsSnappingUtils::onIndividualLayerSettingsChanged( const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
582 {
583  mLayers.clear();
584 
585  QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
586 
587  for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
588  {
589  if ( i->enabled() )
590  {
591  mLayers.append( LayerConfig( i.key(), _snappingTypeToPointLocatorType( i->type() ), i->tolerance(), i->units() ) );
592  }
593  }
594 }
void setEnabled(bool enabled)
enables the snapping
The class defines interface for querying point location:
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:335
A rectangle specified with double values.
Definition: qgsrectangle.h:40
Base class for all map layer types.
Definition: qgsmaplayer.h:61
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
SnappingType type() const
Returns 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
Returns the mode (all layers, active layer, per layer settings)
void setCurrentLayer(QgsVectorLayer *layer)
Sets 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
Returns 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.
Definition: qgsrectangle.h:234
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:104
QgsTolerance::UnitType units() const
Returns 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
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
Interface that allows rejection of some matches in intersection queries (e.g.
void setEnableSnappingForInvisibleFeature(bool enable)
Set if invisible features must be snapped or not.
bool intersectionSnapping() const
Returns 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.h:169
QgsMultiPolylineXY asMultiPolyline() const
Returns contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
For all layers build index of full extent. Uses more memory, but queries are faster.
QgsRectangle extent() const
Returns 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()
Gets 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.
void setRenderContext(const QgsRenderContext *context)
Configure render context - if not null, it will use to index only visible feature.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
QList< QgsSnappingUtils::LayerConfig > layers() const
Query layers used for snapping.
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:85
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
Gets 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
Returns 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:42
Contains information about the context of a rendering operation.
QgsPointLocator * locatorForLayer(QgsVectorLayer *vl)
Gets 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.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
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:65
QgsPolylineXY asPolyline() const
Returns contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
double tolerance() const
Returns 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
Returns 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.
QgsSnappingUtils(QObject *parent=nullptr, bool enableSnappingForInvisibleFeature=true)
Constructor for QgsSnappingUtils.
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:208
QgsPointLocator::Match snapToCurrentLayer(QPoint point, QgsPointLocator::Types type, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to current layer.