QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgspointlocator.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspointlocator.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 "qgspointlocator.h"
17 
18 #include "qgsfeatureiterator.h"
19 #include "qgsgeometry.h"
20 #include "qgsvectorlayer.h"
21 #include "qgswkbptr.h"
22 #include "qgis.h"
23 #include "qgslogger.h"
24 #include "qgsrenderer.h"
25 #include "qgsapplication.h"
28 #include "qgslinestring.h"
30 #include <spatialindex/SpatialIndex.h>
31 
32 #include <QLinkedListIterator>
33 #include <QtConcurrent>
34 
35 using namespace SpatialIndex;
36 
37 
38 
39 static SpatialIndex::Point point2point( const QgsPointXY &point )
40 {
41  double plow[2] = { point.x(), point.y() };
42  return Point( plow, 2 );
43 }
44 
45 
46 static SpatialIndex::Region rect2region( const QgsRectangle &rect )
47 {
48  double pLow[2] = { rect.xMinimum(), rect.yMinimum() };
49  double pHigh[2] = { rect.xMaximum(), rect.yMaximum() };
50  return SpatialIndex::Region( pLow, pHigh, 2 );
51 }
52 
53 
54 // Ahh.... another magic number. Taken from QgsVectorLayer::snapToGeometry() call to closestSegmentWithContext().
55 // The default epsilon used for sqrDistToSegment (1e-8) is too high when working with lat/lon coordinates
56 // I still do not fully understand why the sqrDistToSegment() code uses epsilon and if the square distance
57 // is lower than epsilon it will have a special logic...
58 static const double POINT_LOC_EPSILON = 1e-12;
59 
61 
62 
68 class QgsPointLocator_Stream : public IDataStream
69 {
70  public:
71  explicit QgsPointLocator_Stream( const QLinkedList<RTree::Data *> &dataList )
72  : mDataList( dataList )
73  , mIt( mDataList )
74  { }
75 
76  IData *getNext() override { return mIt.next(); }
77  bool hasNext() override { return mIt.hasNext(); }
78 
79  uint32_t size() override { Q_ASSERT( false && "not available" ); return 0; }
80  void rewind() override { Q_ASSERT( false && "not available" ); }
81 
82  private:
83  QLinkedList<RTree::Data *> mDataList;
84  QLinkedListIterator<RTree::Data *> mIt;
85 };
86 
87 
89 
90 
96 class QgsPointLocator_VisitorNearestVertex : public IVisitor
97 {
98  public:
100  : mLocator( pl )
101  , mBest( m )
102  , mSrcPoint( srcPoint )
103  , mFilter( filter )
104  {}
105 
106  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
107  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
108 
109  void visitData( const IData &d ) override
110  {
111  QgsFeatureId id = d.getIdentifier();
112  QgsGeometry *geom = mLocator->mGeoms.value( id );
113  int vertexIndex, beforeVertex, afterVertex;
114  double sqrDist;
115 
116  QgsPointXY pt = geom->closestVertex( mSrcPoint, vertexIndex, beforeVertex, afterVertex, sqrDist );
117  if ( sqrDist < 0 )
118  return; // probably empty geometry
119 
120  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, vertexIndex );
121  // in range queries the filter may reject some matches
122  if ( mFilter && !mFilter->acceptMatch( m ) )
123  return;
124 
125  if ( !mBest.isValid() || m.distance() < mBest.distance() )
126  mBest = m;
127  }
128 
129  private:
130  QgsPointLocator *mLocator = nullptr;
131  QgsPointLocator::Match &mBest;
132  QgsPointXY mSrcPoint;
133  QgsPointLocator::MatchFilter *mFilter = nullptr;
134 };
135 
136 
137 
145 {
146  public:
147 
155  : mLocator( pl )
156  , mBest( m )
157  , mSrcPoint( srcPoint )
158  , mFilter( filter )
159  {}
160 
161  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
162  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
163 
164  void visitData( const IData &d ) override
165  {
166  QgsFeatureId id = d.getIdentifier();
167  QgsGeometry *geom = mLocator->mGeoms.value( id );
168 
169  QgsPointXY pt = geom->centroid().asPoint();
170 
171  QgsPointLocator::Match m( QgsPointLocator::Centroid, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( pt ) ), pt, -1 );
172  // in range queries the filter may reject some matches
173  if ( mFilter && !mFilter->acceptMatch( m ) )
174  return;
175 
176  if ( !mBest.isValid() || m.distance() < mBest.distance() )
177  mBest = m;
178 
179  }
180 
181  private:
182  QgsPointLocator *mLocator = nullptr;
183  QgsPointLocator::Match &mBest;
184  QgsPointXY mSrcPoint;
185  QgsPointLocator::MatchFilter *mFilter = nullptr;
186 };
187 
189 
197 {
198  public:
199 
207  : mLocator( pl )
208  , mBest( m )
209  , mSrcPoint( srcPoint )
210  , mFilter( filter )
211  {}
212 
213  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
214  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
215 
216  void visitData( const IData &d ) override
217  {
218  QgsFeatureId id = d.getIdentifier();
219  QgsGeometry *geom = mLocator->mGeoms.value( id );
220  QgsPointXY pt;
221  int afterVertex;
222  double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
223  if ( sqrDist < 0 )
224  return;
225 
226  QgsPointXY edgePoints[2];
227  edgePoints[0] = geom->vertexAt( afterVertex - 1 );
228  edgePoints[1] = geom->vertexAt( afterVertex );
229  pt = QgsPointXY( ( edgePoints[0].x() + edgePoints[1].x() ) / 2.0, ( edgePoints[0].y() + edgePoints[1].y() ) / 2.0 );
230 
231  QgsPointLocator::Match m( QgsPointLocator::MiddleOfSegment, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( pt ) ), pt, afterVertex - 1 );
232  // in range queries the filter may reject some matches
233  if ( mFilter && !mFilter->acceptMatch( m ) )
234  return;
235 
236  if ( !mBest.isValid() || m.distance() < mBest.distance() )
237  mBest = m;
238 
239  }
240 
241  private:
242  QgsPointLocator *mLocator = nullptr;
243  QgsPointLocator::Match &mBest;
244  QgsPointXY mSrcPoint;
245  QgsPointLocator::MatchFilter *mFilter = nullptr;
246 };
247 
249 
250 
256 class QgsPointLocator_VisitorNearestEdge : public IVisitor
257 {
258  public:
260  : mLocator( pl )
261  , mBest( m )
262  , mSrcPoint( srcPoint )
263  , mFilter( filter )
264  {}
265 
266  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
267  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
268 
269  void visitData( const IData &d ) override
270  {
271  QgsFeatureId id = d.getIdentifier();
272  QgsGeometry *geom = mLocator->mGeoms.value( id );
273  QgsPointXY pt;
274  int afterVertex;
275  double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
276  if ( sqrDist < 0 )
277  return;
278 
279  QgsPointXY edgePoints[2];
280  edgePoints[0] = geom->vertexAt( afterVertex - 1 );
281  edgePoints[1] = geom->vertexAt( afterVertex );
282  QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, afterVertex - 1, edgePoints );
283  // in range queries the filter may reject some matches
284  if ( mFilter && !mFilter->acceptMatch( m ) )
285  return;
286 
287  if ( !mBest.isValid() || m.distance() < mBest.distance() )
288  mBest = m;
289  }
290 
291  private:
292  QgsPointLocator *mLocator = nullptr;
293  QgsPointLocator::Match &mBest;
294  QgsPointXY mSrcPoint;
295  QgsPointLocator::MatchFilter *mFilter = nullptr;
296 };
297 
298 
300 
306 class QgsPointLocator_VisitorArea : public IVisitor
307 {
308  public:
311  : mLocator( pl )
312  , mList( list )
313  , mGeomPt( QgsGeometry::fromPointXY( origPt ) )
314  {}
315 
316  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
317  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
318 
319  void visitData( const IData &d ) override
320  {
321  QgsFeatureId id = d.getIdentifier();
322  QgsGeometry *g = mLocator->mGeoms.value( id );
323  if ( g->intersects( mGeomPt ) )
324  mList << QgsPointLocator::Match( QgsPointLocator::Area, mLocator->mLayer, id, 0, mGeomPt.asPoint() );
325  }
326  private:
327  QgsPointLocator *mLocator = nullptr;
329  QgsGeometry mGeomPt;
330 };
331 
332 
334 
335 // code adapted from
336 // http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
338 {
339  explicit _CohenSutherland( const QgsRectangle &rect ) : mRect( rect ) {}
340 
341  typedef int OutCode;
342 
343  static const int INSIDE = 0; // 0000
344  static const int LEFT = 1; // 0001
345  static const int RIGHT = 2; // 0010
346  static const int BOTTOM = 4; // 0100
347  static const int TOP = 8; // 1000
348 
350 
351  OutCode computeOutCode( double x, double y )
352  {
353  OutCode code = INSIDE; // initialized as being inside of clip window
354  if ( x < mRect.xMinimum() ) // to the left of clip window
355  code |= LEFT;
356  else if ( x > mRect.xMaximum() ) // to the right of clip window
357  code |= RIGHT;
358  if ( y < mRect.yMinimum() ) // below the clip window
359  code |= BOTTOM;
360  else if ( y > mRect.yMaximum() ) // above the clip window
361  code |= TOP;
362  return code;
363  }
364 
365  bool isSegmentInRect( double x0, double y0, double x1, double y1 )
366  {
367  // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
368  OutCode outcode0 = computeOutCode( x0, y0 );
369  OutCode outcode1 = computeOutCode( x1, y1 );
370  bool accept = false;
371 
372  while ( true )
373  {
374  if ( !( outcode0 | outcode1 ) )
375  {
376  // Bitwise OR is 0. Trivially accept and get out of loop
377  accept = true;
378  break;
379  }
380  else if ( outcode0 & outcode1 )
381  {
382  // Bitwise AND is not 0. Trivially reject and get out of loop
383  break;
384  }
385  else
386  {
387  // failed both tests, so calculate the line segment to clip
388  // from an outside point to an intersection with clip edge
389  double x, y;
390 
391  // At least one endpoint is outside the clip rectangle; pick it.
392  OutCode outcodeOut = outcode0 ? outcode0 : outcode1;
393 
394  // Now find the intersection point;
395  // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
396  if ( outcodeOut & TOP )
397  {
398  // point is above the clip rectangle
399  x = x0 + ( x1 - x0 ) * ( mRect.yMaximum() - y0 ) / ( y1 - y0 );
400  y = mRect.yMaximum();
401  }
402  else if ( outcodeOut & BOTTOM )
403  {
404  // point is below the clip rectangle
405  x = x0 + ( x1 - x0 ) * ( mRect.yMinimum() - y0 ) / ( y1 - y0 );
406  y = mRect.yMinimum();
407  }
408  else if ( outcodeOut & RIGHT )
409  {
410  // point is to the right of clip rectangle
411  y = y0 + ( y1 - y0 ) * ( mRect.xMaximum() - x0 ) / ( x1 - x0 );
412  x = mRect.xMaximum();
413  }
414  else if ( outcodeOut & LEFT )
415  {
416  // point is to the left of clip rectangle
417  y = y0 + ( y1 - y0 ) * ( mRect.xMinimum() - x0 ) / ( x1 - x0 );
418  x = mRect.xMinimum();
419  }
420  else
421  break;
422 
423  // Now we move outside point to intersection point to clip
424  // and get ready for next pass.
425  if ( outcodeOut == outcode0 )
426  {
427  x0 = x;
428  y0 = y;
429  outcode0 = computeOutCode( x0, y0 );
430  }
431  else
432  {
433  x1 = x;
434  y1 = y;
435  outcode1 = computeOutCode( x1, y1 );
436  }
437  }
438  }
439  return accept;
440  }
441 };
442 
443 
444 static QgsPointLocator::MatchList _geometrySegmentsInRect( QgsGeometry *geom, const QgsRectangle &rect, QgsVectorLayer *vl, QgsFeatureId fid )
445 {
446  // this code is stupidly based on QgsGeometry::closestSegmentWithContext
447  // we need iterator for segments...
448 
450 
451  // geom is converted to a MultiCurve
452  QgsGeometry straightGeom = geom->convertToType( QgsWkbTypes::LineGeometry, true );
453  // and convert to straight segemnt / converts curve to linestring
454  straightGeom.convertToStraightSegment();
455 
456  // so, you must have multilinestring
457  //
458  // Special case: Intersections cannot be done on an empty linestring like
459  // QgsGeometry(QgsLineString()) or QgsGeometry::fromWkt("LINESTRING EMPTY")
460  if ( straightGeom.isEmpty() || ( ( straightGeom.type() != QgsWkbTypes::LineGeometry ) && ( !straightGeom.isMultipart() ) ) )
461  return lst;
462 
463  _CohenSutherland cs( rect );
464 
465  int pointIndex = 0;
466  for ( auto part = straightGeom.const_parts_begin(); part != straightGeom.const_parts_end(); ++part )
467  {
468  // Checking for invalid linestrings
469  // A linestring should/(must?) have at least two points.
470  QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( *part );
471  Q_ASSERT( !curve->hasCurvedSegments() );
472  if ( curve->numPoints() < 2 )
473  continue;
474 
475  QgsAbstractGeometry::vertex_iterator it = ( *part )->vertices_begin();
476  QgsPointXY prevPoint( *it );
477  it++;
478  while ( it != ( *part )->vertices_end() )
479  {
480  QgsPointXY thisPoint( *it );
481  if ( cs.isSegmentInRect( prevPoint.x(), prevPoint.y(), thisPoint.x(), thisPoint.y() ) )
482  {
483  QgsPointXY edgePoints[2];
484  edgePoints[0] = prevPoint;
485  edgePoints[1] = thisPoint;
486  lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
487  }
488  prevPoint = QgsPointXY( *it );
489  it++;
490  pointIndex += 1;
491 
492  }
493  }
494  return lst;
495 }
496 
502 class QgsPointLocator_VisitorEdgesInRect : public IVisitor
503 {
504  public:
506  : mLocator( pl )
507  , mList( lst )
508  , mSrcRect( srcRect )
509  , mFilter( filter )
510  {}
511 
512  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
513  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
514 
515  void visitData( const IData &d ) override
516  {
517  QgsFeatureId id = d.getIdentifier();
518  QgsGeometry *geom = mLocator->mGeoms.value( id );
519 
520  const auto segmentsInRect {_geometrySegmentsInRect( geom, mSrcRect, mLocator->mLayer, id )};
521  for ( const QgsPointLocator::Match &m : segmentsInRect )
522  {
523  // in range queries the filter may reject some matches
524  if ( mFilter && !mFilter->acceptMatch( m ) )
525  continue;
526 
527  mList << m;
528  }
529  }
530 
531  private:
532  QgsPointLocator *mLocator = nullptr;
534  QgsRectangle mSrcRect;
535  QgsPointLocator::MatchFilter *mFilter = nullptr;
536 };
537 
539 
547 {
548  public:
551  : mLocator( pl )
552  , mList( lst )
553  , mSrcRect( srcRect )
554  , mFilter( filter )
555  {}
556 
557  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
558  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
559 
560  void visitData( const IData &d ) override
561  {
562  QgsFeatureId id = d.getIdentifier();
563  const QgsGeometry *geom = mLocator->mGeoms.value( id );
564 
565  for ( QgsAbstractGeometry::vertex_iterator it = geom->vertices_begin(); it != geom->vertices_end(); ++it )
566  {
567  if ( mSrcRect.contains( *it ) )
568  {
569  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, 0, *it, geom->vertexNrFromVertexId( it.vertexId() ) );
570 
571  // in range queries the filter may reject some matches
572  if ( mFilter && !mFilter->acceptMatch( m ) )
573  continue;
574 
575  mList << m;
576  }
577  }
578  }
579 
580  private:
581  QgsPointLocator *mLocator = nullptr;
583  QgsRectangle mSrcRect;
584  QgsPointLocator::MatchFilter *mFilter = nullptr;
585 };
586 
594 {
595  public:
598  : mLocator( pl )
599  , mList( lst )
600  , mSrcRect( srcRect )
601  , mFilter( filter )
602  {}
603 
604  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
605  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
606 
607  void visitData( const IData &d ) override
608  {
609  QgsFeatureId id = d.getIdentifier();
610  const QgsGeometry *geom = mLocator->mGeoms.value( id );
611  const QgsPointXY centroid = geom->centroid().asPoint();
612  if ( mSrcRect.contains( centroid ) )
613  {
614  QgsPointLocator::Match m( QgsPointLocator::Centroid, mLocator->mLayer, id, 0, centroid, -1 );
615 
616  // in range queries the filter may reject some matches
617  if ( !( mFilter && !mFilter->acceptMatch( m ) ) )
618  mList << m;
619  }
620  }
621 
622  private:
623  QgsPointLocator *mLocator = nullptr;
625  QgsRectangle mSrcRect;
626  QgsPointLocator::MatchFilter *mFilter = nullptr;
627 };
628 
636 {
637  public:
640  : mLocator( pl )
641  , mList( lst )
642  , mSrcRect( srcRect )
643  , mFilter( filter )
644  {}
645 
646  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
647  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
648 
649  void visitData( const IData &d ) override
650  {
651  QgsFeatureId id = d.getIdentifier();
652  const QgsGeometry *geom = mLocator->mGeoms.value( id );
653 
654  for ( QgsAbstractGeometry::const_part_iterator itPart = geom->const_parts_begin() ; itPart != geom->const_parts_end() ; ++itPart )
655  {
656  QgsAbstractGeometry::vertex_iterator it = ( *itPart )->vertices_begin();
657  QgsAbstractGeometry::vertex_iterator itPrevious = ( *itPart )->vertices_begin();
658  it++;
659  for ( ; it != geom->vertices_end(); ++it, ++itPrevious )
660  {
661  QgsPointXY pt( ( ( *itPrevious ).x() + ( *it ).x() ) / 2.0, ( ( *itPrevious ).y() + ( *it ).y() ) / 2.0 );
662  if ( mSrcRect.contains( pt ) )
663  {
664  QgsPointLocator::Match m( QgsPointLocator::MiddleOfSegment, mLocator->mLayer, id, 0, pt, geom->vertexNrFromVertexId( it.vertexId() ) );
665 
666  // in range queries the filter may reject some matches
667  if ( mFilter && !mFilter->acceptMatch( m ) )
668  continue;
669 
670  mList << m;
671  }
672  }
673  }
674  }
675 
676  private:
677  QgsPointLocator *mLocator = nullptr;
679  QgsRectangle mSrcRect;
680  QgsPointLocator::MatchFilter *mFilter = nullptr;
681 };
682 
684 #include <QStack>
685 
691 class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy
692 {
693  private:
694  QStack<id_type> ids;
695 
696  public:
697 
698  void getNextEntry( const IEntry &entry, id_type &nextEntry, bool &hasNext ) override
699  {
700  const INode *n = dynamic_cast<const INode *>( &entry );
701  if ( !n )
702  return;
703 
704  QgsDebugMsgLevel( QStringLiteral( "NODE: %1" ).arg( n->getIdentifier() ), 4 );
705  if ( n->getLevel() > 0 )
706  {
707  // inner nodes
708  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
709  {
710  QgsDebugMsgLevel( QStringLiteral( "- CH: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
711  ids.push( n->getChildIdentifier( cChild ) );
712  }
713  }
714  else
715  {
716  // leaves
717  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
718  {
719  QgsDebugMsgLevel( QStringLiteral( "- L: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
720  }
721  }
722 
723  if ( ! ids.empty() )
724  {
725  nextEntry = ids.back();
726  ids.pop();
727  hasNext = true;
728  }
729  else
730  hasNext = false;
731  }
732 };
733 
735 
736 
738  : mLayer( layer )
739 {
740  if ( destCRS.isValid() )
741  {
742  mTransform = QgsCoordinateTransform( layer->crs(), destCRS, transformContext );
743  }
744 
745  setExtent( extent );
746 
747  mStorage.reset( StorageManager::createNewMemoryStorageManager() );
748 
749  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
750  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
751  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
752  connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
754 }
755 
756 
758 {
759  // don't delete a locator if there is an indexing task running on it
760  mIsDestroying = true;
761  if ( mIsIndexing )
763 
764  destroyIndex();
765 }
766 
768 {
769  return mTransform.isValid() ? mTransform.destinationCrs() : QgsCoordinateReferenceSystem();
770 }
771 
773 {
774  if ( mIsIndexing )
775  // already indexing, return!
776  return;
777 
778  mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
779 
780  destroyIndex();
781 }
782 
784 {
785  if ( mIsIndexing )
786  // already indexing, return!
787  return;
788 
789  disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
790 
791  destroyIndex();
792  mContext.reset( nullptr );
793 
794  if ( context )
795  {
796  mContext = std::unique_ptr<QgsRenderContext>( new QgsRenderContext( *context ) );
798  }
799 
800 }
801 
802 void QgsPointLocator::onInitTaskFinished()
803 {
804  // Check that we don't call this method twice, when calling waitForFinished
805  // for instance (because of taskCompleted signal)
806  if ( !mIsIndexing )
807  return;
808 
809  if ( mIsDestroying )
810  return;
811 
812  mIsIndexing = false;
813  mRenderer.reset();
814  mSource.reset();
815 
816  // treat added and deleted feature while indexing
817  for ( QgsFeatureId fid : mAddedFeatures )
818  onFeatureAdded( fid );
819  mAddedFeatures.clear();
820 
821  for ( QgsFeatureId fid : mDeletedFeatures )
822  onFeatureDeleted( fid );
823  mDeletedFeatures.clear();
824 
825  emit initFinished( mInitTask->isBuildOK() );
826 }
827 
828 bool QgsPointLocator::init( int maxFeaturesToIndex, bool relaxed )
829 {
830  const QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
831  if ( geomType == QgsWkbTypes::NullGeometry // nothing to index
832  || hasIndex()
833  || mIsIndexing ) // already indexing, return!
834  return true;
835 
836  if ( !mLayer->dataProvider()
837  || !mLayer->dataProvider()->isValid() )
838  return false;
839 
840  mRenderer.reset( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
841  mSource.reset( new QgsVectorLayerFeatureSource( mLayer ) );
842 
843  if ( mContext )
844  {
845  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
846  }
847 
848  mIsIndexing = true;
849 
850  if ( relaxed )
851  {
852  mInitTask = new QgsPointLocatorInitTask( this );
853  connect( mInitTask, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskFinished );
854  connect( mInitTask, &QgsPointLocatorInitTask::taskCompleted, this, &QgsPointLocator::onInitTaskFinished );
855  QgsApplication::taskManager()->addTask( mInitTask );
856  return true;
857  }
858  else
859  {
860  const bool ok = rebuildIndex( maxFeaturesToIndex );
861  mIsIndexing = false;
862  emit initFinished( ok );
863  return ok;
864  }
865 }
866 
868 {
869  mInitTask->waitForFinished();
870 
871  if ( !mIsDestroying )
872  onInitTaskFinished();
873 }
874 
876 {
877  return mIsIndexing || mRTree || mIsEmptyLayer;
878 }
879 
880 bool QgsPointLocator::prepare( bool relaxed )
881 {
882  if ( mIsIndexing )
883  {
884  if ( relaxed )
885  return false;
886  else
888  }
889 
890  if ( !mRTree )
891  {
892  init( -1, relaxed );
893  if ( ( relaxed && mIsIndexing ) || !mRTree ) // relaxed mode and currently indexing or still invalid?
894  return false;
895  }
896 
897  return true;
898 }
899 
900 bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
901 {
902  QElapsedTimer t;
903  t.start();
904 
905  QgsDebugMsgLevel( QStringLiteral( "RebuildIndex start : %1" ).arg( mSource->id() ), 2 );
906 
907  destroyIndex();
908 
909  QLinkedList<RTree::Data *> dataList;
910  QgsFeature f;
911 
912  QgsFeatureRequest request;
913  request.setNoAttributes();
914 
915  if ( mExtent )
916  {
917  QgsRectangle rect = *mExtent;
918  if ( mTransform.isValid() )
919  {
920  try
921  {
923  }
924  catch ( const QgsException &e )
925  {
926  Q_UNUSED( e )
927  // See https://github.com/qgis/QGIS/issues/20749
928  QgsDebugMsg( QStringLiteral( "could not transform bounding box to map, skipping the snap filter (%1)" ).arg( e.what() ) );
929  }
930  }
931  request.setFilterRect( rect );
932  }
933 
934  bool filter = false;
935  QgsRenderContext *ctx = nullptr;
936  if ( mContext )
937  {
938  ctx = mContext.get();
939  if ( mRenderer )
940  {
941  // setup scale for scale dependent visibility (rule based)
942  mRenderer->startRender( *ctx, mSource->fields() );
943  filter = mRenderer->capabilities() & QgsFeatureRenderer::Filter;
944  request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), mSource->fields() );
945  }
946  }
947 
948  QgsFeatureIterator fi = mSource->getFeatures( request );
949  int indexedCount = 0;
950 
951  while ( fi.nextFeature( f ) )
952  {
953  if ( !f.hasGeometry() )
954  continue;
955 
956  if ( filter && ctx && mRenderer )
957  {
958  ctx->expressionContext().setFeature( f );
959  if ( !mRenderer->willRenderFeature( f, *ctx ) )
960  {
961  continue;
962  }
963  }
964 
965  if ( mTransform.isValid() )
966  {
967  try
968  {
969  QgsGeometry transformedGeometry = f.geometry();
970  transformedGeometry.transform( mTransform );
971  f.setGeometry( transformedGeometry );
972  }
973  catch ( const QgsException &e )
974  {
975  Q_UNUSED( e )
976  // See https://github.com/qgis/QGIS/issues/20749
977  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
978  continue;
979  }
980  }
981 
982  const QgsRectangle bbox = f.geometry().boundingBox();
983  if ( bbox.isFinite() )
984  {
985  SpatialIndex::Region r( rect2region( bbox ) );
986  dataList << new RTree::Data( 0, nullptr, r, f.id() );
987 
988  if ( mGeoms.contains( f.id() ) )
989  delete mGeoms.take( f.id() );
990  mGeoms[f.id()] = new QgsGeometry( f.geometry() );
991  ++indexedCount;
992  }
993 
994  if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
995  {
996  qDeleteAll( dataList );
997  destroyIndex();
998  return false;
999  }
1000  }
1001 
1002  // R-Tree parameters
1003  double fillFactor = 0.7;
1004  unsigned long indexCapacity = 10;
1005  unsigned long leafCapacity = 10;
1006  unsigned long dimension = 2;
1007  RTree::RTreeVariant variant = RTree::RV_RSTAR;
1008  SpatialIndex::id_type indexId;
1009 
1010  if ( dataList.isEmpty() )
1011  {
1012  mIsEmptyLayer = true;
1013  return true; // no features
1014  }
1015 
1016  QgsPointLocator_Stream stream( dataList );
1017  mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
1018  leafCapacity, dimension, variant, indexId ) );
1019 
1020  if ( ctx && mRenderer )
1021  {
1022  mRenderer->stopRender( *ctx );
1023  }
1024 
1025  QgsDebugMsgLevel( QStringLiteral( "RebuildIndex end : %1 ms (%2)" ).arg( t.elapsed() ).arg( mSource->id() ), 2 );
1026 
1027  return true;
1028 }
1029 
1030 
1032 {
1033  mRTree.reset();
1034 
1035  mIsEmptyLayer = false;
1036 
1037  qDeleteAll( mGeoms );
1038 
1039  mGeoms.clear();
1040 }
1041 
1042 void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
1043 {
1044  if ( mIsIndexing )
1045  {
1046  // will modify index once current indexing is finished
1047  mAddedFeatures << fid;
1048  return;
1049  }
1050 
1051  if ( !mRTree )
1052  {
1053  if ( mIsEmptyLayer )
1054  {
1055  // layer is not empty any more, let's build the index
1056  mIsEmptyLayer = false;
1057  init();
1058  }
1059  return; // nothing to do if we are not initialized yet
1060  }
1061 
1062  QgsFeature f;
1063  if ( mLayer->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f ) )
1064  {
1065  if ( !f.hasGeometry() )
1066  return;
1067 
1068  if ( mContext )
1069  {
1070  std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
1071  QgsRenderContext *ctx = nullptr;
1072 
1073  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
1074  ctx = mContext.get();
1075  if ( renderer && ctx )
1076  {
1077  bool pass = false;
1078  renderer->startRender( *ctx, mLayer->fields() );
1079 
1080  ctx->expressionContext().setFeature( f );
1081  if ( !renderer->willRenderFeature( f, *ctx ) )
1082  {
1083  pass = true;
1084  }
1085 
1086  renderer->stopRender( *ctx );
1087  if ( pass )
1088  return;
1089  }
1090  }
1091 
1092  if ( mTransform.isValid() )
1093  {
1094  try
1095  {
1096  QgsGeometry transformedGeom = f.geometry();
1097  transformedGeom.transform( mTransform );
1098  f.setGeometry( transformedGeom );
1099  }
1100  catch ( const QgsException &e )
1101  {
1102  Q_UNUSED( e )
1103  // See https://github.com/qgis/QGIS/issues/20749
1104  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
1105  return;
1106  }
1107  }
1108 
1109  const QgsRectangle bbox = f.geometry().boundingBox();
1110  if ( bbox.isFinite() )
1111  {
1112  SpatialIndex::Region r( rect2region( bbox ) );
1113  mRTree->insertData( 0, nullptr, r, f.id() );
1114 
1115  if ( mGeoms.contains( f.id() ) )
1116  delete mGeoms.take( f.id() );
1117  mGeoms[fid] = new QgsGeometry( f.geometry() );
1118  }
1119  }
1120 }
1121 
1122 void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
1123 {
1124  if ( mIsIndexing )
1125  {
1126  if ( mAddedFeatures.contains( fid ) )
1127  {
1128  mAddedFeatures.remove( fid );
1129  }
1130  else
1131  {
1132  // will modify index once current indexing is finished
1133  mDeletedFeatures << fid;
1134  }
1135  return;
1136  }
1137 
1138  if ( !mRTree )
1139  return; // nothing to do if we are not initialized yet
1140 
1141  if ( mGeoms.contains( fid ) )
1142  {
1143  mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
1144  delete mGeoms.take( fid );
1145  }
1146 
1147 }
1148 
1149 void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
1150 {
1151  Q_UNUSED( geom )
1152  onFeatureDeleted( fid );
1153  onFeatureAdded( fid );
1154 }
1155 
1156 void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
1157 {
1158  Q_UNUSED( idx )
1159  Q_UNUSED( value )
1160  if ( mContext )
1161  {
1162  onFeatureDeleted( fid );
1163  onFeatureAdded( fid );
1164  }
1165 }
1166 
1167 
1168 QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1169 {
1170  if ( !prepare( relaxed ) )
1171  return Match();
1172 
1173  Match m;
1174  QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
1175  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1176  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1177  if ( m.isValid() && m.distance() > tolerance )
1178  return Match(); // make sure that only match strictly within the tolerance is returned
1179  return m;
1180 }
1181 
1182 QgsPointLocator::Match QgsPointLocator::nearestCentroid( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1183 {
1184  if ( !prepare( relaxed ) )
1185  return Match();
1186 
1187  Match m;
1188  QgsPointLocator_VisitorNearestCentroid visitor( this, m, point, filter );
1189 
1190  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1191  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1192  if ( m.isValid() && m.distance() > tolerance )
1193  return Match(); // make sure that only match strictly within the tolerance is returned
1194  return m;
1195 }
1196 
1197 QgsPointLocator::Match QgsPointLocator::nearestMiddleOfSegment( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1198 {
1199  if ( !prepare( relaxed ) )
1200  return Match();
1201 
1202  Match m;
1203  QgsPointLocator_VisitorNearestMiddleOfSegment visitor( this, m, point, filter );
1204 
1205  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1206  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1207  if ( m.isValid() && m.distance() > tolerance )
1208  return Match(); // make sure that only match strictly within the tolerance is returned
1209  return m;
1210 }
1211 
1212 QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1213 {
1214  if ( !prepare( relaxed ) )
1215  return Match();
1216 
1217  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1218  if ( geomType == QgsWkbTypes::PointGeometry )
1219  return Match();
1220 
1221  Match m;
1222  QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
1223  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1224  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1225  if ( m.isValid() && m.distance() > tolerance )
1226  return Match(); // make sure that only match strictly within the tolerance is returned
1227  return m;
1228 }
1229 
1230 QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1231 {
1232  if ( !prepare( relaxed ) )
1233  return Match();
1234 
1235  MatchList mlist = pointInPolygon( point );
1236  if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() )
1237  {
1238  return mlist.at( 0 );
1239  }
1240 
1241  if ( tolerance == 0 )
1242  {
1243  return Match();
1244  }
1245 
1246  // discard point and line layers to keep only polygons
1247  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1248  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
1249  return Match();
1250 
1251  // use edges for adding tolerance
1252  Match m = nearestEdge( point, tolerance, filter );
1253  if ( m.isValid() )
1254  return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
1255  else
1256  return Match();
1257 }
1258 
1259 
1261 {
1262  if ( !prepare( relaxed ) )
1263  return MatchList();
1264 
1265  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1266  if ( geomType == QgsWkbTypes::PointGeometry )
1267  return MatchList();
1268 
1269  MatchList lst;
1270  QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
1271  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1272 
1273  return lst;
1274 }
1275 
1277 {
1278  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1279  return edgesInRect( rect, filter, relaxed );
1280 }
1281 
1283 {
1284  if ( !prepare( relaxed ) )
1285  return MatchList();
1286 
1287  MatchList lst;
1288  QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
1289  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1290 
1291  return lst;
1292 }
1293 
1295 {
1296  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1297  return verticesInRect( rect, filter, relaxed );
1298 }
1299 
1301 {
1302  if ( !prepare( relaxed ) )
1303  return MatchList();
1304 
1305  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1306  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
1307  return MatchList();
1308 
1309  MatchList lst;
1310  QgsPointLocator_VisitorArea visitor( this, point, lst );
1311  mRTree->intersectsWithQuery( point2point( point ), visitor );
1312  return lst;
1313 }
The part_iterator class provides STL-style iterator for const references to geometry parts.
The vertex_iterator class provides STL-style iterator for vertices.
QgsVertexId vertexId() const
Returns vertex ID of the current item.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
@ ReverseTransform
Transform from destination to source CRS.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual bool isValid() const =0
Returns true if this is a valid layer.
Defines a QGIS exception class.
Definition: qgsexception.h:35
QString what() const
Definition: qgsexception.h:48
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
@ Filter
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ....
Definition: qgsrenderer.h:256
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:204
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:144
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry convertToType(QgsWkbTypes::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:127
void convertToStraightSegment(double tolerance=M_PI/180., QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle)
Converts the geometry to straight line segments, if it is a curved geometry type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:91
void styleChanged()
Signal emitted whenever a change affects the layer's style.
void dataChanged()
Data of layer changed.
Helper class to dump the R-index nodes and their content.
void getNextEntry(const IEntry &entry, id_type &nextEntry, bool &hasNext) override
Helper class for bulk loading of R-trees.
IData * getNext() override
uint32_t size() override
QgsPointLocator_Stream(const QLinkedList< RTree::Data * > &dataList)
Helper class used when traversing the index with areas - builds a list of matches.
void visitNode(const INode &n) override
void visitData(const IData &d) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorArea(QgsPointLocator *pl, const QgsPointXY &origPt, QgsPointLocator::MatchList &list)
constructor
Helper class used when traversing the index looking for centroid - builds a list of matches.
QgsPointLocator_VisitorCentroidsInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
Constructs the visitor.
void visitData(const IData &d) override
void visitData(std::vector< const IData * > &v) override
void visitNode(const INode &n) override
Helper class used when traversing the index looking for edges - builds a list of matches.
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorEdgesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
void visitNode(const INode &n) override
void visitData(const IData &d) override
Helper class used when traversing the index looking for middle segment - builds a list of matches.
void visitData(std::vector< const IData * > &v) override
void visitNode(const INode &n) override
void visitData(const IData &d) override
QgsPointLocator_VisitorMiddlesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
Constructs the visitor.
Helper class used when traversing the index looking for centroid - builds a list of matches.
void visitData(const IData &d) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
Helper class used when traversing the index looking for edges - builds a list of matches.
QgsPointLocator_VisitorNearestEdge(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
void visitData(const IData &d) override
void visitData(std::vector< const IData * > &v) override
void visitNode(const INode &n) override
Helper class used when traversing the index looking for middle segment - builds a list of matches.
void visitData(std::vector< const IData * > &v) override
Helper class used when traversing the index looking for vertices - builds a list of matches.
void visitData(const IData &d) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorNearestVertex(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class used when traversing the index looking for vertices - builds a list of matches.
void visitData(const IData &d) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorVerticesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
Constructs the visitor.
The class defines interface for querying point location:
const QgsRectangle * extent() const
Gets extent of the area point locator covers - if nullptr then it caches the whole layer.
friend class QgsPointLocatorInitTask
MatchList pointInPolygon(const QgsPointXY &point, bool relaxed=false)
find out if the point is in any polygons This method is either blocking or non blocking according to ...
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 ...
void setRenderContext(const QgsRenderContext *context)
Configure render context - if not nullptr, it will use to index only visible feature.
QgsCoordinateReferenceSystem destinationCrs() const
Gets destination CRS - may be an invalid QgsCoordinateReferenceSystem if not doing OTF reprojection.
void setExtent(const QgsRectangle *extent)
Configure extent - if not nullptr, it will index only that area.
bool init(int maxFeaturesToIndex=-1, bool relaxed=false)
Prepare the index for queries.
MatchList verticesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find vertices within a specified rectangle This method is either blocking or non blocking according t...
class QList< QgsPointLocator::Match > MatchList
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...
QgsVectorLayer * layer() const
Gets associated layer.
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find edges within a specified rectangle Optional filter may discard unwanted matches.
bool hasIndex() const
Indicate whether the data have been already indexed.
Match nearestMiddleOfSegment(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest middle of segment to the specified point - up to distance specified by tolerance Optiona...
void waitForIndexingFinished()
If the point locator has been initialized relaxedly and is currently indexing, this methods waits for...
Match nearestCentroid(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest centroid to the specified point - up to distance specified by tolerance Optional filter ...
void initFinished(bool ok)
Emitted whenever index has been built and initialization is finished.
bool rebuildIndex(int maxFeaturesToIndex=-1)
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 ...
@ Area
Snapped to an area.
@ MiddleOfSegment
Snapped to the middle of a segment.
@ Vertex
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
@ Centroid
Snapped to a centroid.
@ Edge
Snapped to an edge.
QgsPointLocator(QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), const QgsRectangle *extent=nullptr)
Construct point locator for a layer.
~QgsPointLocator() override
A class to represent a 2D point.
Definition: qgspointxy.h:44
double y
Definition: qgspointxy.h:48
Q_GADGET double x
Definition: qgspointxy.h:47
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Definition: qgsrectangle.h:527
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
Partial snapshot of vector layer's state (only the members necessary for access to features)
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Emitted whenever an attribute value change is done in the edit buffer.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Emitted whenever a geometry change is done in the edit buffer.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
QgsPointLocator_VisitorNearestCentroid(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class used when traversing the index looking for centroid - builds a list of matches.
QgsPointLocator_VisitorNearestMiddleOfSegment(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class used when traversing the index looking for middle segment - builds a list of matches.
#define LEFT(x)
Definition: priorityqueue.h:38
#define RIGHT(x)
Definition: priorityqueue.h:39
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Interface that allows rejection of some matches in intersection queries (e.g.
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units,...
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
OutCode computeOutCode(double x, double y)
bool isSegmentInRect(double x0, double y0, double x1, double y1)
_CohenSutherland(const QgsRectangle &rect)