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