QGIS API Documentation  3.0.2-Girona (307d082)
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 
25 #include <SpatialIndex.h>
26 
27 #include <QLinkedListIterator>
28 
29 using namespace SpatialIndex;
30 
31 
32 
33 static SpatialIndex::Point point2point( const QgsPointXY &point )
34 {
35  double plow[2] = { point.x(), point.y() };
36  return Point( plow, 2 );
37 }
38 
39 
40 static SpatialIndex::Region rect2region( const QgsRectangle &rect )
41 {
42  double pLow[2] = { rect.xMinimum(), rect.yMinimum() };
43  double pHigh[2] = { rect.xMaximum(), rect.yMaximum() };
44  return SpatialIndex::Region( pLow, pHigh, 2 );
45 }
46 
47 
48 // Ahh.... another magic number. Taken from QgsVectorLayer::snapToGeometry() call to closestSegmentWithContext().
49 // The default epsilon used for sqrDistToSegment (1e-8) is too high when working with lat/lon coordinates
50 // I still do not fully understand why the sqrDistToSegment() code uses epsilon and if the square distance
51 // is lower than epsilon it will have a special logic...
52 static const double POINT_LOC_EPSILON = 1e-12;
53 
55 
56 
62 class QgsPointLocator_Stream : public IDataStream
63 {
64  public:
65  explicit QgsPointLocator_Stream( const QLinkedList<RTree::Data *> &dataList )
66  : mDataList( dataList )
67  , mIt( mDataList )
68  { }
69 
70  IData *getNext() override { return mIt.next(); }
71  bool hasNext() override { return mIt.hasNext(); }
72 
73  uint32_t size() override { Q_ASSERT( false && "not available" ); return 0; }
74  void rewind() override { Q_ASSERT( false && "not available" ); }
75 
76  private:
77  QLinkedList<RTree::Data *> mDataList;
78  QLinkedListIterator<RTree::Data *> mIt;
79 };
80 
81 
83 
84 
90 class QgsPointLocator_VisitorNearestVertex : public IVisitor
91 {
92  public:
94  : mLocator( pl )
95  , mBest( m )
96  , mSrcPoint( srcPoint )
97  , mFilter( filter )
98  {}
99 
100  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
101  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
102 
103  void visitData( const IData &d ) override
104  {
105  QgsFeatureId id = d.getIdentifier();
106  QgsGeometry *geom = mLocator->mGeoms.value( id );
107  int vertexIndex, beforeVertex, afterVertex;
108  double sqrDist;
109 
110  QgsPointXY pt = geom->closestVertex( mSrcPoint, vertexIndex, beforeVertex, afterVertex, sqrDist );
111  if ( sqrDist < 0 )
112  return; // probably empty geometry
113 
114  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, vertexIndex );
115  // in range queries the filter may reject some matches
116  if ( mFilter && !mFilter->acceptMatch( m ) )
117  return;
118 
119  if ( !mBest.isValid() || m.distance() < mBest.distance() )
120  mBest = m;
121  }
122 
123  private:
124  QgsPointLocator *mLocator = nullptr;
125  QgsPointLocator::Match &mBest;
126  QgsPointXY mSrcPoint;
127  QgsPointLocator::MatchFilter *mFilter = nullptr;
128 };
129 
130 
132 
133 
139 class QgsPointLocator_VisitorNearestEdge : public IVisitor
140 {
141  public:
143  : mLocator( pl )
144  , mBest( m )
145  , mSrcPoint( srcPoint )
146  , mFilter( filter )
147  {}
148 
149  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
150  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
151 
152  void visitData( const IData &d ) override
153  {
154  QgsFeatureId id = d.getIdentifier();
155  QgsGeometry *geom = mLocator->mGeoms.value( id );
156  QgsPointXY pt;
157  int afterVertex;
158  double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
159  if ( sqrDist < 0 )
160  return;
161 
162  QgsPointXY edgePoints[2];
163  edgePoints[0] = geom->vertexAt( afterVertex - 1 );
164  edgePoints[1] = geom->vertexAt( afterVertex );
165  QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, afterVertex - 1, edgePoints );
166  // in range queries the filter may reject some matches
167  if ( mFilter && !mFilter->acceptMatch( m ) )
168  return;
169 
170  if ( !mBest.isValid() || m.distance() < mBest.distance() )
171  mBest = m;
172  }
173 
174  private:
175  QgsPointLocator *mLocator = nullptr;
176  QgsPointLocator::Match &mBest;
177  QgsPointXY mSrcPoint;
178  QgsPointLocator::MatchFilter *mFilter = nullptr;
179 };
180 
181 
183 
189 class QgsPointLocator_VisitorArea : public IVisitor
190 {
191  public:
194  : mLocator( pl )
195  , mList( list )
196  , mGeomPt( QgsGeometry::fromPointXY( origPt ) )
197  {}
198 
199  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
200  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
201 
202  void visitData( const IData &d ) override
203  {
204  QgsFeatureId id = d.getIdentifier();
205  QgsGeometry *g = mLocator->mGeoms.value( id );
206  if ( g->intersects( mGeomPt ) )
207  mList << QgsPointLocator::Match( QgsPointLocator::Area, mLocator->mLayer, id, 0, mGeomPt.asPoint() );
208  }
209  private:
210  QgsPointLocator *mLocator = nullptr;
212  QgsGeometry mGeomPt;
213 };
214 
215 
217 
218 // code adapted from
219 // http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
221 {
222  explicit _CohenSutherland( const QgsRectangle &rect ) : mRect( rect ) {}
223 
224  typedef int OutCode;
225 
226  static const int INSIDE = 0; // 0000
227  static const int LEFT = 1; // 0001
228  static const int RIGHT = 2; // 0010
229  static const int BOTTOM = 4; // 0100
230  static const int TOP = 8; // 1000
231 
233 
234  OutCode computeOutCode( double x, double y )
235  {
236  OutCode code = INSIDE; // initialized as being inside of clip window
237  if ( x < mRect.xMinimum() ) // to the left of clip window
238  code |= LEFT;
239  else if ( x > mRect.xMaximum() ) // to the right of clip window
240  code |= RIGHT;
241  if ( y < mRect.yMinimum() ) // below the clip window
242  code |= BOTTOM;
243  else if ( y > mRect.yMaximum() ) // above the clip window
244  code |= TOP;
245  return code;
246  }
247 
248  bool isSegmentInRect( double x0, double y0, double x1, double y1 )
249  {
250  // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
251  OutCode outcode0 = computeOutCode( x0, y0 );
252  OutCode outcode1 = computeOutCode( x1, y1 );
253  bool accept = false;
254 
255  while ( true )
256  {
257  if ( !( outcode0 | outcode1 ) )
258  {
259  // Bitwise OR is 0. Trivially accept and get out of loop
260  accept = true;
261  break;
262  }
263  else if ( outcode0 & outcode1 )
264  {
265  // Bitwise AND is not 0. Trivially reject and get out of loop
266  break;
267  }
268  else
269  {
270  // failed both tests, so calculate the line segment to clip
271  // from an outside point to an intersection with clip edge
272  double x, y;
273 
274  // At least one endpoint is outside the clip rectangle; pick it.
275  OutCode outcodeOut = outcode0 ? outcode0 : outcode1;
276 
277  // Now find the intersection point;
278  // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
279  if ( outcodeOut & TOP )
280  {
281  // point is above the clip rectangle
282  x = x0 + ( x1 - x0 ) * ( mRect.yMaximum() - y0 ) / ( y1 - y0 );
283  y = mRect.yMaximum();
284  }
285  else if ( outcodeOut & BOTTOM )
286  {
287  // point is below the clip rectangle
288  x = x0 + ( x1 - x0 ) * ( mRect.yMinimum() - y0 ) / ( y1 - y0 );
289  y = mRect.yMinimum();
290  }
291  else if ( outcodeOut & RIGHT )
292  {
293  // point is to the right of clip rectangle
294  y = y0 + ( y1 - y0 ) * ( mRect.xMaximum() - x0 ) / ( x1 - x0 );
295  x = mRect.xMaximum();
296  }
297  else if ( outcodeOut & LEFT )
298  {
299  // point is to the left of clip rectangle
300  y = y0 + ( y1 - y0 ) * ( mRect.xMinimum() - x0 ) / ( x1 - x0 );
301  x = mRect.xMinimum();
302  }
303  else
304  break;
305 
306  // Now we move outside point to intersection point to clip
307  // and get ready for next pass.
308  if ( outcodeOut == outcode0 )
309  {
310  x0 = x;
311  y0 = y;
312  outcode0 = computeOutCode( x0, y0 );
313  }
314  else
315  {
316  x1 = x;
317  y1 = y;
318  outcode1 = computeOutCode( x1, y1 );
319  }
320  }
321  }
322  return accept;
323  }
324 };
325 
326 
327 static QgsPointLocator::MatchList _geometrySegmentsInRect( QgsGeometry *geom, const QgsRectangle &rect, QgsVectorLayer *vl, QgsFeatureId fid )
328 {
329  // this code is stupidly based on QgsGeometry::closestSegmentWithContext
330  // we need iterator for segments...
331 
333  QByteArray wkb( geom->asWkb() );
334  if ( wkb.isEmpty() )
335  return lst;
336 
337  _CohenSutherland cs( rect );
338 
339  QgsConstWkbPtr wkbPtr( wkb );
340  wkbPtr.readHeader();
341 
342  QgsWkbTypes::Type wkbType = geom->wkbType();
343 
344  bool hasZValue = false;
345  switch ( wkbType )
346  {
348  case QgsWkbTypes::Point:
351  {
352  // Points have no lines
353  return lst;
354  }
355 
357  hasZValue = true;
358  //intentional fall-through
359  FALLTHROUGH;
361  {
362  int nPoints;
363  wkbPtr >> nPoints;
364 
365  double prevx = 0.0, prevy = 0.0;
366  for ( int index = 0; index < nPoints; ++index )
367  {
368  double thisx = 0.0, thisy = 0.0;
369  wkbPtr >> thisx >> thisy;
370  if ( hasZValue )
371  wkbPtr += sizeof( double );
372 
373  if ( index > 0 )
374  {
375  if ( cs.isSegmentInRect( prevx, prevy, thisx, thisy ) )
376  {
377  QgsPointXY edgePoints[2];
378  edgePoints[0].set( prevx, prevy );
379  edgePoints[1].set( thisx, thisy );
380  lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), index - 1, edgePoints );
381  }
382  }
383 
384  prevx = thisx;
385  prevy = thisy;
386  }
387  break;
388  }
389 
391  hasZValue = true;
392  //intentional fall-through
393  FALLTHROUGH;
395  {
396  int nLines;
397  wkbPtr >> nLines;
398  for ( int linenr = 0, pointIndex = 0; linenr < nLines; ++linenr )
399  {
400  wkbPtr.readHeader();
401  int nPoints;
402  wkbPtr >> nPoints;
403 
404  double prevx = 0.0, prevy = 0.0;
405  for ( int pointnr = 0; pointnr < nPoints; ++pointnr )
406  {
407  double thisx = 0.0, thisy = 0.0;
408  wkbPtr >> thisx >> thisy;
409  if ( hasZValue )
410  wkbPtr += sizeof( double );
411 
412  if ( pointnr > 0 )
413  {
414  if ( cs.isSegmentInRect( prevx, prevy, thisx, thisy ) )
415  {
416  QgsPointXY edgePoints[2];
417  edgePoints[0].set( prevx, prevy );
418  edgePoints[1].set( thisx, thisy );
419  lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
420  }
421  }
422 
423  prevx = thisx;
424  prevy = thisy;
425  ++pointIndex;
426  }
427  }
428  break;
429  }
430 
432  hasZValue = true;
433  //intentional fall-through
434  FALLTHROUGH;
436  {
437  int nRings;
438  wkbPtr >> nRings;
439 
440  for ( int ringnr = 0, pointIndex = 0; ringnr < nRings; ++ringnr )//loop over rings
441  {
442  int nPoints;
443  wkbPtr >> nPoints;
444 
445  double prevx = 0.0, prevy = 0.0;
446  for ( int pointnr = 0; pointnr < nPoints; ++pointnr )//loop over points in a ring
447  {
448  double thisx = 0.0, thisy = 0.0;
449  wkbPtr >> thisx >> thisy;
450  if ( hasZValue )
451  wkbPtr += sizeof( double );
452 
453  if ( pointnr > 0 )
454  {
455  if ( cs.isSegmentInRect( prevx, prevy, thisx, thisy ) )
456  {
457  QgsPointXY edgePoints[2];
458  edgePoints[0].set( prevx, prevy );
459  edgePoints[1].set( thisx, thisy );
460  lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
461  }
462  }
463 
464  prevx = thisx;
465  prevy = thisy;
466  ++pointIndex;
467  }
468  }
469  break;
470  }
471 
473  hasZValue = true;
474  //intentional fall-through
475  FALLTHROUGH;
477  {
478  int nPolygons;
479  wkbPtr >> nPolygons;
480  for ( int polynr = 0, pointIndex = 0; polynr < nPolygons; ++polynr )
481  {
482  wkbPtr.readHeader();
483  int nRings;
484  wkbPtr >> nRings;
485  for ( int ringnr = 0; ringnr < nRings; ++ringnr )
486  {
487  int nPoints;
488  wkbPtr >> nPoints;
489 
490  double prevx = 0.0, prevy = 0.0;
491  for ( int pointnr = 0; pointnr < nPoints; ++pointnr )
492  {
493  double thisx = 0.0, thisy = 0.0;
494  wkbPtr >> thisx >> thisy;
495  if ( hasZValue )
496  wkbPtr += sizeof( double );
497 
498  if ( pointnr > 0 )
499  {
500  if ( cs.isSegmentInRect( prevx, prevy, thisx, thisy ) )
501  {
502  QgsPointXY edgePoints[2];
503  edgePoints[0].set( prevx, prevy );
504  edgePoints[1].set( thisx, thisy );
505  lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
506  }
507  }
508 
509  prevx = thisx;
510  prevy = thisy;
511  ++pointIndex;
512  }
513  }
514  }
515  break;
516  }
517 
519  default:
520  return lst;
521  } // switch (wkbType)
522 
523  return lst;
524 }
525 
531 class QgsPointLocator_VisitorEdgesInRect : public IVisitor
532 {
533  public:
535  : mLocator( pl )
536  , mList( lst )
537  , mSrcRect( srcRect )
538  , mFilter( filter )
539  {}
540 
541  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
542  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
543 
544  void visitData( const IData &d ) override
545  {
546  QgsFeatureId id = d.getIdentifier();
547  QgsGeometry *geom = mLocator->mGeoms.value( id );
548 
549  Q_FOREACH ( const QgsPointLocator::Match &m, _geometrySegmentsInRect( geom, mSrcRect, mLocator->mLayer, id ) )
550  {
551  // in range queries the filter may reject some matches
552  if ( mFilter && !mFilter->acceptMatch( m ) )
553  continue;
554 
555  mList << m;
556  }
557  }
558 
559  private:
560  QgsPointLocator *mLocator = nullptr;
562  QgsRectangle mSrcRect;
563  QgsPointLocator::MatchFilter *mFilter = nullptr;
564 };
565 
566 
567 
569 #include <QStack>
570 
576 class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy
577 {
578  private:
579  QStack<id_type> ids;
580 
581  public:
582 
583  void getNextEntry( const IEntry &entry, id_type &nextEntry, bool &hasNext ) override
584  {
585  const INode *n = dynamic_cast<const INode *>( &entry );
586  if ( !n )
587  return;
588 
589  QgsDebugMsg( QString( "NODE: %1" ).arg( n->getIdentifier() ) );
590  if ( n->getLevel() > 0 )
591  {
592  // inner nodes
593  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
594  {
595  QgsDebugMsg( QString( "- CH: %1" ).arg( n->getChildIdentifier( cChild ) ) );
596  ids.push( n->getChildIdentifier( cChild ) );
597  }
598  }
599  else
600  {
601  // leaves
602  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
603  {
604  QgsDebugMsg( QString( "- L: %1" ).arg( n->getChildIdentifier( cChild ) ) );
605  }
606  }
607 
608  if ( ! ids.empty() )
609  {
610  nextEntry = ids.back();
611  ids.pop();
612  hasNext = true;
613  }
614  else
615  hasNext = false;
616  }
617 };
618 
620 
621 
623  : mIsEmptyLayer( false )
624  , mLayer( layer )
625 {
626  if ( destCRS.isValid() )
627  {
628  mTransform = QgsCoordinateTransform( layer->crs(), destCRS, transformContext );
629  }
630 
631  setExtent( extent );
632 
633  mStorage = StorageManager::createNewMemoryStorageManager();
634 
635  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
636  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
637  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
639 }
640 
641 
643 {
644  destroyIndex();
645  delete mStorage;
646  delete mExtent;
647 }
648 
650 {
651  return mTransform.isValid() ? mTransform.destinationCrs() : QgsCoordinateReferenceSystem();
652 }
653 
655 {
656  if ( extent )
657  {
658  mExtent = new QgsRectangle( *extent );
659  }
660 
661  destroyIndex();
662 }
663 
664 
665 bool QgsPointLocator::init( int maxFeaturesToIndex )
666 {
667  return hasIndex() ? true : rebuildIndex( maxFeaturesToIndex );
668 }
669 
670 
672 {
673  return mRTree || mIsEmptyLayer;
674 }
675 
676 
677 bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
678 {
679  destroyIndex();
680 
681  QLinkedList<RTree::Data *> dataList;
682  QgsFeature f;
683  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
684  if ( geomType == QgsWkbTypes::NullGeometry )
685  return true; // nothing to index
686 
687  QgsFeatureRequest request;
689  if ( mExtent )
690  {
691  QgsRectangle rect = *mExtent;
692  if ( mTransform.isValid() )
693  {
694  try
695  {
697  }
698  catch ( const QgsException &e )
699  {
700  Q_UNUSED( e );
701  // See https://issues.qgis.org/issues/12634
702  QgsDebugMsg( QString( "could not transform bounding box to map, skipping the snap filter (%1)" ).arg( e.what() ) );
703  }
704  }
705  request.setFilterRect( rect );
706  }
707  QgsFeatureIterator fi = mLayer->getFeatures( request );
708  int indexedCount = 0;
709  while ( fi.nextFeature( f ) )
710  {
711  if ( !f.hasGeometry() )
712  continue;
713 
714  if ( mTransform.isValid() )
715  {
716  try
717  {
718  QgsGeometry transformedGeometry = f.geometry();
719  transformedGeometry.transform( mTransform );
720  f.setGeometry( transformedGeometry );
721  }
722  catch ( const QgsException &e )
723  {
724  Q_UNUSED( e );
725  // See https://issues.qgis.org/issues/12634
726  QgsDebugMsg( QString( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
727  continue;
728  }
729  }
730 
731  SpatialIndex::Region r( rect2region( f.geometry().boundingBox() ) );
732  dataList << new RTree::Data( 0, nullptr, r, f.id() );
733 
734  if ( mGeoms.contains( f.id() ) )
735  delete mGeoms.take( f.id() );
736  mGeoms[f.id()] = new QgsGeometry( f.geometry() );
737  ++indexedCount;
738 
739  if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
740  {
741  qDeleteAll( dataList );
742  destroyIndex();
743  return false;
744  }
745  }
746 
747  // R-Tree parameters
748  double fillFactor = 0.7;
749  unsigned long indexCapacity = 10;
750  unsigned long leafCapacity = 10;
751  unsigned long dimension = 2;
752  RTree::RTreeVariant variant = RTree::RV_RSTAR;
753  SpatialIndex::id_type indexId;
754 
755  if ( dataList.isEmpty() )
756  {
757  mIsEmptyLayer = true;
758  return true; // no features
759  }
760 
761  QgsPointLocator_Stream stream( dataList );
762  mRTree = RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
763  leafCapacity, dimension, variant, indexId );
764  return true;
765 }
766 
767 
769 {
770  delete mRTree;
771  mRTree = nullptr;
772 
773  mIsEmptyLayer = false;
774 
775  qDeleteAll( mGeoms );
776 
777  mGeoms.clear();
778 }
779 
780 void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
781 {
782  if ( !mRTree )
783  {
784  if ( mIsEmptyLayer )
785  rebuildIndex(); // first feature - let's built the index
786  return; // nothing to do if we are not initialized yet
787  }
788 
789  QgsFeature f;
790  if ( mLayer->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f ) )
791  {
792  if ( !f.hasGeometry() )
793  return;
794 
795  if ( mTransform.isValid() )
796  {
797  try
798  {
799  QgsGeometry transformedGeom = f.geometry();
800  transformedGeom.transform( mTransform );
801  f.setGeometry( transformedGeom );
802  }
803  catch ( const QgsException &e )
804  {
805  Q_UNUSED( e );
806  // See https://issues.qgis.org/issues/12634
807  QgsDebugMsg( QString( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
808  return;
809  }
810  }
811 
812  QgsRectangle bbox = f.geometry().boundingBox();
813  if ( !bbox.isNull() )
814  {
815  SpatialIndex::Region r( rect2region( bbox ) );
816  mRTree->insertData( 0, nullptr, r, f.id() );
817 
818  if ( mGeoms.contains( f.id() ) )
819  delete mGeoms.take( f.id() );
820  mGeoms[fid] = new QgsGeometry( f.geometry() );
821  }
822  }
823 }
824 
825 void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
826 {
827  if ( !mRTree )
828  return; // nothing to do if we are not initialized yet
829 
830  if ( mGeoms.contains( fid ) )
831  {
832  mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
833  delete mGeoms.take( fid );
834  }
835 }
836 
837 void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
838 {
839  Q_UNUSED( geom );
840  onFeatureDeleted( fid );
841  onFeatureAdded( fid );
842 }
843 
844 
846 {
847  if ( !mRTree )
848  {
849  init();
850  if ( !mRTree ) // still invalid?
851  return Match();
852  }
853 
854  Match m;
855  QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
856  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
857  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
858  if ( m.isValid() && m.distance() > tolerance )
859  return Match(); // make sure that only match strictly within the tolerance is returned
860  return m;
861 }
862 
864 {
865  if ( !mRTree )
866  {
867  init();
868  if ( !mRTree ) // still invalid?
869  return Match();
870  }
871 
872  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
873  if ( geomType == QgsWkbTypes::PointGeometry )
874  return Match();
875 
876  Match m;
877  QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
878  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
879  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
880  if ( m.isValid() && m.distance() > tolerance )
881  return Match(); // make sure that only match strictly within the tolerance is returned
882  return m;
883 }
884 
886 {
887  if ( !mRTree )
888  {
889  init();
890  if ( !mRTree ) // still invalid?
891  return Match();
892  }
893 
894  MatchList mlist = pointInPolygon( point );
895  if ( mlist.count() && mlist.at( 0 ).isValid() )
896  {
897  return mlist.at( 0 );
898  }
899 
900  if ( tolerance == 0 )
901  {
902  return Match();
903  }
904 
905  // discard point and line layers to keep only polygons
906  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
907  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
908  return Match();
909 
910  // use edges for adding tolerance
911  Match m = nearestEdge( point, tolerance, filter );
912  if ( m.isValid() )
913  return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
914  else
915  return Match();
916 }
917 
918 
920 {
921  if ( !mRTree )
922  {
923  init();
924  if ( !mRTree ) // still invalid?
925  return MatchList();
926  }
927 
928  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
929  if ( geomType == QgsWkbTypes::PointGeometry )
930  return MatchList();
931 
932  MatchList lst;
933  QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
934  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
935 
936  return lst;
937 }
938 
940 {
941  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
942  return edgesInRect( rect, filter );
943 }
944 
945 
947 {
948  if ( !mRTree )
949  {
950  init();
951  if ( !mRTree ) // still invalid?
952  return MatchList();
953  }
954 
955  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
956  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
957  return MatchList();
958 
959  MatchList lst;
960  QgsPointLocator_VisitorArea visitor( this, point, lst );
961  mRTree->intersectsWithQuery( point2point( point ), visitor );
962  return lst;
963 }
#define LEFT(x)
Definition: priorityqueue.h:38
The class defines interface for querying point location:
QgsFeatureId id
Definition: qgsfeature.h:71
Wrapper for iterator of features from vector data provider or vector layer.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &afterVertex, int *leftOf=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
void visitData(std::vector< const IData *> &v) override
void set(double x, double y)
Sets the x and y value of the point.
Definition: qgspointxy.h:119
A rectangle specified with double values.
Definition: qgsrectangle.h:39
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
uint32_t size() override
void visitData(const IData &d) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool hasIndex() const
Indicate whether the data have been already indexed.
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
double y
Definition: qgspointxy.h:48
QgsPointLocator_VisitorArea(QgsPointLocator *pl, const QgsPointXY &origPt, QgsPointLocator::MatchList &list)
constructor
A class to represent a 2D point.
Definition: qgspointxy.h:43
Match nearestEdge(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest edge to the specified point - up to distance specified by tolerance Optional filter may ...
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem destinationCrs() const
Get destination CRS - may be an invalid QgsCoordinateReferenceSystem if not doing OTF reprojection...
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
class QList< QgsPointLocator::Match > MatchList
Helper class used when traversing the index looking for edges - builds a list of matches.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
Match nearestArea(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest area to the specified point - up to distance specified by tolerance Optional filter may ...
bool rebuildIndex(int maxFeaturesToIndex=-1)
void visitNode(const INode &n) override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
Interface that allows rejection of some matches in intersection queries (e.g.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsPointLocator_Stream(const QLinkedList< RTree::Data *> &dataList)
QString what() const
Definition: qgsexception.h:48
QgsPointXY closestVertex(const QgsPointXY &point, int &atVertex, int &beforeVertex, int &afterVertex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
~QgsPointLocator() override
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
#define FALLTHROUGH
Definition: qgis.h:548
QgsPointLocator_VisitorNearestVertex(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
OutCode computeOutCode(double x, double y)
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
Helper class to dump the R-index nodes and their content.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
bool isSegmentInRect(double x0, double y0, double x1, double y1)
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Is emitted whenever a geometry change is done in the edit buffer.
void visitNode(const INode &n) override
void setExtent(const QgsRectangle *extent)
Configure extent - if not null, it will index only that area.
Helper class used when traversing the index with areas - builds a list of matches.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.
Contains information about the context in which a coordinate transform is executed.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
void visitData(const IData &d) override
Helper class used when traversing the index looking for vertices - builds a list of matches...
const QgsRectangle * extent() const
Get extent of the area point locator covers - if null then it caches the whole layer.
void visitData(std::vector< const IData *> &v) override
QByteArray asWkb() const
Export the geometry to WKB.
double x
Definition: qgspointxy.h:47
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
IData * getNext() override
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
void visitData(std::vector< const IData *> &v) override
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:130
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:115
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:137
QgsPointLocator(QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), const QgsRectangle *extent=nullptr)
Construct point locator for a layer.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords...
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
void visitData(std::vector< const IData *> &v) override
Transform from destination to source CRS.
QgsPointLocator_VisitorNearestEdge(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class for bulk loading of R-trees.
#define RIGHT(x)
Definition: priorityqueue.h:39
void visitData(const IData &d) override
MatchList pointInPolygon(const QgsPointXY &point)
find out if the point is in any polygons
void visitNode(const INode &n) override
bool init(int maxFeaturesToIndex=-1)
Prepare the index for queries.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
_CohenSutherland(const QgsRectangle &rect)
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
void getNextEntry(const IEntry &entry, id_type &nextEntry, bool &hasNext) override
Match nearestVertex(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest vertex to the specified point - up to distance specified by tolerance Optional filter ma...
void visitData(const IData &d) override
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:120
qint64 QgsFeatureId
Definition: qgsfeature.h:37
void dataChanged()
Data of layer changed.
Snapped to an edge.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units...
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:125
Helper class used when traversing the index looking for edges - builds a list of matches.
void visitNode(const INode &n) override
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr)
Find edges within a specified recangle Optional filter may discard unwanted matches.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
bool nextFeature(QgsFeature &f)
Represents a vector layer which manages a vector based data sets.
QgsWkbTypes::Type readHeader() const
readHeader
Definition: qgswkbptr.cpp:53
Defines a QGIS exception class.
Definition: qgsexception.h:34
Snapped to an area.
QgsPointLocator_VisitorEdgesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
bool isValid() const
Returns whether this CRS is correctly initialized and usable.