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