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