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 *
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
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:
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
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() );
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  {
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  {
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
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 );
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.
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)
#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...