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 *
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"
30 #include "qgsrendercontext.h"
32 #include <spatialindex/SpatialIndex.h>
33 
34 #include <QLinkedListIterator>
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:
85  QLinkedList<RTree::Data *> mDataList;
86  QLinkedListIterator<RTree::Data *> mIt;
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 
442 // code adapted from
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 
856  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
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 )
882  // already indexing, return!
883  return;
884 
885  mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
886 
887  destroyIndex();
888 }
889 
891 {
892  if ( mIsIndexing )
893  // already indexing, return!
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 
909 void QgsPointLocator::onInitTaskFinished()
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 )
925  onFeatureAdded( fid );
926  mAddedFeatures.clear();
927 
928  for ( const QgsFeatureId fid : mDeletedFeatures )
929  onFeatureDeleted( fid );
930  mDeletedFeatures.clear();
931 
932  emit initFinished( mInitTask->isBuildOK() );
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  {
959  mInitTask = new QgsPointLocatorInitTask( this );
960  connect( mInitTask, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskFinished );
961  connect( mInitTask, &QgsPointLocatorInitTask::taskCompleted, this, &QgsPointLocator::onInitTaskFinished );
962  QgsApplication::taskManager()->addTask( mInitTask );
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 {
976  mInitTask->waitForFinished();
977 
978  if ( !mIsDestroying )
979  onInitTaskFinished();
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 
1016  QLinkedList<RTree::Data *> dataList;
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
1154  mAddedFeatures << fid;
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  {
1235  mAddedFeatures.remove( fid );
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 );
1260  onFeatureAdded( 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 );
1270  onFeatureAdded( 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.
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.
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
Q_GADGET QgsFeatureId id
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
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...
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)