QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  if ( mStrategy == IndexExtent && loc->hasIndex() && loc->extent()->intersects( areaOfInterest ) )
93  return true;
94 
95  QgsRectangle aoi( areaOfInterest );
96  aoi.scale( 0.999 );
97  return mStrategy == IndexHybrid && loc->hasIndex() && ( !loc->extent() || loc->extent()->contains( aoi ) ); // the index - even if it exists - is not suitable
98 }
99 
100 static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY &pt, const QgsPointLocator::MatchList &segments )
101 {
102  if ( segments.isEmpty() )
103  return QgsPointLocator::Match();
104 
105  QSet<QgsPointXY> endpoints;
106 
107  // make a geometry
108  QVector<QgsGeometry> geoms;
109  const auto constSegments = segments;
110  for ( const QgsPointLocator::Match &m : constSegments )
111  {
112  if ( m.hasEdge() )
113  {
114  QgsPolylineXY pl( 2 );
115  m.edgePoints( pl[0], pl[1] );
116  geoms << QgsGeometry::fromPolylineXY( pl );
117  endpoints << pl[0] << pl[1];
118  }
119  }
120 
122 
123  // get intersection points
124  QList<QgsPointXY> newPoints;
125  if ( g.wkbType() == QgsWkbTypes::LineString )
126  {
127  const auto constAsPolyline = g.asPolyline();
128  for ( const QgsPointXY &p : constAsPolyline )
129  {
130  if ( !endpoints.contains( p ) )
131  newPoints << p;
132  }
133  }
135  {
136  const auto constAsMultiPolyline = g.asMultiPolyline();
137  for ( const QgsPolylineXY &pl : constAsMultiPolyline )
138  {
139  const auto constPl = pl;
140  for ( const QgsPointXY &p : constPl )
141  {
142  if ( !endpoints.contains( p ) )
143  newPoints << p;
144  }
145  }
146  }
147 
148  if ( newPoints.isEmpty() )
149  return QgsPointLocator::Match();
150 
151  // find the closest points
152  QgsPointXY minP;
153  double minSqrDist = 1e20; // "infinity"
154  const auto constNewPoints = newPoints;
155  for ( const QgsPointXY &p : constNewPoints )
156  {
157  double sqrDist = pt.sqrDist( p.x(), p.y() );
158  if ( sqrDist < minSqrDist )
159  {
160  minSqrDist = sqrDist;
161  minP = p;
162  }
163  }
164 
165  return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP );
166 }
167 
168 static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointLocator::Match &candidateMatch, double maxDistance )
169 {
170 
171  // is candidate match relevant?
172  if ( !candidateMatch.isValid() || candidateMatch.distance() > maxDistance )
173  return;
174 
175  // is candidate match actually better?
176  if ( bestMatch.isValid() && bestMatch.type() == candidateMatch.type() && bestMatch.distance() - 10e-6 < candidateMatch.distance() )
177  return;
178 
179  // prefer vertex matches over edge matches (even if they are closer)
180  if ( bestMatch.type() == QgsPointLocator::Vertex && candidateMatch.type() == QgsPointLocator::Edge )
181  return;
182 
183  bestMatch = candidateMatch; // the other match is better!
184 }
185 
186 static void _updateBestMatch( QgsPointLocator::Match &bestMatch, const QgsPointXY &pointMap, QgsPointLocator *loc, QgsPointLocator::Types type, double tolerance, QgsPointLocator::MatchFilter *filter )
187 {
188  if ( type & QgsPointLocator::Vertex )
189  {
190  _replaceIfBetter( bestMatch, loc->nearestVertex( pointMap, tolerance, filter ), tolerance );
191  }
192  if ( bestMatch.type() != QgsPointLocator::Vertex && ( type & QgsPointLocator::Edge ) )
193  {
194  _replaceIfBetter( bestMatch, loc->nearestEdge( pointMap, tolerance, filter ), tolerance );
195  }
196  if ( bestMatch.type() != QgsPointLocator::Vertex && bestMatch.type() != QgsPointLocator::Edge && ( type & QgsPointLocator::Area ) )
197  {
198  // if edges were detected, set tolerance to 0 to only do pointInPolygon (and avoid redo nearestEdge)
199  if ( type & QgsPointLocator::Edge )
200  tolerance = 0;
201  _replaceIfBetter( bestMatch, loc->nearestArea( pointMap, tolerance, filter ), tolerance );
202  }
203 }
204 
205 
206 static QgsPointLocator::Types _snappingTypeToPointLocatorType( QgsSnappingConfig::SnappingType type )
207 {
208  // watch out: vertex+segment vs segment are in different order in the two enums
209  switch ( type )
210  {
214  return QgsPointLocator::Types( QgsPointLocator::Vertex | QgsPointLocator::Edge );
216  return QgsPointLocator::Edge;
217  default:
219  }
220 }
221 
222 
224 {
225  return snapToMap( mMapSettings.mapToPixel().toMapCoordinates( point ), filter );
226 }
227 
228 inline QgsRectangle _areaOfInterest( const QgsPointXY &point, double tolerance )
229 {
230  return QgsRectangle( point.x() - tolerance, point.y() - tolerance,
231  point.x() + tolerance, point.y() + tolerance );
232 }
233 
235 {
236  if ( !mMapSettings.hasValidSettings() || !mSnappingConfig.enabled() )
237  {
238  return QgsPointLocator::Match();
239  }
240 
241  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
242  {
243  if ( !mCurrentLayer || mSnappingConfig.type() == 0 )
244  return QgsPointLocator::Match();
245 
246  // data from project
247  double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), mCurrentLayer, mMapSettings, mSnappingConfig.units() );
248  QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.type() );
249 
250  prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer, _areaOfInterest( pointMap, tolerance ) ) );
251 
252  // use ad-hoc locator
253  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
254  if ( !loc )
255  return QgsPointLocator::Match();
256 
257  QgsPointLocator::Match bestMatch;
258  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
259 
260  if ( mSnappingConfig.intersectionSnapping() )
261  {
262  QgsPointLocator *locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
263  QgsPointLocator::MatchList edges = locEdges->edgesInRect( pointMap, tolerance );
264  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
265  }
266 
267  return bestMatch;
268  }
269  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
270  {
271  QList<LayerAndAreaOfInterest> layers;
272  for ( const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
273  {
274  double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit );
275  layers << qMakePair( layerConfig.layer, _areaOfInterest( pointMap, tolerance ) );
276  }
277  prepareIndex( layers );
278 
279  QgsPointLocator::Match bestMatch;
280  QgsPointLocator::MatchList edges; // for snap on intersection
281  double maxSnapIntTolerance = 0;
282 
283  for ( const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
284  {
285  double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit );
286  if ( QgsPointLocator *loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
287  {
288  _updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter );
289 
290  if ( mSnappingConfig.intersectionSnapping() )
291  {
292  edges << loc->edgesInRect( pointMap, tolerance );
293  maxSnapIntTolerance = std::max( maxSnapIntTolerance, tolerance );
294  }
295  }
296  }
297 
298  if ( mSnappingConfig.intersectionSnapping() )
299  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxSnapIntTolerance );
300 
301  return bestMatch;
302  }
303  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
304  {
305  // data from project
306  double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), nullptr, mMapSettings, mSnappingConfig.units() );
307  QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.type() );
308  QgsRectangle aoi = _areaOfInterest( pointMap, tolerance );
309 
310  QList<LayerAndAreaOfInterest> layers;
311  const auto constLayers = mMapSettings.layers();
312  for ( QgsMapLayer *layer : constLayers )
313  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
314  layers << qMakePair( vl, aoi );
315  prepareIndex( layers );
316 
317  QgsPointLocator::MatchList edges; // for snap on intersection
318  QgsPointLocator::Match bestMatch;
319 
320  for ( const LayerAndAreaOfInterest &entry : qgis::as_const( layers ) )
321  {
322  QgsVectorLayer *vl = entry.first;
323  if ( QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance ) )
324  {
325  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
326 
327  if ( mSnappingConfig.intersectionSnapping() )
328  edges << loc->edgesInRect( pointMap, tolerance );
329  }
330  }
331 
332  if ( mSnappingConfig.intersectionSnapping() )
333  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
334 
335  return bestMatch;
336  }
337 
338  return QgsPointLocator::Match();
339 }
340 
341 void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers )
342 {
343  if ( mIsIndexing )
344  return;
345  mIsIndexing = true;
346 
347  // check if we need to build any index
348  QList<LayerAndAreaOfInterest> layersToIndex;
349  const auto constLayers = layers;
350  for ( const LayerAndAreaOfInterest &entry : constLayers )
351  {
352  QgsVectorLayer *vl = entry.first;
353 
354  if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
355  continue;
356 
357  if ( !isIndexPrepared( vl, entry.second ) )
358  layersToIndex << entry;
359  }
360  if ( !layersToIndex.isEmpty() )
361  {
362  // build indexes
363  QTime t;
364  t.start();
365  int i = 0;
366  prepareIndexStarting( layersToIndex.count() );
367  const auto constLayersToIndex = layersToIndex;
368  for ( const LayerAndAreaOfInterest &entry : constLayersToIndex )
369  {
370  QgsVectorLayer *vl = entry.first;
371  QTime tt;
372  tt.start();
373 
374  QgsPointLocator *loc = locatorForLayer( vl );
375 
376  if ( !mEnableSnappingForInvisibleFeature )
377  {
379  loc->setRenderContext( &ctx );
380  }
381 
382  if ( mStrategy == IndexExtent )
383  {
384  QgsRectangle rect( mMapSettings.visibleExtent() );
385  loc->setExtent( &rect );
386  loc->init();
387  }
388  else if ( mStrategy == IndexHybrid )
389  {
390  // first time the layer is used? - let's set an initial guess about indexing
391  if ( !mHybridMaxAreaPerLayer.contains( vl->id() ) )
392  {
393  int totalFeatureCount = vl->featureCount();
394  if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
395  {
396  // index the whole layer
397  mHybridMaxAreaPerLayer[vl->id()] = -1;
398  }
399  else
400  {
401  // estimate for how big area it probably makes sense to build partial index to not exceed the limit
402  // (we may change the limit later)
403  QgsRectangle layerExtent = mMapSettings.layerExtentToOutputExtent( vl, vl->extent() );
404  double totalArea = layerExtent.width() * layerExtent.height();
405  mHybridMaxAreaPerLayer[vl->id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
406  }
407  }
408 
409  double indexReasonableArea = mHybridMaxAreaPerLayer[vl->id()];
410  if ( indexReasonableArea == -1 )
411  {
412  // we can safely index the whole layer
413  loc->init();
414  }
415  else
416  {
417  // use area as big as we think may fit into our limit
418  QgsPointXY c = entry.second.center();
419  double halfSide = std::sqrt( indexReasonableArea ) / 2;
420  QgsRectangle rect( c.x() - halfSide, c.y() - halfSide,
421  c.x() + halfSide, c.y() + halfSide );
422  loc->setExtent( &rect );
423 
424  // see if it's possible build index for this area
425  if ( !loc->init( mHybridPerLayerFeatureLimit ) )
426  {
427  // hmm that didn't work out - too many features!
428  // let's make the allowed area smaller for the next time
429  mHybridMaxAreaPerLayer[vl->id()] /= 4;
430  }
431  }
432 
433  }
434  else // full index strategy
435  loc->init();
436 
437  QgsDebugMsg( QStringLiteral( "Index init: %1 ms (%2)" ).arg( tt.elapsed() ).arg( vl->id() ) );
438  prepareIndexProgress( ++i );
439  }
440  QgsDebugMsg( QStringLiteral( "Prepare index total: %1 ms" ).arg( t.elapsed() ) );
441  }
442  mIsIndexing = false;
443 }
444 
446 {
447  return mSnappingConfig;
448 }
449 
451 {
452  mEnableSnappingForInvisibleFeature = enable;
453 }
454 
456 {
457  if ( mSnappingConfig == config )
458  return;
459 
460  if ( mSnappingConfig.individualLayerSettings() != config.individualLayerSettings() )
461  onIndividualLayerSettingsChanged( config.individualLayerSettings() );
462 
463  mSnappingConfig = config;
464 
465  emit configChanged( mSnappingConfig );
466 }
467 
469 {
470  mSnappingConfig.setEnabled( !mSnappingConfig.enabled() );
471  emit configChanged( mSnappingConfig );
472 }
473 
475 {
476  if ( !mCurrentLayer )
477  return QgsPointLocator::Match();
478 
479  QgsPointXY pointMap = mMapSettings.mapToPixel().toMapCoordinates( point );
480  double tolerance = QgsTolerance::vertexSearchRadius( mMapSettings );
481 
482  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
483  if ( !loc )
484  return QgsPointLocator::Match();
485 
486  QgsPointLocator::Match bestMatch;
487  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
488  return bestMatch;
489 }
490 
492 {
493  QString oldDestCRS = mMapSettings.destinationCrs().authid();
494  QString newDestCRS = settings.destinationCrs().authid();
495  mMapSettings = settings;
496 
497  if ( newDestCRS != oldDestCRS )
499 }
500 
502 {
503  mCurrentLayer = layer;
504 }
505 
507 {
508  QString msg = QStringLiteral( "--- SNAPPING UTILS DUMP ---\n" );
509 
510  if ( !mMapSettings.hasValidSettings() )
511  {
512  msg += QLatin1String( "invalid map settings!" );
513  return msg;
514  }
515 
516  QList<LayerConfig> layers;
517 
518  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
519  {
520  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer && !mCurrentLayer )
521  {
522  msg += QLatin1String( "no current layer!" );
523  return msg;
524  }
525 
526  layers << LayerConfig( mCurrentLayer, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
527  }
528  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
529  {
530  const auto constLayers = mMapSettings.layers();
531  for ( QgsMapLayer *layer : constLayers )
532  {
533  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
534  layers << LayerConfig( vl, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
535  }
536  }
537  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
538  {
539  layers = mLayers;
540  }
541 
542  const auto constLayers = layers;
543  for ( const LayerConfig &layer : constLayers )
544  {
545  msg += QString( "layer : %1\n"
546  "config: %2 tolerance %3 %4\n" )
547  .arg( layer.layer->name() )
548  .arg( layer.type ).arg( layer.tolerance ).arg( layer.unit );
549 
550  if ( mStrategy == IndexAlwaysFull || mStrategy == IndexHybrid || mStrategy == IndexExtent )
551  {
552  if ( QgsPointLocator *loc = locatorForLayer( layer.layer ) )
553  {
554  QString extentStr, cachedGeoms, limit( QStringLiteral( "no max area" ) );
555  if ( const QgsRectangle *r = loc->extent() )
556  {
557  extentStr = QStringLiteral( " extent %1" ).arg( r->toString() );
558  }
559  else
560  extentStr = QStringLiteral( "full extent" );
561  if ( loc->hasIndex() )
562  cachedGeoms = QStringLiteral( "%1 feats" ).arg( loc->cachedGeometryCount() );
563  else
564  cachedGeoms = QStringLiteral( "not initialized" );
565  if ( mStrategy == IndexHybrid )
566  {
567  if ( mHybridMaxAreaPerLayer.contains( layer.layer->id() ) )
568  {
569  double maxArea = mStrategy == IndexHybrid ? mHybridMaxAreaPerLayer[layer.layer->id()] : -1;
570  if ( maxArea != -1 )
571  limit = QStringLiteral( "max area %1" ).arg( maxArea );
572  }
573  else
574  limit = QStringLiteral( "not evaluated" );
575  }
576  msg += QStringLiteral( "index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
577  }
578  else
579  msg += QStringLiteral( "index : ???\n" ); // should not happen
580  }
581  else
582  msg += QLatin1String( "index : NO\n" );
583  msg += QLatin1String( "-\n" );
584  }
585 
586  return msg;
587 }
588 
589 QgsCoordinateReferenceSystem QgsSnappingUtils::destinationCrs() const
590 {
591  return mMapSettings.destinationCrs();
592 }
593 
594 void QgsSnappingUtils::onIndividualLayerSettingsChanged( const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
595 {
596  mLayers.clear();
597 
598  QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
599 
600  for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
601  {
602  if ( i->enabled() )
603  {
604  mLayers.append( LayerConfig( i.key(), _snappingTypeToPointLocatorType( i->type() ), i->tolerance(), i->units() ) );
605  }
606  }
607 }
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:342
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:78
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:235
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
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.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
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 the contents of the geometry as a multi-linestring.
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.
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...
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 nullptr, it will use to index only visible feature.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QList< QgsSnappingUtils::LayerConfig > layers() const
Query layers used for snapping.
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
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:89
On a per layer configuration basis.
void setExtent(const QgsRectangle *extent)
Configure extent - if not nullptr, it will index only that area.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
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
const QgsRectangle * extent() const
Gets extent of the area point locator covers - if nullptr then it caches the whole layer...
QgsSnappingConfig config() const
The snapping configuration controls the behavior of this object.
QgsRectangle extent() const FINAL
Returns the extent of the layer.
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:49
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.
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...
Snapped to an edge.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units...
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
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...
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
Definition: qgsrectangle.h:328
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
Transform device coordinates to map (world) coordinates.
QString authid() const
Returns the authority identifier for the CRS.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
QgsPointLocator::Match snapToCurrentLayer(QPoint point, QgsPointLocator::Types type, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to current layer.