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