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