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