QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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.isShortCircuited() )
1026  {
1027  QgsCoordinateTransform rectTransform = mTransform;
1028  rectTransform.setBallparkTransformsAreAppropriate( true );
1029  try
1030  {
1031  rect = rectTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
1032  }
1033  catch ( const QgsException &e )
1034  {
1035  Q_UNUSED( e )
1036  // See https://github.com/qgis/QGIS/issues/20749
1037  QgsDebugMsg( QStringLiteral( "could not transform bounding box to map, skipping the snap filter (%1)" ).arg( e.what() ) );
1038  }
1039  }
1040  request.setFilterRect( rect );
1041  }
1042 
1043  bool filter = false;
1044  QgsRenderContext *ctx = nullptr;
1045  if ( mContext )
1046  {
1047  ctx = mContext.get();
1048  if ( mRenderer )
1049  {
1050  // setup scale for scale dependent visibility (rule based)
1051  mRenderer->startRender( *ctx, mSource->fields() );
1052  filter = mRenderer->capabilities() & QgsFeatureRenderer::Filter;
1053  request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), mSource->fields() );
1054  }
1055  }
1056 
1057  QgsFeatureIterator fi = mSource->getFeatures( request );
1058  int indexedCount = 0;
1059 
1060  while ( fi.nextFeature( f ) )
1061  {
1062  if ( !f.hasGeometry() )
1063  continue;
1064 
1065  if ( filter && ctx && mRenderer )
1066  {
1067  ctx->expressionContext().setFeature( f );
1068  if ( !mRenderer->willRenderFeature( f, *ctx ) )
1069  {
1070  continue;
1071  }
1072  }
1073 
1074  if ( mTransform.isValid() )
1075  {
1076  try
1077  {
1078  QgsGeometry transformedGeometry = f.geometry();
1079  transformedGeometry.transform( mTransform );
1080  f.setGeometry( transformedGeometry );
1081  }
1082  catch ( const QgsException &e )
1083  {
1084  Q_UNUSED( e )
1085  // See https://github.com/qgis/QGIS/issues/20749
1086  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
1087  continue;
1088  }
1089  }
1090 
1091  const QgsRectangle bbox = f.geometry().boundingBox();
1092  if ( bbox.isFinite() )
1093  {
1094  SpatialIndex::Region r( rect2region( bbox ) );
1095  dataList << new RTree::Data( 0, nullptr, r, f.id() );
1096 
1097  if ( mGeoms.contains( f.id() ) )
1098  delete mGeoms.take( f.id() );
1099  mGeoms[f.id()] = new QgsGeometry( f.geometry() );
1100  ++indexedCount;
1101  }
1102 
1103  if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
1104  {
1105  qDeleteAll( dataList );
1106  destroyIndex();
1107  return false;
1108  }
1109  }
1110 
1111  // R-Tree parameters
1112  const double fillFactor = 0.7;
1113  const unsigned long indexCapacity = 10;
1114  const unsigned long leafCapacity = 10;
1115  const unsigned long dimension = 2;
1116  const RTree::RTreeVariant variant = RTree::RV_RSTAR;
1117  SpatialIndex::id_type indexId;
1118 
1119  if ( dataList.isEmpty() )
1120  {
1121  mIsEmptyLayer = true;
1122  return true; // no features
1123  }
1124 
1125  QgsPointLocator_Stream stream( dataList );
1126  mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
1127  leafCapacity, dimension, variant, indexId ) );
1128 
1129  if ( ctx && mRenderer )
1130  {
1131  mRenderer->stopRender( *ctx );
1132  }
1133 
1134  QgsDebugMsgLevel( QStringLiteral( "RebuildIndex end : %1 ms (%2)" ).arg( t.elapsed() ).arg( mSource->id() ), 2 );
1135 
1136  return true;
1137 }
1138 
1139 
1141 {
1142  mRTree.reset();
1143 
1144  mIsEmptyLayer = false;
1145 
1146  qDeleteAll( mGeoms );
1147 
1148  mGeoms.clear();
1149 }
1150 
1151 void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
1152 {
1153  if ( mIsIndexing )
1154  {
1155  // will modify index once current indexing is finished
1156  mAddedFeatures << fid;
1157  return;
1158  }
1159 
1160  if ( !mRTree )
1161  {
1162  if ( mIsEmptyLayer )
1163  {
1164  // layer is not empty any more, let's build the index
1165  mIsEmptyLayer = false;
1166  init();
1167  }
1168  return; // nothing to do if we are not initialized yet
1169  }
1170 
1171  QgsFeature f;
1172  if ( mLayer->getFeatures( mContext ? QgsFeatureRequest( fid ) : QgsFeatureRequest( fid ).setNoAttributes() ).nextFeature( f ) )
1173  {
1174  if ( !f.hasGeometry() )
1175  return;
1176 
1177  if ( mContext )
1178  {
1179  std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
1180  QgsRenderContext *ctx = nullptr;
1181 
1182  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
1183  ctx = mContext.get();
1184  if ( renderer && ctx )
1185  {
1186  bool pass = false;
1187  renderer->startRender( *ctx, mLayer->fields() );
1188 
1189  ctx->expressionContext().setFeature( f );
1190  if ( !renderer->willRenderFeature( f, *ctx ) )
1191  {
1192  pass = true;
1193  }
1194 
1195  renderer->stopRender( *ctx );
1196  if ( pass )
1197  return;
1198  }
1199  }
1200 
1201  if ( mTransform.isValid() )
1202  {
1203  try
1204  {
1205  QgsGeometry transformedGeom = f.geometry();
1206  transformedGeom.transform( mTransform );
1207  f.setGeometry( transformedGeom );
1208  }
1209  catch ( const QgsException &e )
1210  {
1211  Q_UNUSED( e )
1212  // See https://github.com/qgis/QGIS/issues/20749
1213  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
1214  return;
1215  }
1216  }
1217 
1218  const QgsRectangle bbox = f.geometry().boundingBox();
1219  if ( bbox.isFinite() )
1220  {
1221  const SpatialIndex::Region r( rect2region( bbox ) );
1222  mRTree->insertData( 0, nullptr, r, f.id() );
1223 
1224  if ( mGeoms.contains( f.id() ) )
1225  delete mGeoms.take( f.id() );
1226  mGeoms[fid] = new QgsGeometry( f.geometry() );
1227  }
1228  }
1229 }
1230 
1231 void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
1232 {
1233  if ( mIsIndexing )
1234  {
1235  if ( mAddedFeatures.contains( fid ) )
1236  {
1237  mAddedFeatures.remove( fid );
1238  }
1239  else
1240  {
1241  // will modify index once current indexing is finished
1242  mDeletedFeatures << fid;
1243  }
1244  return;
1245  }
1246 
1247  if ( !mRTree )
1248  return; // nothing to do if we are not initialized yet
1249 
1250  if ( mGeoms.contains( fid ) )
1251  {
1252  mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
1253  delete mGeoms.take( fid );
1254  }
1255 
1256 }
1257 
1258 void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
1259 {
1260  Q_UNUSED( geom )
1261  onFeatureDeleted( fid );
1262  onFeatureAdded( fid );
1263 }
1264 
1265 void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
1266 {
1267  Q_UNUSED( idx )
1268  Q_UNUSED( value )
1269  if ( mContext )
1270  {
1271  onFeatureDeleted( fid );
1272  onFeatureAdded( fid );
1273  }
1274 }
1275 
1276 
1277 QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1278 {
1279  if ( !prepare( relaxed ) )
1280  return Match();
1281 
1282  Match m;
1283  QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
1284  const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1285  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1286  if ( m.isValid() && m.distance() > tolerance )
1287  return Match(); // make sure that only match strictly within the tolerance is returned
1288  return m;
1289 }
1290 
1291 QgsPointLocator::Match QgsPointLocator::nearestCentroid( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1292 {
1293  if ( !prepare( relaxed ) )
1294  return Match();
1295 
1296  Match m;
1297  QgsPointLocator_VisitorNearestCentroid visitor( this, m, point, filter );
1298 
1299  const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1300  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1301  if ( m.isValid() && m.distance() > tolerance )
1302  return Match(); // make sure that only match strictly within the tolerance is returned
1303  return m;
1304 }
1305 
1306 QgsPointLocator::Match QgsPointLocator::nearestMiddleOfSegment( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1307 {
1308  if ( !prepare( relaxed ) )
1309  return Match();
1310 
1311  Match m;
1312  QgsPointLocator_VisitorNearestMiddleOfSegment visitor( this, m, point, filter );
1313 
1314  const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1315  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1316  if ( m.isValid() && m.distance() > tolerance )
1317  return Match(); // make sure that only match strictly within the tolerance is returned
1318  return m;
1319 }
1320 
1322 {
1323  if ( !prepare( relaxed ) )
1324  return Match();
1325 
1326  Match m;
1327  QgsPointLocator_VisitorNearestLineEndpoint visitor( this, m, point, filter );
1328 
1329  const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1330  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1331  if ( m.isValid() && m.distance() > tolerance )
1332  return Match(); // make sure that only match strictly within the tolerance is returned
1333  return m;
1334 }
1335 
1336 QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1337 {
1338  if ( !prepare( relaxed ) )
1339  return Match();
1340 
1341  const QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1342  if ( geomType == QgsWkbTypes::PointGeometry )
1343  return Match();
1344 
1345  Match m;
1346  QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
1347  const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1348  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1349  if ( m.isValid() && m.distance() > tolerance )
1350  return Match(); // make sure that only match strictly within the tolerance is returned
1351  return m;
1352 }
1353 
1354 QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1355 {
1356  if ( !prepare( relaxed ) )
1357  return Match();
1358 
1359  const MatchList mlist = pointInPolygon( point );
1360  if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() )
1361  {
1362  return mlist.at( 0 );
1363  }
1364 
1365  if ( tolerance == 0 )
1366  {
1367  return Match();
1368  }
1369 
1370  // discard point and line layers to keep only polygons
1371  const QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1372  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
1373  return Match();
1374 
1375  // use edges for adding tolerance
1376  const Match m = nearestEdge( point, tolerance, filter );
1377  if ( m.isValid() )
1378  return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
1379  else
1380  return Match();
1381 }
1382 
1383 
1385 {
1386  if ( !prepare( relaxed ) )
1387  return MatchList();
1388 
1389  const QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1390  if ( geomType == QgsWkbTypes::PointGeometry )
1391  return MatchList();
1392 
1393  MatchList lst;
1394  QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
1395  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1396 
1397  return lst;
1398 }
1399 
1401 {
1402  const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1403  return edgesInRect( rect, filter, relaxed );
1404 }
1405 
1407 {
1408  if ( !prepare( relaxed ) )
1409  return MatchList();
1410 
1411  MatchList lst;
1412  QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
1413  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
1414 
1415  return lst;
1416 }
1417 
1419 {
1420  const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1421  return verticesInRect( rect, filter, relaxed );
1422 }
1423 
1425 {
1426  if ( !prepare( relaxed ) )
1427  return MatchList();
1428 
1429  const QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
1430  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
1431  return MatchList();
1432 
1433  MatchList lst;
1434  QgsPointLocator_VisitorArea visitor( this, point, lst );
1435  mRTree->intersectsWithQuery( point2point( point ), visitor );
1436  return lst;
1437 }
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.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
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:223
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
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:968
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)