QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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(), nullptr );
45  connect( vlpl, &QgsPointLocator::initFinished, this, &QgsSnappingUtils::onInitFinished );
46  mLocators.insert( vl, vlpl );
47  }
48  return mLocators.value( vl );
49 }
50 
52 {
53  qDeleteAll( mLocators );
54  mLocators.clear();
55 
56  qDeleteAll( mTemporaryLocators );
57  mTemporaryLocators.clear();
58 }
59 
60 
61 QgsPointLocator *QgsSnappingUtils::locatorForLayerUsingStrategy( QgsVectorLayer *vl, const QgsPointXY &pointMap, double tolerance )
62 {
63  if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
64  return nullptr;
65 
66  QgsRectangle aoi( pointMap.x() - tolerance, pointMap.y() - tolerance,
67  pointMap.x() + tolerance, pointMap.y() + tolerance );
68 
69  QgsPointLocator *loc = locatorForLayer( vl );
70 
71  if ( loc->isIndexing() || isIndexPrepared( loc, aoi ) )
72  return loc;
73  else
74  return temporaryLocatorForLayer( vl, pointMap, tolerance );
75 }
76 
77 QgsPointLocator *QgsSnappingUtils::temporaryLocatorForLayer( QgsVectorLayer *vl, const QgsPointXY &pointMap, double tolerance )
78 {
79  if ( mTemporaryLocators.contains( vl ) )
80  delete mTemporaryLocators.take( vl );
81 
82  QgsRectangle rect( pointMap.x() - tolerance, pointMap.y() - tolerance,
83  pointMap.x() + tolerance, pointMap.y() + tolerance );
84 
85  QgsPointLocator *vlpl = new QgsPointLocator( vl, destinationCrs(), mMapSettings.transformContext(), &rect );
86  connect( vlpl, &QgsPointLocator::initFinished, this, &QgsSnappingUtils::onInitFinished );
87 
88  mTemporaryLocators.insert( vl, vlpl );
89  return mTemporaryLocators.value( vl );
90 }
91 
92 bool QgsSnappingUtils::isIndexPrepared( QgsPointLocator *loc, const QgsRectangle &areaOfInterest )
93 {
94  if ( mStrategy == IndexAlwaysFull && loc->hasIndex() )
95  return true;
96 
97  if ( mStrategy == IndexExtent && loc->hasIndex() && loc->extent()->intersects( areaOfInterest ) )
98  return true;
99 
100  QgsRectangle aoi( areaOfInterest );
101  aoi.scale( 0.999 );
102  return mStrategy == IndexHybrid && loc->hasIndex() && ( !loc->extent() || loc->extent()->contains( aoi ) ); // the index - even if it exists - is not suitable
103 }
104 
105 static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY &pt, const QgsPointLocator::MatchList &segments )
106 {
107  if ( segments.isEmpty() )
108  return QgsPointLocator::Match();
109 
110  QSet<QgsPointXY> endpoints;
111 
112  // make a geometry
113  QVector<QgsGeometry> geoms;
114  const auto constSegments = segments;
115  for ( const QgsPointLocator::Match &m : constSegments )
116  {
117  if ( m.hasEdge() )
118  {
119  QgsPolylineXY pl( 2 );
120  m.edgePoints( pl[0], pl[1] );
121  geoms << QgsGeometry::fromPolylineXY( pl );
122  endpoints << pl[0] << pl[1];
123  }
124  }
125 
127 
128  // get intersection points
129  QList<QgsPointXY> newPoints;
130  if ( g.wkbType() == QgsWkbTypes::LineString )
131  {
132  const auto constAsPolyline = g.asPolyline();
133  for ( const QgsPointXY &p : constAsPolyline )
134  {
135  if ( !endpoints.contains( p ) )
136  newPoints << p;
137  }
138  }
140  {
141  const auto constAsMultiPolyline = g.asMultiPolyline();
142  for ( const QgsPolylineXY &pl : constAsMultiPolyline )
143  {
144  const auto constPl = pl;
145  for ( const QgsPointXY &p : constPl )
146  {
147  if ( !endpoints.contains( p ) )
148  newPoints << p;
149  }
150  }
151  }
152 
153  if ( newPoints.isEmpty() )
154  return QgsPointLocator::Match();
155 
156  // find the closest points
157  QgsPointXY minP;
158  double minSqrDist = 1e20; // "infinity"
159  const auto constNewPoints = newPoints;
160  for ( const QgsPointXY &p : constNewPoints )
161  {
162  double sqrDist = pt.sqrDist( p.x(), p.y() );
163  if ( sqrDist < minSqrDist )
164  {
165  minSqrDist = sqrDist;
166  minP = p;
167  }
168  }
169 
170  return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP );
171 }
172 
173 static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointLocator::Match &candidateMatch, double maxDistance )
174 {
175 
176  // is candidate match relevant?
177  if ( !candidateMatch.isValid() || candidateMatch.distance() > maxDistance )
178  return;
179 
180  // is candidate match actually better?
181  if ( bestMatch.isValid() && bestMatch.type() == candidateMatch.type() && bestMatch.distance() - 10e-6 < candidateMatch.distance() )
182  return;
183 
184  // prefer vertex matches over edge matches (even if they are closer)
185  if ( bestMatch.type() == QgsPointLocator::Vertex && candidateMatch.type() == QgsPointLocator::Edge )
186  return;
187 
188  bestMatch = candidateMatch; // the other match is better!
189 }
190 
191 static void _updateBestMatch( QgsPointLocator::Match &bestMatch, const QgsPointXY &pointMap, QgsPointLocator *loc, QgsPointLocator::Types type, double tolerance, QgsPointLocator::MatchFilter *filter, bool relaxed )
192 {
193  if ( type & QgsPointLocator::Vertex )
194  {
195  _replaceIfBetter( bestMatch, loc->nearestVertex( pointMap, tolerance, filter, relaxed ), tolerance );
196  }
197  if ( bestMatch.type() != QgsPointLocator::Vertex && ( type & QgsPointLocator::Edge ) )
198  {
199  _replaceIfBetter( bestMatch, loc->nearestEdge( pointMap, tolerance, filter, relaxed ), tolerance );
200  }
201  if ( bestMatch.type() != QgsPointLocator::Vertex && bestMatch.type() != QgsPointLocator::Edge && ( type & QgsPointLocator::Area ) )
202  {
203  // if edges were detected, set tolerance to 0 to only do pointInPolygon (and avoid redo nearestEdge)
204  if ( type & QgsPointLocator::Edge )
205  tolerance = 0;
206  _replaceIfBetter( bestMatch, loc->nearestArea( pointMap, tolerance, filter, relaxed ), tolerance );
207  }
208 }
209 
210 
211 static QgsPointLocator::Types _snappingTypeToPointLocatorType( QgsSnappingConfig::SnappingType type )
212 {
213  // watch out: vertex+segment vs segment are in different order in the two enums
214  switch ( type )
215  {
219  return QgsPointLocator::Types( QgsPointLocator::Vertex | QgsPointLocator::Edge );
221  return QgsPointLocator::Edge;
222  default:
224  }
225 }
226 
228 {
229  return snapToMap( mMapSettings.mapToPixel().toMapCoordinates( point ), filter, relaxed );
230 }
231 
232 inline QgsRectangle _areaOfInterest( const QgsPointXY &point, double tolerance )
233 {
234  return QgsRectangle( point.x() - tolerance, point.y() - tolerance,
235  point.x() + tolerance, point.y() + tolerance );
236 }
237 
239 {
240  if ( !mMapSettings.hasValidSettings() || !mSnappingConfig.enabled() )
241  {
242  return QgsPointLocator::Match();
243  }
244 
245  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
246  {
247  if ( !mCurrentLayer || mSnappingConfig.type() == 0 )
248  return QgsPointLocator::Match();
249 
250  // data from project
251  double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), mCurrentLayer, mMapSettings, mSnappingConfig.units() );
252  QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.type() );
253 
254  prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer, _areaOfInterest( pointMap, tolerance ) ), relaxed );
255 
256  // use ad-hoc locator
257  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
258  if ( !loc )
259  return QgsPointLocator::Match();
260 
261  QgsPointLocator::Match bestMatch;
262  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, relaxed );
263 
264  if ( mSnappingConfig.intersectionSnapping() )
265  {
266  QgsPointLocator *locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
267  if ( !locEdges )
268  return QgsPointLocator::Match();
269 
270  QgsPointLocator::MatchList edges = locEdges->edgesInRect( pointMap, tolerance );
271  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
272  }
273 
274  return bestMatch;
275  }
276  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
277  {
278  QList<LayerAndAreaOfInterest> layers;
279  for ( const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
280  {
281  double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit );
282  layers << qMakePair( layerConfig.layer, _areaOfInterest( pointMap, tolerance ) );
283  }
284  prepareIndex( layers, relaxed );
285 
286  QgsPointLocator::Match bestMatch;
287  QgsPointLocator::MatchList edges; // for snap on intersection
288  double maxSnapIntTolerance = 0;
289 
290  for ( const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
291  {
292  double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit );
293  if ( QgsPointLocator *loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
294  {
295  _updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter, relaxed );
296 
297  if ( mSnappingConfig.intersectionSnapping() )
298  {
299  edges << loc->edgesInRect( pointMap, tolerance );
300  maxSnapIntTolerance = std::max( maxSnapIntTolerance, tolerance );
301  }
302  }
303  }
304 
305  if ( mSnappingConfig.intersectionSnapping() )
306  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxSnapIntTolerance );
307 
308  return bestMatch;
309  }
310  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
311  {
312  // data from project
313  double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), nullptr, mMapSettings, mSnappingConfig.units() );
314  QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.type() );
315  QgsRectangle aoi = _areaOfInterest( pointMap, tolerance );
316 
317  QList<LayerAndAreaOfInterest> layers;
318  const auto constLayers = mMapSettings.layers();
319  for ( QgsMapLayer *layer : constLayers )
320  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
321  layers << qMakePair( vl, aoi );
322  prepareIndex( layers, relaxed );
323 
324  QgsPointLocator::MatchList edges; // for snap on intersection
325  QgsPointLocator::Match bestMatch;
326 
327  for ( const LayerAndAreaOfInterest &entry : qgis::as_const( layers ) )
328  {
329  QgsVectorLayer *vl = entry.first;
330  if ( QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance ) )
331  {
332  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, relaxed );
333 
334  if ( mSnappingConfig.intersectionSnapping() )
335  edges << loc->edgesInRect( pointMap, tolerance );
336  }
337  }
338 
339  if ( mSnappingConfig.intersectionSnapping() )
340  _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
341 
342  return bestMatch;
343  }
344 
345  return QgsPointLocator::Match();
346 }
347 
348 void QgsSnappingUtils::onInitFinished( bool ok )
349 {
350  QgsPointLocator *loc = static_cast<QgsPointLocator *>( sender() );
351 
352  // point locator init didn't work out - too many features!
353  // let's make the allowed area smaller for the next time
354  if ( !ok )
355  {
356  mHybridMaxAreaPerLayer[loc->layer()->id()] /= 4;
357  }
358 }
359 
360 void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers, bool relaxed )
361 {
362  // check if we need to build any index
363  QList<LayerAndAreaOfInterest> layersToIndex;
364  const auto constLayers = layers;
365  for ( const LayerAndAreaOfInterest &entry : constLayers )
366  {
367  QgsVectorLayer *vl = entry.first;
368 
369  if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
370  continue;
371 
372  QgsPointLocator *loc = locatorForLayer( vl );
373 
374  if ( !loc->isIndexing() && !isIndexPrepared( loc, entry.second ) )
375  layersToIndex << entry;
376  }
377  if ( !layersToIndex.isEmpty() )
378  {
379  // build indexes
380  QElapsedTimer t;
381  int i = 0;
382 
383  if ( !relaxed )
384  {
385  t.start();
386  prepareIndexStarting( layersToIndex.count() );
387  }
388 
389  for ( const LayerAndAreaOfInterest &entry : layersToIndex )
390  {
391  QgsVectorLayer *vl = entry.first;
392  QgsPointLocator *loc = locatorForLayer( vl );
393 
394  if ( loc->isIndexing() && !relaxed )
395  {
397  }
398 
399 
400  if ( !mEnableSnappingForInvisibleFeature )
401  {
403  loc->setRenderContext( &ctx );
404  }
405 
406  if ( mStrategy == IndexExtent )
407  {
408  QgsRectangle rect( mMapSettings.visibleExtent() );
409  loc->setExtent( &rect );
410  loc->init( -1, relaxed );
411  }
412  else if ( mStrategy == IndexHybrid )
413  {
414  // first time the layer is used? - let's set an initial guess about indexing
415  if ( !mHybridMaxAreaPerLayer.contains( vl->id() ) )
416  {
417  int totalFeatureCount = vl->featureCount();
418  if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
419  {
420  // index the whole layer
421  mHybridMaxAreaPerLayer[vl->id()] = -1;
422  }
423  else
424  {
425  // estimate for how big area it probably makes sense to build partial index to not exceed the limit
426  // (we may change the limit later)
427  QgsRectangle layerExtent = mMapSettings.layerExtentToOutputExtent( vl, vl->extent() );
428  double totalArea = layerExtent.width() * layerExtent.height();
429  mHybridMaxAreaPerLayer[vl->id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
430  }
431  }
432 
433  double indexReasonableArea = mHybridMaxAreaPerLayer[vl->id()];
434  if ( indexReasonableArea == -1 )
435  {
436  // we can safely index the whole layer
437  loc->init( -1, relaxed );
438  }
439  else
440  {
441  // use area as big as we think may fit into our limit
442  QgsPointXY c = entry.second.center();
443  double halfSide = std::sqrt( indexReasonableArea ) / 2;
444  QgsRectangle rect( c.x() - halfSide, c.y() - halfSide,
445  c.x() + halfSide, c.y() + halfSide );
446  loc->setExtent( &rect );
447 
448  // see if it's possible build index for this area
449  loc->init( mHybridPerLayerFeatureLimit, relaxed );
450  }
451 
452  }
453  else // full index strategy
454  loc->init( relaxed );
455 
456  if ( !relaxed )
457  prepareIndexProgress( ++i );
458  }
459 
460  if ( !relaxed )
461  {
462  QgsDebugMsg( QStringLiteral( "Prepare index total: %1 ms" ).arg( t.elapsed() ) );
463  }
464  }
465 }
466 
468 {
469  return mSnappingConfig;
470 }
471 
473 {
474  mEnableSnappingForInvisibleFeature = enable;
475 }
476 
478 {
479  if ( mSnappingConfig == config )
480  return;
481 
482  if ( mSnappingConfig.individualLayerSettings() != config.individualLayerSettings() )
483  onIndividualLayerSettingsChanged( config.individualLayerSettings() );
484 
485  mSnappingConfig = config;
486 
487  emit configChanged( mSnappingConfig );
488 }
489 
491 {
492  mSnappingConfig.setEnabled( !mSnappingConfig.enabled() );
493  emit configChanged( mSnappingConfig );
494 }
495 
497 {
498  if ( !mCurrentLayer )
499  return QgsPointLocator::Match();
500 
501  QgsPointXY pointMap = mMapSettings.mapToPixel().toMapCoordinates( point );
502  double tolerance = QgsTolerance::vertexSearchRadius( mMapSettings );
503 
504  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
505  if ( !loc )
506  return QgsPointLocator::Match();
507 
508  QgsPointLocator::Match bestMatch;
509  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, false );
510  return bestMatch;
511 }
512 
514 {
515  QString oldDestCRS = mMapSettings.destinationCrs().authid();
516  QString newDestCRS = settings.destinationCrs().authid();
517  mMapSettings = settings;
518 
519  if ( newDestCRS != oldDestCRS )
521 }
522 
524 {
525  mCurrentLayer = layer;
526 }
527 
529 {
530  QString msg = QStringLiteral( "--- SNAPPING UTILS DUMP ---\n" );
531 
532  if ( !mMapSettings.hasValidSettings() )
533  {
534  msg += QLatin1String( "invalid map settings!" );
535  return msg;
536  }
537 
538  QList<LayerConfig> layers;
539 
540  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
541  {
542  if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer && !mCurrentLayer )
543  {
544  msg += QLatin1String( "no current layer!" );
545  return msg;
546  }
547 
548  layers << LayerConfig( mCurrentLayer, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
549  }
550  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
551  {
552  const auto constLayers = mMapSettings.layers();
553  for ( QgsMapLayer *layer : constLayers )
554  {
555  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
556  layers << LayerConfig( vl, _snappingTypeToPointLocatorType( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
557  }
558  }
559  else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
560  {
561  layers = mLayers;
562  }
563 
564  const auto constLayers = layers;
565  for ( const LayerConfig &layer : constLayers )
566  {
567  msg += QString( "layer : %1\n"
568  "config: %2 tolerance %3 %4\n" )
569  .arg( layer.layer->name() )
570  .arg( layer.type ).arg( layer.tolerance ).arg( layer.unit );
571 
572  if ( mStrategy == IndexAlwaysFull || mStrategy == IndexHybrid || mStrategy == IndexExtent )
573  {
574  if ( QgsPointLocator *loc = locatorForLayer( layer.layer ) )
575  {
576  QString extentStr, cachedGeoms, limit( QStringLiteral( "no max area" ) );
577  if ( const QgsRectangle *r = loc->extent() )
578  {
579  extentStr = QStringLiteral( " extent %1" ).arg( r->toString() );
580  }
581  else
582  extentStr = QStringLiteral( "full extent" );
583  if ( loc->hasIndex() )
584  cachedGeoms = QStringLiteral( "%1 feats" ).arg( loc->cachedGeometryCount() );
585  else
586  cachedGeoms = QStringLiteral( "not initialized" );
587  if ( mStrategy == IndexHybrid )
588  {
589  if ( mHybridMaxAreaPerLayer.contains( layer.layer->id() ) )
590  {
591  double maxArea = mStrategy == IndexHybrid ? mHybridMaxAreaPerLayer[layer.layer->id()] : -1;
592  if ( maxArea != -1 )
593  limit = QStringLiteral( "max area %1" ).arg( maxArea );
594  }
595  else
596  limit = QStringLiteral( "not evaluated" );
597  }
598  msg += QStringLiteral( "index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
599  }
600  else
601  msg += QStringLiteral( "index : ???\n" ); // should not happen
602  }
603  else
604  msg += QLatin1String( "index : NO\n" );
605  msg += QLatin1String( "-\n" );
606  }
607 
608  return msg;
609 }
610 
611 QgsCoordinateReferenceSystem QgsSnappingUtils::destinationCrs() const
612 {
613  return mMapSettings.destinationCrs();
614 }
615 
616 void QgsSnappingUtils::onIndividualLayerSettingsChanged( const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
617 {
618  mLayers.clear();
619 
620  QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
621 
622  for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
623  {
624  if ( i->enabled() )
625  {
626  mLayers.append( LayerConfig( i.key(), _snappingTypeToPointLocatorType( i->type() ), i->tolerance(), i->units() ) );
627  }
628  }
629 }
void setEnabled(bool enabled)
enables the snapping
The class defines interface for querying point location:
void initFinished(bool ok)
Emitted whenever index has been built and initialization is finished.
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:79
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 with snapToMap. When index == count the indexing is complete...
Match nearestVertex(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest vertex to the specified point - up to distance specified by tolerance Optional filter ma...
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
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:235
Q_INVOKABLE 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:122
QgsTolerance::UnitType units() const
Returns the type of units.
On all vector layers.
Match nearestEdge(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest edge to the specified point - up to distance specified by tolerance Optional filter may ...
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 waitForIndexingFinished()
If the point locator has been initialized relaxedly and is currently indexing, this methods waits for...
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:175
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...
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
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
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:91
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.
bool isIndexing() const
Returns true if the point locator is currently indexing the data.
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
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find edges within a specified recangle Optional filter may discard unwanted matches.
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 with snapToMap - can be overridden and e.g. progress dialog can be prov...
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:50
Contains information about the context of a rendering operation.
QgsPointLocator * locatorForLayer(QgsVectorLayer *vl)
Gets a point locator for the given layer.
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.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
bool init(int maxFeaturesToIndex=-1, bool relaxed=false)
Prepare the index for queries.
This class represents a coordinate reference system (CRS).
Match nearestArea(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest area to the specified point - up to distance specified by tolerance Optional filter may ...
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.
This is a container for configuration of the snapping of the project.
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)
QgsVectorLayer * layer() const
Gets associated layer.
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.