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