QGIS API Documentation  3.20.0-Odense (decaadbb31)
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"
29 #include "qgscurvepolygon.h"
31 #include <spatialindex/SpatialIndex.h>
32 
33 #include <QLinkedListIterator>
34 #include <QtConcurrent>
35 
36 using namespace SpatialIndex;
37 
38 
39 
40 static SpatialIndex::Point point2point( const QgsPointXY &point )
41 {
42  double plow[2] = { point.x(), point.y() };
43  return Point( plow, 2 );
44 }
45 
46 
47 static SpatialIndex::Region rect2region( const QgsRectangle &rect )
48 {
49  double pLow[2] = { rect.xMinimum(), rect.yMinimum() };
50  double pHigh[2] = { rect.xMaximum(), rect.yMaximum() };
51  return SpatialIndex::Region( pLow, pHigh, 2 );
52 }
53 
54 
55 // Ahh.... another magic number. Taken from QgsVectorLayer::snapToGeometry() call to closestSegmentWithContext().
56 // The default epsilon used for sqrDistToSegment (1e-8) is too high when working with lat/lon coordinates
57 // I still do not fully understand why the sqrDistToSegment() code uses epsilon and if the square distance
58 // is lower than epsilon it will have a special logic...
59 static const double POINT_LOC_EPSILON = 1e-12;
60 
62 
63 
69 class QgsPointLocator_Stream : public IDataStream
70 {
71  public:
72  explicit QgsPointLocator_Stream( const QLinkedList<RTree::Data *> &dataList )
73  : mDataList( dataList )
74  , mIt( mDataList )
75  { }
76 
77  IData *getNext() override { return mIt.next(); }
78  bool hasNext() override { return mIt.hasNext(); }
79 
80  uint32_t size() override { Q_ASSERT( false && "not available" ); return 0; }
81  void rewind() override { Q_ASSERT( false && "not available" ); }
82 
83  private:
84  QLinkedList<RTree::Data *> mDataList;
85  QLinkedListIterator<RTree::Data *> mIt;
86 };
87 
88 
90 
91 
97 class QgsPointLocator_VisitorNearestVertex : public IVisitor
98 {
99  public:
101  : mLocator( pl )
102  , mBest( m )
103  , mSrcPoint( srcPoint )
104  , mFilter( filter )
105  {}
106 
107  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
108  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
109 
110  void visitData( const IData &d ) override
111  {
112  QgsFeatureId id = d.getIdentifier();
113  QgsGeometry *geom = mLocator->mGeoms.value( id );
114  int vertexIndex, beforeVertex, afterVertex;
115  double sqrDist;
116 
117  QgsPointXY pt = geom->closestVertex( mSrcPoint, vertexIndex, beforeVertex, afterVertex, sqrDist );
118  if ( sqrDist < 0 )
119  return; // probably empty geometry
120 
121  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, vertexIndex );
122  // in range queries the filter may reject some matches
123  if ( mFilter && !mFilter->acceptMatch( m ) )
124  return;
125 
126  if ( !mBest.isValid() || m.distance() < mBest.distance() )
127  mBest = m;
128  }
129 
130  private:
131  QgsPointLocator *mLocator = nullptr;
132  QgsPointLocator::Match &mBest;
133  QgsPointXY mSrcPoint;
134  QgsPointLocator::MatchFilter *mFilter = nullptr;
135 };
136 
137 
138 
146 {
147  public:
148 
156  : mLocator( pl )
157  , mBest( m )
158  , mSrcPoint( srcPoint )
159  , mFilter( filter )
160  {}
161 
162  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
163  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
164 
165  void visitData( const IData &d ) override
166  {
167  QgsFeatureId id = d.getIdentifier();
168  QgsGeometry *geom = mLocator->mGeoms.value( id );
169 
170  QgsPointXY pt = geom->centroid().asPoint();
171 
172  QgsPointLocator::Match m( QgsPointLocator::Centroid, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( pt ) ), pt, -1 );
173  // in range queries the filter may reject some matches
174  if ( mFilter && !mFilter->acceptMatch( m ) )
175  return;
176 
177  if ( !mBest.isValid() || m.distance() < mBest.distance() )
178  mBest = m;
179 
180  }
181 
182  private:
183  QgsPointLocator *mLocator = nullptr;
184  QgsPointLocator::Match &mBest;
185  QgsPointXY mSrcPoint;
186  QgsPointLocator::MatchFilter *mFilter = nullptr;
187 };
188 
190 
198 {
199  public:
200 
208  : mLocator( pl )
209  , mBest( m )
210  , mSrcPoint( srcPoint )
211  , mFilter( filter )
212  {}
213 
214  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
215  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
216 
217  void visitData( const IData &d ) override
218  {
219  QgsFeatureId id = d.getIdentifier();
220  QgsGeometry *geom = mLocator->mGeoms.value( id );
221  QgsPointXY pt;
222  int afterVertex;
223  double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
224  if ( sqrDist < 0 )
225  return;
226 
227  QgsPointXY edgePoints[2];
228  edgePoints[0] = geom->vertexAt( afterVertex - 1 );
229  edgePoints[1] = geom->vertexAt( afterVertex );
230  pt = QgsPointXY( ( edgePoints[0].x() + edgePoints[1].x() ) / 2.0, ( edgePoints[0].y() + edgePoints[1].y() ) / 2.0 );
231 
232  QgsPointLocator::Match m( QgsPointLocator::MiddleOfSegment, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( pt ) ), pt, afterVertex - 1 );
233  // in range queries the filter may reject some matches
234  if ( mFilter && !mFilter->acceptMatch( m ) )
235  return;
236 
237  if ( !mBest.isValid() || m.distance() < mBest.distance() )
238  mBest = m;
239 
240  }
241 
242  private:
243  QgsPointLocator *mLocator = nullptr;
244  QgsPointLocator::Match &mBest;
245  QgsPointXY mSrcPoint;
246  QgsPointLocator::MatchFilter *mFilter = nullptr;
247 };
248 
250 
258 {
259  public:
260 
266  : mLocator( pl )
267  , mBest( m )
268  , mSrcPoint( srcPoint )
269  , mFilter( filter )
270  {}
271 
272  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
273  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
274 
275  void visitData( const IData &d ) override
276  {
277  QgsFeatureId id = d.getIdentifier();
278  const QgsGeometry *geom = mLocator->mGeoms.value( id );
279 
280  QgsPointXY bestPoint;
281  int bestVertexNumber = -1;
282  auto replaceIfBetter = [this, &bestPoint, &bestVertexNumber]( const QgsPoint & candidate, int vertexNumber )
283  {
284  if ( bestPoint.isEmpty() || candidate.distanceSquared( mSrcPoint.x(), mSrcPoint.y() ) < bestPoint.sqrDist( mSrcPoint ) )
285  {
286  bestPoint = QgsPointXY( candidate );
287  bestVertexNumber = vertexNumber;
288  }
289  };
290 
291  switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
292  {
296  return;
297 
299  {
300  int partStartVertexNum = 0;
301  for ( auto partIt = geom->const_parts_begin(); partIt != geom->const_parts_end(); ++partIt )
302  {
303  if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( *partIt ) )
304  {
305  replaceIfBetter( curve->startPoint(), partStartVertexNum );
306  replaceIfBetter( curve->endPoint(), partStartVertexNum + curve->numPoints() - 1 );
307  partStartVertexNum += curve->numPoints();
308  }
309  }
310  break;
311  }
312 
314  {
315  int partStartVertexNum = 0;
316  for ( auto partIt = geom->const_parts_begin(); partIt != geom->const_parts_end(); ++partIt )
317  {
318  if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( *partIt ) )
319  {
320  if ( polygon->exteriorRing() )
321  {
322  replaceIfBetter( polygon->exteriorRing()->startPoint(), partStartVertexNum );
323  partStartVertexNum += polygon->exteriorRing()->numPoints();
324  }
325  for ( int i = 0; i < polygon->numInteriorRings(); ++i )
326  {
327  const QgsCurve *ring = polygon->interiorRing( i );
328  replaceIfBetter( ring->startPoint(), partStartVertexNum );
329  partStartVertexNum += ring->numPoints();
330  }
331  }
332  }
333  break;
334  }
335  }
336 
337  QgsPointLocator::Match m( QgsPointLocator::LineEndpoint, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( bestPoint ) ), bestPoint, bestVertexNumber );
338  // in range queries the filter may reject some matches
339  if ( mFilter && !mFilter->acceptMatch( m ) )
340  return;
341 
342  if ( !mBest.isValid() || m.distance() < mBest.distance() )
343  mBest = m;
344  }
345 
346  private:
347  QgsPointLocator *mLocator = nullptr;
348  QgsPointLocator::Match &mBest;
349  QgsPointXY mSrcPoint;
350  QgsPointLocator::MatchFilter *mFilter = nullptr;
351 };
352 
353 
355 
356 
362 class QgsPointLocator_VisitorNearestEdge : public IVisitor
363 {
364  public:
366  : mLocator( pl )
367  , mBest( m )
368  , mSrcPoint( srcPoint )
369  , mFilter( filter )
370  {}
371 
372  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
373  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
374 
375  void visitData( const IData &d ) override
376  {
377  QgsFeatureId id = d.getIdentifier();
378  QgsGeometry *geom = mLocator->mGeoms.value( id );
379  QgsPointXY pt;
380  int afterVertex;
381  double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
382  if ( sqrDist < 0 )
383  return;
384 
385  QgsPointXY edgePoints[2];
386  edgePoints[0] = geom->vertexAt( afterVertex - 1 );
387  edgePoints[1] = geom->vertexAt( afterVertex );
388  QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, afterVertex - 1, edgePoints );
389  // in range queries the filter may reject some matches
390  if ( mFilter && !mFilter->acceptMatch( m ) )
391  return;
392 
393  if ( !mBest.isValid() || m.distance() < mBest.distance() )
394  mBest = m;
395  }
396 
397  private:
398  QgsPointLocator *mLocator = nullptr;
399  QgsPointLocator::Match &mBest;
400  QgsPointXY mSrcPoint;
401  QgsPointLocator::MatchFilter *mFilter = nullptr;
402 };
403 
404 
406 
412 class QgsPointLocator_VisitorArea : public IVisitor
413 {
414  public:
417  : mLocator( pl )
418  , mList( list )
419  , mGeomPt( QgsGeometry::fromPointXY( origPt ) )
420  {}
421 
422  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
423  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
424 
425  void visitData( const IData &d ) override
426  {
427  QgsFeatureId id = d.getIdentifier();
428  QgsGeometry *g = mLocator->mGeoms.value( id );
429  if ( g->intersects( mGeomPt ) )
430  mList << QgsPointLocator::Match( QgsPointLocator::Area, mLocator->mLayer, id, 0, mGeomPt.asPoint() );
431  }
432  private:
433  QgsPointLocator *mLocator = nullptr;
435  QgsGeometry mGeomPt;
436 };
437 
438 
440 
441 // code adapted from
442 // http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
444 {
445  explicit _CohenSutherland( const QgsRectangle &rect ) : mRect( rect ) {}
446 
447  typedef int OutCode;
448 
449  static const int INSIDE = 0; // 0000
450  static const int LEFT = 1; // 0001
451  static const int RIGHT = 2; // 0010
452  static const int BOTTOM = 4; // 0100
453  static const int TOP = 8; // 1000
454 
456 
457  OutCode computeOutCode( double x, double y )
458  {
459  OutCode code = INSIDE; // initialized as being inside of clip window
460  if ( x < mRect.xMinimum() ) // to the left of clip window
461  code |= LEFT;
462  else if ( x > mRect.xMaximum() ) // to the right of clip window
463  code |= RIGHT;
464  if ( y < mRect.yMinimum() ) // below the clip window
465  code |= BOTTOM;
466  else if ( y > mRect.yMaximum() ) // above the clip window
467  code |= TOP;
468  return code;
469  }
470 
471  bool isSegmentInRect( double x0, double y0, double x1, double y1 )
472  {
473  // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
474  OutCode outcode0 = computeOutCode( x0, y0 );
475  OutCode outcode1 = computeOutCode( x1, y1 );
476  bool accept = false;
477 
478  while ( true )
479  {
480  if ( !( outcode0 | outcode1 ) )
481  {
482  // Bitwise OR is 0. Trivially accept and get out of loop
483  accept = true;
484  break;
485  }
486  else if ( outcode0 & outcode1 )
487  {
488  // Bitwise AND is not 0. Trivially reject and get out of loop
489  break;
490  }
491  else
492  {
493  // failed both tests, so calculate the line segment to clip
494  // from an outside point to an intersection with clip edge
495  double x, y;
496 
497  // At least one endpoint is outside the clip rectangle; pick it.
498  OutCode outcodeOut = outcode0 ? outcode0 : outcode1;
499 
500  // Now find the intersection point;
501  // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
502  if ( outcodeOut & TOP )
503  {
504  // point is above the clip rectangle
505  x = x0 + ( x1 - x0 ) * ( mRect.yMaximum() - y0 ) / ( y1 - y0 );
506  y = mRect.yMaximum();
507  }
508  else if ( outcodeOut & BOTTOM )
509  {
510  // point is below the clip rectangle
511  x = x0 + ( x1 - x0 ) * ( mRect.yMinimum() - y0 ) / ( y1 - y0 );
512  y = mRect.yMinimum();
513  }
514  else if ( outcodeOut & RIGHT )
515  {
516  // point is to the right of clip rectangle
517  y = y0 + ( y1 - y0 ) * ( mRect.xMaximum() - x0 ) / ( x1 - x0 );
518  x = mRect.xMaximum();
519  }
520  else if ( outcodeOut & LEFT )
521  {
522  // point is to the left of clip rectangle
523  y = y0 + ( y1 - y0 ) * ( mRect.xMinimum() - x0 ) / ( x1 - x0 );
524  x = mRect.xMinimum();
525  }
526  else
527  break;
528 
529  // Now we move outside point to intersection point to clip
530  // and get ready for next pass.
531  if ( outcodeOut == outcode0 )
532  {
533  x0 = x;
534  y0 = y;
535  outcode0 = computeOutCode( x0, y0 );
536  }
537  else
538  {
539  x1 = x;
540  y1 = y;
541  outcode1 = computeOutCode( x1, y1 );
542  }
543  }
544  }
545  return accept;
546  }
547 };
548 
549 
550 static QgsPointLocator::MatchList _geometrySegmentsInRect( QgsGeometry *geom, const QgsRectangle &rect, QgsVectorLayer *vl, QgsFeatureId fid )
551 {
552  // this code is stupidly based on QgsGeometry::closestSegmentWithContext
553  // we need iterator for segments...
554 
556 
557  // geom is converted to a MultiCurve
558  QgsGeometry straightGeom = geom->convertToType( QgsWkbTypes::LineGeometry, true );
559  // and convert to straight segemnt / converts curve to linestring
560  straightGeom.convertToStraightSegment();
561 
562  // so, you must have multilinestring
563  //
564  // Special case: Intersections cannot be done on an empty linestring like
565  // QgsGeometry(QgsLineString()) or QgsGeometry::fromWkt("LINESTRING EMPTY")
566  if ( straightGeom.isEmpty() || ( ( straightGeom.type() != QgsWkbTypes::LineGeometry ) && ( !straightGeom.isMultipart() ) ) )
567  return lst;
568 
569  _CohenSutherland cs( rect );
570 
571  int pointIndex = 0;
572  for ( auto part = straightGeom.const_parts_begin(); part != straightGeom.const_parts_end(); ++part )
573  {
574  // Checking for invalid linestrings
575  // A linestring should/(must?) have at least two points.
576  QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( *part );
577  Q_ASSERT( !curve->hasCurvedSegments() );
578  if ( curve->numPoints() < 2 )
579  continue;
580 
581  QgsAbstractGeometry::vertex_iterator it = ( *part )->vertices_begin();
582  QgsPointXY prevPoint( *it );
583  it++;
584  while ( it != ( *part )->vertices_end() )
585  {
586  QgsPointXY thisPoint( *it );
587  if ( cs.isSegmentInRect( prevPoint.x(), prevPoint.y(), thisPoint.x(), thisPoint.y() ) )
588  {
589  QgsPointXY edgePoints[2];
590  edgePoints[0] = prevPoint;
591  edgePoints[1] = thisPoint;
592  lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
593  }
594  prevPoint = QgsPointXY( *it );
595  it++;
596  pointIndex += 1;
597 
598  }
599  }
600  return lst;
601 }
602 
608 class QgsPointLocator_VisitorEdgesInRect : public IVisitor
609 {
610  public:
612  : mLocator( pl )
613  , mList( lst )
614  , mSrcRect( srcRect )
615  , mFilter( filter )
616  {}
617 
618  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
619  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
620 
621  void visitData( const IData &d ) override
622  {
623  QgsFeatureId id = d.getIdentifier();
624  QgsGeometry *geom = mLocator->mGeoms.value( id );
625 
626  const auto segmentsInRect {_geometrySegmentsInRect( geom, mSrcRect, mLocator->mLayer, id )};
627  for ( const QgsPointLocator::Match &m : segmentsInRect )
628  {
629  // in range queries the filter may reject some matches
630  if ( mFilter && !mFilter->acceptMatch( m ) )
631  continue;
632 
633  mList << m;
634  }
635  }
636 
637  private:
638  QgsPointLocator *mLocator = nullptr;
640  QgsRectangle mSrcRect;
641  QgsPointLocator::MatchFilter *mFilter = nullptr;
642 };
643 
645 
653 {
654  public:
657  : mLocator( pl )
658  , mList( lst )
659  , mSrcRect( srcRect )
660  , mFilter( filter )
661  {}
662 
663  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
664  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
665 
666  void visitData( const IData &d ) override
667  {
668  QgsFeatureId id = d.getIdentifier();
669  const QgsGeometry *geom = mLocator->mGeoms.value( id );
670 
671  for ( QgsAbstractGeometry::vertex_iterator it = geom->vertices_begin(); it != geom->vertices_end(); ++it )
672  {
673  if ( mSrcRect.contains( *it ) )
674  {
675  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, 0, *it, geom->vertexNrFromVertexId( it.vertexId() ) );
676 
677  // in range queries the filter may reject some matches
678  if ( mFilter && !mFilter->acceptMatch( m ) )
679  continue;
680 
681  mList << m;
682  }
683  }
684  }
685 
686  private:
687  QgsPointLocator *mLocator = nullptr;
689  QgsRectangle mSrcRect;
690  QgsPointLocator::MatchFilter *mFilter = nullptr;
691 };
692 
700 {
701  public:
704  : mLocator( pl )
705  , mList( lst )
706  , mSrcRect( srcRect )
707  , mFilter( filter )
708  {}
709 
710  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
711  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
712 
713  void visitData( const IData &d ) override
714  {
715  QgsFeatureId id = d.getIdentifier();
716  const QgsGeometry *geom = mLocator->mGeoms.value( id );
717  const QgsPointXY centroid = geom->centroid().asPoint();
718  if ( mSrcRect.contains( centroid ) )
719  {
720  QgsPointLocator::Match m( QgsPointLocator::Centroid, mLocator->mLayer, id, 0, centroid, -1 );
721 
722  // in range queries the filter may reject some matches
723  if ( !( mFilter && !mFilter->acceptMatch( m ) ) )
724  mList << m;
725  }
726  }
727 
728  private:
729  QgsPointLocator *mLocator = nullptr;
731  QgsRectangle mSrcRect;
732  QgsPointLocator::MatchFilter *mFilter = nullptr;
733 };
734 
742 {
743  public:
746  : mLocator( pl )
747  , mList( lst )
748  , mSrcRect( srcRect )
749  , mFilter( filter )
750  {}
751 
752  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
753  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
754 
755  void visitData( const IData &d ) override
756  {
757  QgsFeatureId id = d.getIdentifier();
758  const QgsGeometry *geom = mLocator->mGeoms.value( id );
759 
760  for ( QgsAbstractGeometry::const_part_iterator itPart = geom->const_parts_begin() ; itPart != geom->const_parts_end() ; ++itPart )
761  {
762  QgsAbstractGeometry::vertex_iterator it = ( *itPart )->vertices_begin();
763  QgsAbstractGeometry::vertex_iterator itPrevious = ( *itPart )->vertices_begin();
764  it++;
765  for ( ; it != geom->vertices_end(); ++it, ++itPrevious )
766  {
767  QgsPointXY pt( ( ( *itPrevious ).x() + ( *it ).x() ) / 2.0, ( ( *itPrevious ).y() + ( *it ).y() ) / 2.0 );
768  if ( mSrcRect.contains( pt ) )
769  {
770  QgsPointLocator::Match m( QgsPointLocator::MiddleOfSegment, mLocator->mLayer, id, 0, pt, geom->vertexNrFromVertexId( it.vertexId() ) );
771 
772  // in range queries the filter may reject some matches
773  if ( mFilter && !mFilter->acceptMatch( m ) )
774  continue;
775 
776  mList << m;
777  }
778  }
779  }
780  }
781 
782  private:
783  QgsPointLocator *mLocator = nullptr;
785  QgsRectangle mSrcRect;
786  QgsPointLocator::MatchFilter *mFilter = nullptr;
787 };
788 
790 #include <QStack>
791 
797 class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy
798 {
799  private:
800  QStack<id_type> ids;
801 
802  public:
803 
804  void getNextEntry( const IEntry &entry, id_type &nextEntry, bool &hasNext ) override
805  {
806  const INode *n = dynamic_cast<const INode *>( &entry );
807  if ( !n )
808  return;
809 
810  QgsDebugMsgLevel( QStringLiteral( "NODE: %1" ).arg( n->getIdentifier() ), 4 );
811  if ( n->getLevel() > 0 )
812  {
813  // inner nodes
814  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
815  {
816  QgsDebugMsgLevel( QStringLiteral( "- CH: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
817  ids.push( n->getChildIdentifier( cChild ) );
818  }
819  }
820  else
821  {
822  // leaves
823  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
824  {
825  QgsDebugMsgLevel( QStringLiteral( "- L: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
826  }
827  }
828 
829  if ( ! ids.empty() )
830  {
831  nextEntry = ids.back();
832  ids.pop();
833  hasNext = true;
834  }
835  else
836  hasNext = false;
837  }
838 };
839 
841 
842 
844  : mLayer( layer )
845 {
846  if ( destCRS.isValid() )
847  {
848  mTransform = QgsCoordinateTransform( layer->crs(), destCRS, transformContext );
849  }
850 
851  setExtent( extent );
852 
853  mStorage.reset( StorageManager::createNewMemoryStorageManager() );
854 
855  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
856  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
857  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
858  connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
860 }
861 
862 
864 {
865  // don't delete a locator if there is an indexing task running on it
866  mIsDestroying = true;
867  if ( mIsIndexing )
869 
870  destroyIndex();
871 }
872 
874 {
875  return mTransform.isValid() ? mTransform.destinationCrs() : QgsCoordinateReferenceSystem();
876 }
877 
879 {
880  if ( mIsIndexing )
881  // already indexing, return!
882  return;
883 
884  mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
885 
886  destroyIndex();
887 }
888 
890 {
891  if ( mIsIndexing )
892  // already indexing, return!
893  return;
894 
895  disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
896 
897  destroyIndex();
898  mContext.reset( nullptr );
899 
900  if ( context )
901  {
902  mContext = std::unique_ptr<QgsRenderContext>( new QgsRenderContext( *context ) );
904  }
905 
906 }
907 
908 void QgsPointLocator::onInitTaskFinished()
909 {
910  // Check that we don't call this method twice, when calling waitForFinished
911  // for instance (because of taskCompleted signal)
912  if ( !mIsIndexing )
913  return;
914 
915  if ( mIsDestroying )
916  return;
917 
918  mIsIndexing = false;
919  mRenderer.reset();
920  mSource.reset();
921 
922  // treat added and deleted feature while indexing
923  for ( QgsFeatureId fid : mAddedFeatures )
924  onFeatureAdded( fid );
925  mAddedFeatures.clear();
926 
927  for ( QgsFeatureId fid : mDeletedFeatures )
928  onFeatureDeleted( fid );
929  mDeletedFeatures.clear();
930 
931  emit initFinished( mInitTask->isBuildOK() );
932 }
933 
934 bool QgsPointLocator::init( int maxFeaturesToIndex, bool relaxed )
935 {
936  const QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
937  if ( geomType == QgsWkbTypes::NullGeometry // nothing to index
938  || hasIndex()
939  || mIsIndexing ) // already indexing, return!
940  return true;
941 
942  if ( !mLayer->dataProvider()
943  || !mLayer->dataProvider()->isValid() )
944  return false;
945 
946  mRenderer.reset( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
947  mSource.reset( new QgsVectorLayerFeatureSource( mLayer ) );
948 
949  if ( mContext )
950  {
951  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
952  }
953 
954  mIsIndexing = true;
955 
956  if ( relaxed )
957  {
958  mInitTask = new QgsPointLocatorInitTask( this );
959  connect( mInitTask, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskFinished );
960  connect( mInitTask, &QgsPointLocatorInitTask::taskCompleted, this, &QgsPointLocator::onInitTaskFinished );
961  QgsApplication::taskManager()->addTask( mInitTask );
962  return true;
963  }
964  else
965  {
966  const bool ok = rebuildIndex( maxFeaturesToIndex );
967  mIsIndexing = false;
968  emit initFinished( ok );
969  return ok;
970  }
971 }
972 
974 {
975  mInitTask->waitForFinished();
976 
977  if ( !mIsDestroying )
978  onInitTaskFinished();
979 }
980 
982 {
983  return mIsIndexing || mRTree || mIsEmptyLayer;
984 }
985 
986 bool QgsPointLocator::prepare( bool relaxed )
987 {
988  if ( mIsIndexing )
989  {
990  if ( relaxed )
991  return false;
992  else
994  }
995 
996  if ( !mRTree )
997  {
998  init( -1, relaxed );
999  if ( ( relaxed && mIsIndexing ) || !mRTree ) // relaxed mode and currently indexing or still invalid?
1000  return false;
1001  }
1002 
1003  return true;
1004 }
1005 
1006 bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
1007 {
1008  QElapsedTimer t;
1009  t.start();
1010 
1011  QgsDebugMsgLevel( QStringLiteral( "RebuildIndex start : %1" ).arg( mSource->id() ), 2 );
1012 
1013  destroyIndex();
1014 
1015  QLinkedList<RTree::Data *> dataList;
1016  QgsFeature f;
1017 
1018  QgsFeatureRequest request;
1019  request.setNoAttributes();
1020 
1021  if ( mExtent )
1022  {
1023  QgsRectangle rect = *mExtent;
1024  if ( mTransform.isValid() )
1025  {
1026  try
1027  {
1029  }
1030  catch ( const QgsException &e )
1031  {
1032  Q_UNUSED( e )
1033  // See https://github.com/qgis/QGIS/issues/20749
1034  QgsDebugMsg( QStringLiteral( "could not transform bounding box to map, skipping the snap filter (%1)" ).arg( e.what() ) );
1035  }
1036  }
1037  request.setFilterRect( rect );
1038  }
1039 
1040  bool filter = false;
1041  QgsRenderContext *ctx = nullptr;
1042  if ( mContext )
1043  {
1044  ctx = mContext.get();
1045  if ( mRenderer )
1046  {
1047  // setup scale for scale dependent visibility (rule based)
1048  mRenderer->startRender( *ctx, mSource->fields() );
1049  filter = mRenderer->capabilities() & QgsFeatureRenderer::Filter;
1050  request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), mSource->fields() );
1051  }
1052  }
1053 
1054  QgsFeatureIterator fi = mSource->getFeatures( request );
1055  int indexedCount = 0;
1056 
1057  while ( fi.nextFeature( f ) )
1058  {
1059  if ( !f.hasGeometry() )
1060  continue;
1061 
1062  if ( filter && ctx && mRenderer )
1063  {
1064  ctx->expressionContext().setFeature( f );
1065  if ( !mRenderer->willRenderFeature( f, *ctx ) )
1066  {
1067  continue;
1068  }
1069  }
1070 
1071  if ( mTransform.isValid() )
1072  {
1073  try
1074  {
1075  QgsGeometry transformedGeometry = f.geometry();
1076  transformedGeometry.transform( mTransform );
1077  f.setGeometry( transformedGeometry );
1078  }
1079  catch ( const QgsException &e )
1080  {
1081  Q_UNUSED( e )
1082  // See https://github.com/qgis/QGIS/issues/20749
1083  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
1084  continue;
1085  }
1086  }
1087 
1088  const QgsRectangle bbox = f.geometry().boundingBox();
1089  if ( bbox.isFinite() )
1090  {
1091  SpatialIndex::Region r( rect2region( bbox ) );
1092  dataList << new RTree::Data( 0, nullptr, r, f.id() );
1093 
1094  if ( mGeoms.contains( f.id() ) )
1095  delete mGeoms.take( f.id() );
1096  mGeoms[f.id()] = new QgsGeometry( f.geometry() );
1097  ++indexedCount;
1098  }
1099 
1100  if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
1101  {
1102  qDeleteAll( dataList );
1103  destroyIndex();
1104  return false;
1105  }
1106  }
1107 
1108  // R-Tree parameters
1109  double fillFactor = 0.7;
1110  unsigned long indexCapacity = 10;
1111  unsigned long leafCapacity = 10;
1112  unsigned long dimension = 2;
1113  RTree::RTreeVariant variant = RTree::RV_RSTAR;
1114  SpatialIndex::id_type indexId;
1115 
1116  if ( dataList.isEmpty() )
1117  {
1118  mIsEmptyLayer = true;
1119  return true; // no features
1120  }
1121 
1122  QgsPointLocator_Stream stream( dataList );
1123  mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
1124  leafCapacity, dimension, variant, indexId ) );
1125 
1126  if ( ctx && mRenderer )
1127  {
1128  mRenderer->stopRender( *ctx );
1129  }
1130 
1131  QgsDebugMsgLevel( QStringLiteral( "RebuildIndex end : %1 ms (%2)" ).arg( t.elapsed() ).arg( mSource->id() ), 2 );
1132 
1133  return true;
1134 }
1135 
1136 
1138 {
1139  mRTree.reset();
1140 
1141  mIsEmptyLayer = false;
1142 
1143  qDeleteAll( mGeoms );
1144 
1145  mGeoms.clear();
1146 }
1147 
1148 void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
1149 {
1150  if ( mIsIndexing )
1151  {
1152  // will modify index once current indexing is finished
1153  mAddedFeatures << fid;
1154  return;
1155  }
1156 
1157  if ( !mRTree )
1158  {
1159  if ( mIsEmptyLayer )
1160  {
1161  // layer is not empty any more, let's build the index
1162  mIsEmptyLayer = false;
1163  init();
1164  }
1165  return; // nothing to do if we are not initialized yet
1166  }
1167 
1168  QgsFeature f;
1169  if ( mLayer->getFeatures( mContext ? QgsFeatureRequest( fid ) : QgsFeatureRequest( fid ).setNoAttributes() ).nextFeature( f ) )
1170  {
1171  if ( !f.hasGeometry() )
1172  return;
1173 
1174  if ( mContext )
1175  {
1176  std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
1177  QgsRenderContext *ctx = nullptr;
1178 
1179  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
1180  ctx = mContext.get();
1181  if ( renderer && ctx )
1182  {
1183  bool pass = false;
1184  renderer->startRender( *ctx, mLayer->fields() );
1185 
1186  ctx->expressionContext().setFeature( f );
1187  if ( !renderer->willRenderFeature( f, *ctx ) )
1188  {
1189  pass = true;
1190  }
1191 
1192  renderer->stopRender( *ctx );
1193  if ( pass )
1194  return;
1195  }
1196  }
1197 
1198  if ( mTransform.isValid() )
1199  {
1200  try
1201  {
1202  QgsGeometry transformedGeom = f.geometry();
1203  transformedGeom.transform( mTransform );
1204  f.setGeometry( transformedGeom );
1205  }
1206  catch ( const QgsException &e )
1207  {
1208  Q_UNUSED( e )
1209  // See https://github.com/qgis/QGIS/issues/20749
1210  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
1211  return;
1212  }
1213  }
1214 
1215  const QgsRectangle bbox = f.geometry().boundingBox();
1216  if ( bbox.isFinite() )
1217  {
1218  SpatialIndex::Region r( rect2region( bbox ) );
1219  mRTree->insertData( 0, nullptr, r, f.id() );
1220 
1221  if ( mGeoms.contains( f.id() ) )
1222  delete mGeoms.take( f.id() );
1223  mGeoms[fid] = new QgsGeometry( f.geometry() );
1224  }
1225  }
1226 }
1227 
1228 void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
1229 {
1230  if ( mIsIndexing )
1231  {
1232  if ( mAddedFeatures.contains( fid ) )
1233  {
1234  mAddedFeatures.remove( fid );
1235  }
1236  else
1237  {
1238  // will modify index once current indexing is finished
1239  mDeletedFeatures << fid;
1240  }
1241  return;
1242  }
1243 
1244  if ( !mRTree )
1245  return; // nothing to do if we are not initialized yet
1246 
1247  if ( mGeoms.contains( fid ) )
1248  {
1249  mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
1250  delete mGeoms.take( fid );
1251  }
1252 
1253 }
1254 
1255 void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
1256 {
1257  Q_UNUSED( geom )
1258  onFeatureDeleted( fid );
1259  onFeatureAdded( fid );
1260 }
1261 
1262 void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
1263 {
1264  Q_UNUSED( idx )
1265  Q_UNUSED( value )
1266  if ( mContext )
1267  {
1268  onFeatureDeleted( fid );
1269  onFeatureAdded( fid );
1270  }
1271 }
1272 
1273 
1274 QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1275 {
1276  if ( !prepare( relaxed ) )
1277  return Match();
1278 
1279  Match m;
1280  QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
1281  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1282  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1283  if ( m.isValid() && m.distance() > tolerance )
1284  return Match(); // make sure that only match strictly within the tolerance is returned
1285  return m;
1286 }
1287 
1288 QgsPointLocator::Match QgsPointLocator::nearestCentroid( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1289 {
1290  if ( !prepare( relaxed ) )
1291  return Match();
1292 
1293  Match m;
1294  QgsPointLocator_VisitorNearestCentroid visitor( this, m, point, filter );
1295 
1296  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1297  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1298  if ( m.isValid() && m.distance() > tolerance )
1299  return Match(); // make sure that only match strictly within the tolerance is returned
1300  return m;
1301 }
1302 
1303 QgsPointLocator::Match QgsPointLocator::nearestMiddleOfSegment( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1304 {
1305  if ( !prepare( relaxed ) )
1306  return Match();
1307 
1308  Match m;
1309  QgsPointLocator_VisitorNearestMiddleOfSegment visitor( this, m, point, filter );
1310 
1311  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1312  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1313  if ( m.isValid() && m.distance() > tolerance )
1314  return Match(); // make sure that only match strictly within the tolerance is returned
1315  return m;
1316 }
1317 
1319 {
1320  if ( !prepare( relaxed ) )
1321  return Match();
1322 
1323  Match m;
1324  QgsPointLocator_VisitorNearestLineEndpoint visitor( this, m, point, filter );
1325 
1326  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1327  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1328  if ( m.isValid() && m.distance() > tolerance )
1329  return Match(); // make sure that only match strictly within the tolerance is returned
1330  return m;
1331 }
1332 
1333 QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1334 {
1335  if ( !prepare( relaxed ) )
1336  return Match();
1337 
1338  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1339  if ( geomType == QgsWkbTypes::PointGeometry )
1340  return Match();
1341 
1342  Match m;
1343  QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
1344  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1345  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1346  if ( m.isValid() && m.distance() > tolerance )
1347  return Match(); // make sure that only match strictly within the tolerance is returned
1348  return m;
1349 }
1350 
1351 QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1352 {
1353  if ( !prepare( relaxed ) )
1354  return Match();
1355 
1356  MatchList mlist = pointInPolygon( point );
1357  if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() )
1358  {
1359  return mlist.at( 0 );
1360  }
1361 
1362  if ( tolerance == 0 )
1363  {
1364  return Match();
1365  }
1366 
1367  // discard point and line layers to keep only polygons
1368  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1369  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
1370  return Match();
1371 
1372  // use edges for adding tolerance
1373  Match m = nearestEdge( point, tolerance, filter );
1374  if ( m.isValid() )
1375  return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
1376  else
1377  return Match();
1378 }
1379 
1380 
1382 {
1383  if ( !prepare( relaxed ) )
1384  return MatchList();
1385 
1386  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1387  if ( geomType == QgsWkbTypes::PointGeometry )
1388  return MatchList();
1389 
1390  MatchList lst;
1391  QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
1392  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1393 
1394  return lst;
1395 }
1396 
1398 {
1399  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1400  return edgesInRect( rect, filter, relaxed );
1401 }
1402 
1404 {
1405  if ( !prepare( relaxed ) )
1406  return MatchList();
1407 
1408  MatchList lst;
1409  QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
1410  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1411 
1412  return lst;
1413 }
1414 
1416 {
1417  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1418  return verticesInRect( rect, filter, relaxed );
1419 }
1420 
1422 {
1423  if ( !prepare( relaxed ) )
1424  return MatchList();
1425 
1426  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1427  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
1428  return MatchList();
1429 
1430  MatchList lst;
1431  QgsPointLocator_VisitorArea visitor( this, point, lst );
1432  mRTree->intersectsWithQuery( point2point( point ), visitor );
1433  return lst;
1434 }
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...
Curve polygon geometry type.
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 QgsPoint startPoint() const =0
Returns the starting point of 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:264
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 unique ID, geometry and a list of field...
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:205
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:145
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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:76
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 line endpoints (start or end vertex) - builds...
void visitData(std::vector< const IData * > &v) override
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
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.
@ LineEndpoint
Start or end points of lines only (since QGIS 3.20)
QgsPointLocator(QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), const QgsRectangle *extent=nullptr)
Construct point locator for a layer.
~QgsPointLocator() override
Match nearestLineEndpoints(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest line endpoint (start or end vertex) to the specified point - up to distance specified by...
A class to represent a 2D point.
Definition: qgspointxy.h:59
bool isEmpty() const SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspointxy.h:249
double sqrDist(double x, double y) const SIP_HOLDGIL
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:190
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
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:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Definition: qgsrectangle.h:559
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.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:938
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.
QgsPointLocator_VisitorNearestLineEndpoint(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class used when traversing the index looking for line endpoints (start or end vertex) - builds...
#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)