QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 "qgsfeatureiterator.h"
19 #include "qgsgeometry.h"
20 #include "qgsvectorlayer.h"
21 #include "qgswkbptr.h"
22 #include "qgis.h"
23 #include "qgslogger.h"
24 #include "qgsrenderer.h"
26 #include "qgslinestring.h"
27 #include <spatialindex/SpatialIndex.h>
28 
29 #include <QLinkedListIterator>
30 
31 using namespace SpatialIndex;
32 
33 
34 
35 static SpatialIndex::Point point2point( const QgsPointXY &point )
36 {
37  double plow[2] = { point.x(), point.y() };
38  return Point( plow, 2 );
39 }
40 
41 
42 static SpatialIndex::Region rect2region( const QgsRectangle &rect )
43 {
44  double pLow[2] = { rect.xMinimum(), rect.yMinimum() };
45  double pHigh[2] = { rect.xMaximum(), 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 
64 class QgsPointLocator_Stream : public IDataStream
65 {
66  public:
67  explicit QgsPointLocator_Stream( const QLinkedList<RTree::Data *> &dataList )
68  : mDataList( dataList )
69  , mIt( mDataList )
70  { }
71 
72  IData *getNext() override { return mIt.next(); }
73  bool hasNext() override { return mIt.hasNext(); }
74 
75  uint32_t size() override { Q_ASSERT( false && "not available" ); return 0; }
76  void rewind() override { Q_ASSERT( false && "not available" ); }
77 
78  private:
79  QLinkedList<RTree::Data *> mDataList;
80  QLinkedListIterator<RTree::Data *> mIt;
81 };
82 
83 
85 
86 
92 class QgsPointLocator_VisitorNearestVertex : public IVisitor
93 {
94  public:
96  : mLocator( pl )
97  , mBest( m )
98  , mSrcPoint( srcPoint )
99  , mFilter( filter )
100  {}
101 
102  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
103  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
104 
105  void visitData( const IData &d ) override
106  {
107  QgsFeatureId id = d.getIdentifier();
108  QgsGeometry *geom = mLocator->mGeoms.value( id );
109  int vertexIndex, beforeVertex, afterVertex;
110  double sqrDist;
111 
112  QgsPointXY pt = geom->closestVertex( mSrcPoint, vertexIndex, beforeVertex, afterVertex, sqrDist );
113  if ( sqrDist < 0 )
114  return; // probably empty geometry
115 
116  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, vertexIndex );
117  // in range queries the filter may reject some matches
118  if ( mFilter && !mFilter->acceptMatch( m ) )
119  return;
120 
121  if ( !mBest.isValid() || m.distance() < mBest.distance() )
122  mBest = m;
123  }
124 
125  private:
126  QgsPointLocator *mLocator = nullptr;
127  QgsPointLocator::Match &mBest;
128  QgsPointXY mSrcPoint;
129  QgsPointLocator::MatchFilter *mFilter = nullptr;
130 };
131 
132 
134 
135 
141 class QgsPointLocator_VisitorNearestEdge : public IVisitor
142 {
143  public:
145  : mLocator( pl )
146  , mBest( m )
147  , mSrcPoint( srcPoint )
148  , mFilter( filter )
149  {}
150 
151  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
152  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
153 
154  void visitData( const IData &d ) override
155  {
156  QgsFeatureId id = d.getIdentifier();
157  QgsGeometry *geom = mLocator->mGeoms.value( id );
158  QgsPointXY pt;
159  int afterVertex;
160  double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
161  if ( sqrDist < 0 )
162  return;
163 
164  QgsPointXY edgePoints[2];
165  edgePoints[0] = geom->vertexAt( afterVertex - 1 );
166  edgePoints[1] = geom->vertexAt( afterVertex );
167  QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, afterVertex - 1, edgePoints );
168  // in range queries the filter may reject some matches
169  if ( mFilter && !mFilter->acceptMatch( m ) )
170  return;
171 
172  if ( !mBest.isValid() || m.distance() < mBest.distance() )
173  mBest = m;
174  }
175 
176  private:
177  QgsPointLocator *mLocator = nullptr;
178  QgsPointLocator::Match &mBest;
179  QgsPointXY mSrcPoint;
180  QgsPointLocator::MatchFilter *mFilter = nullptr;
181 };
182 
183 
185 
191 class QgsPointLocator_VisitorArea : public IVisitor
192 {
193  public:
196  : mLocator( pl )
197  , mList( list )
198  , mGeomPt( QgsGeometry::fromPointXY( origPt ) )
199  {}
200 
201  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
202  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
203 
204  void visitData( const IData &d ) override
205  {
206  QgsFeatureId id = d.getIdentifier();
207  QgsGeometry *g = mLocator->mGeoms.value( id );
208  if ( g->intersects( mGeomPt ) )
209  mList << QgsPointLocator::Match( QgsPointLocator::Area, mLocator->mLayer, id, 0, mGeomPt.asPoint() );
210  }
211  private:
212  QgsPointLocator *mLocator = nullptr;
214  QgsGeometry mGeomPt;
215 };
216 
217 
219 
220 // code adapted from
221 // http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
223 {
224  explicit _CohenSutherland( const QgsRectangle &rect ) : mRect( rect ) {}
225 
226  typedef int OutCode;
227 
228  static const int INSIDE = 0; // 0000
229  static const int LEFT = 1; // 0001
230  static const int RIGHT = 2; // 0010
231  static const int BOTTOM = 4; // 0100
232  static const int TOP = 8; // 1000
233 
235 
236  OutCode computeOutCode( double x, double y )
237  {
238  OutCode code = INSIDE; // initialized as being inside of clip window
239  if ( x < mRect.xMinimum() ) // to the left of clip window
240  code |= LEFT;
241  else if ( x > mRect.xMaximum() ) // to the right of clip window
242  code |= RIGHT;
243  if ( y < mRect.yMinimum() ) // below the clip window
244  code |= BOTTOM;
245  else if ( y > mRect.yMaximum() ) // above the clip window
246  code |= TOP;
247  return code;
248  }
249 
250  bool isSegmentInRect( double x0, double y0, double x1, double y1 )
251  {
252  // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
253  OutCode outcode0 = computeOutCode( x0, y0 );
254  OutCode outcode1 = computeOutCode( x1, y1 );
255  bool accept = false;
256 
257  while ( true )
258  {
259  if ( !( outcode0 | outcode1 ) )
260  {
261  // Bitwise OR is 0. Trivially accept and get out of loop
262  accept = true;
263  break;
264  }
265  else if ( outcode0 & outcode1 )
266  {
267  // Bitwise AND is not 0. Trivially reject and get out of loop
268  break;
269  }
270  else
271  {
272  // failed both tests, so calculate the line segment to clip
273  // from an outside point to an intersection with clip edge
274  double x, y;
275 
276  // At least one endpoint is outside the clip rectangle; pick it.
277  OutCode outcodeOut = outcode0 ? outcode0 : outcode1;
278 
279  // Now find the intersection point;
280  // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
281  if ( outcodeOut & TOP )
282  {
283  // point is above the clip rectangle
284  x = x0 + ( x1 - x0 ) * ( mRect.yMaximum() - y0 ) / ( y1 - y0 );
285  y = mRect.yMaximum();
286  }
287  else if ( outcodeOut & BOTTOM )
288  {
289  // point is below the clip rectangle
290  x = x0 + ( x1 - x0 ) * ( mRect.yMinimum() - y0 ) / ( y1 - y0 );
291  y = mRect.yMinimum();
292  }
293  else if ( outcodeOut & RIGHT )
294  {
295  // point is to the right of clip rectangle
296  y = y0 + ( y1 - y0 ) * ( mRect.xMaximum() - x0 ) / ( x1 - x0 );
297  x = mRect.xMaximum();
298  }
299  else if ( outcodeOut & LEFT )
300  {
301  // point is to the left of clip rectangle
302  y = y0 + ( y1 - y0 ) * ( mRect.xMinimum() - x0 ) / ( x1 - x0 );
303  x = mRect.xMinimum();
304  }
305  else
306  break;
307 
308  // Now we move outside point to intersection point to clip
309  // and get ready for next pass.
310  if ( outcodeOut == outcode0 )
311  {
312  x0 = x;
313  y0 = y;
314  outcode0 = computeOutCode( x0, y0 );
315  }
316  else
317  {
318  x1 = x;
319  y1 = y;
320  outcode1 = computeOutCode( x1, y1 );
321  }
322  }
323  }
324  return accept;
325  }
326 };
327 
328 
329 static QgsPointLocator::MatchList _geometrySegmentsInRect( QgsGeometry *geom, const QgsRectangle &rect, QgsVectorLayer *vl, QgsFeatureId fid )
330 {
331  // this code is stupidly based on QgsGeometry::closestSegmentWithContext
332  // we need iterator for segments...
333 
335 
336  // geom is converted to a MultiCurve
337  QgsGeometry straightGeom = geom->convertToType( QgsWkbTypes::LineGeometry, true );
338  // and convert to straight segemnt / converts curve to linestring
339  straightGeom.convertToStraightSegment();
340 
341  // so, you must have multilinestring
342  //
343  // Special case: Intersections cannot be done on an empty linestring like
344  // QgsGeometry(QgsLineString()) or QgsGeometry::fromWkt("LINESTRING EMPTY")
345  if ( straightGeom.isEmpty() || ( ( straightGeom.type() != QgsWkbTypes::LineGeometry ) && ( !straightGeom.isMultipart() ) ) )
346  return lst;
347 
348  _CohenSutherland cs( rect );
349 
350  int pointIndex = 0;
351  for ( auto part = straightGeom.const_parts_begin(); part != straightGeom.const_parts_end(); ++part )
352  {
353  // Checking for invalid linestrings
354  // A linestring should/(must?) have at least two points
355  if ( qgsgeometry_cast<QgsLineString *>( *part )->numPoints() < 2 )
356  continue;
357 
358  QgsAbstractGeometry::vertex_iterator it = ( *part )->vertices_begin();
359  QgsPointXY prevPoint( *it );
360  it++;
361  while ( it != ( *part )->vertices_end() )
362  {
363  QgsPointXY thisPoint( *it );
364  if ( cs.isSegmentInRect( prevPoint.x(), prevPoint.y(), thisPoint.x(), thisPoint.y() ) )
365  {
366  QgsPointXY edgePoints[2];
367  edgePoints[0] = prevPoint;
368  edgePoints[1] = thisPoint;
369  lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
370  }
371  prevPoint = QgsPointXY( *it );
372  it++;
373  pointIndex += 1;
374 
375  }
376  }
377  return lst;
378 }
379 
385 class QgsPointLocator_VisitorEdgesInRect : public IVisitor
386 {
387  public:
389  : mLocator( pl )
390  , mList( lst )
391  , mSrcRect( srcRect )
392  , mFilter( filter )
393  {}
394 
395  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
396  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
397 
398  void visitData( const IData &d ) override
399  {
400  QgsFeatureId id = d.getIdentifier();
401  QgsGeometry *geom = mLocator->mGeoms.value( id );
402 
403  const auto segmentsInRect {_geometrySegmentsInRect( geom, mSrcRect, mLocator->mLayer, id )};
404  for ( const QgsPointLocator::Match &m : segmentsInRect )
405  {
406  // in range queries the filter may reject some matches
407  if ( mFilter && !mFilter->acceptMatch( m ) )
408  continue;
409 
410  mList << m;
411  }
412  }
413 
414  private:
415  QgsPointLocator *mLocator = nullptr;
417  QgsRectangle mSrcRect;
418  QgsPointLocator::MatchFilter *mFilter = nullptr;
419 };
420 
422 
430 {
431  public:
434  : mLocator( pl )
435  , mList( lst )
436  , mSrcRect( srcRect )
437  , mFilter( filter )
438  {}
439 
440  void visitNode( const INode &n ) override { Q_UNUSED( n ) }
441  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
442 
443  void visitData( const IData &d ) override
444  {
445  QgsFeatureId id = d.getIdentifier();
446  const QgsGeometry *geom = mLocator->mGeoms.value( id );
447 
448  for ( QgsAbstractGeometry::vertex_iterator it = geom->vertices_begin(); it != geom->vertices_end(); ++it )
449  {
450  if ( mSrcRect.contains( *it ) )
451  {
452  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, 0, *it, geom->vertexNrFromVertexId( it.vertexId() ) );
453 
454  // in range queries the filter may reject some matches
455  if ( mFilter && !mFilter->acceptMatch( m ) )
456  continue;
457 
458  mList << m;
459  }
460  }
461  }
462 
463  private:
464  QgsPointLocator *mLocator = nullptr;
466  QgsRectangle mSrcRect;
467  QgsPointLocator::MatchFilter *mFilter = nullptr;
468 };
469 
470 
472 #include <QStack>
473 
479 class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy
480 {
481  private:
482  QStack<id_type> ids;
483 
484  public:
485 
486  void getNextEntry( const IEntry &entry, id_type &nextEntry, bool &hasNext ) override
487  {
488  const INode *n = dynamic_cast<const INode *>( &entry );
489  if ( !n )
490  return;
491 
492  QgsDebugMsgLevel( QStringLiteral( "NODE: %1" ).arg( n->getIdentifier() ), 4 );
493  if ( n->getLevel() > 0 )
494  {
495  // inner nodes
496  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
497  {
498  QgsDebugMsgLevel( QStringLiteral( "- CH: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
499  ids.push( n->getChildIdentifier( cChild ) );
500  }
501  }
502  else
503  {
504  // leaves
505  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
506  {
507  QgsDebugMsgLevel( QStringLiteral( "- L: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
508  }
509  }
510 
511  if ( ! ids.empty() )
512  {
513  nextEntry = ids.back();
514  ids.pop();
515  hasNext = true;
516  }
517  else
518  hasNext = false;
519  }
520 };
521 
523 
524 
526  : mLayer( layer )
527 {
528  if ( destCRS.isValid() )
529  {
530  mTransform = QgsCoordinateTransform( layer->crs(), destCRS, transformContext );
531  }
532 
533  setExtent( extent );
534 
535  mStorage.reset( StorageManager::createNewMemoryStorageManager() );
536 
537  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
538  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
539  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
540  connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
542 }
543 
544 
546 {
547  destroyIndex();
548 }
549 
551 {
552  return mTransform.isValid() ? mTransform.destinationCrs() : QgsCoordinateReferenceSystem();
553 }
554 
556 {
557  mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
558 
559  destroyIndex();
560 }
561 
563 {
564  disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
565 
566  destroyIndex();
567  mContext.reset( nullptr );
568 
569  if ( context )
570  {
571  mContext = std::unique_ptr<QgsRenderContext>( new QgsRenderContext( *context ) );
573  }
574 
575 }
576 
577 bool QgsPointLocator::init( int maxFeaturesToIndex )
578 {
579  return hasIndex() ? true : rebuildIndex( maxFeaturesToIndex );
580 }
581 
582 
584 {
585  return mRTree || mIsEmptyLayer;
586 }
587 
588 
589 bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
590 {
591  destroyIndex();
592 
593  QLinkedList<RTree::Data *> dataList;
594  QgsFeature f;
595  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
596  if ( geomType == QgsWkbTypes::NullGeometry )
597  return true; // nothing to index
598 
599  QgsFeatureRequest request;
600  request.setNoAttributes();
601 
602  if ( mExtent )
603  {
604  QgsRectangle rect = *mExtent;
605  if ( mTransform.isValid() )
606  {
607  try
608  {
610  }
611  catch ( const QgsException &e )
612  {
613  Q_UNUSED( e )
614  // See https://github.com/qgis/QGIS/issues/20749
615  QgsDebugMsg( QStringLiteral( "could not transform bounding box to map, skipping the snap filter (%1)" ).arg( e.what() ) );
616  }
617  }
618  request.setFilterRect( rect );
619  }
620 
621  bool filter = false;
622  std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
623  QgsRenderContext *ctx = nullptr;
624  if ( mContext )
625  {
626  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
627  ctx = mContext.get();
628  if ( renderer )
629  {
630  // setup scale for scale dependent visibility (rule based)
631  renderer->startRender( *ctx, mLayer->fields() );
632  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
633  request.setSubsetOfAttributes( renderer->usedAttributes( *ctx ), mLayer->fields() );
634  }
635  }
636 
637  QgsFeatureIterator fi = mLayer->getFeatures( request );
638  int indexedCount = 0;
639 
640  while ( fi.nextFeature( f ) )
641  {
642  if ( !f.hasGeometry() )
643  continue;
644 
645  if ( filter && ctx && renderer )
646  {
647  ctx->expressionContext().setFeature( f );
648  if ( !renderer->willRenderFeature( f, *ctx ) )
649  {
650  continue;
651  }
652  }
653 
654  if ( mTransform.isValid() )
655  {
656  try
657  {
658  QgsGeometry transformedGeometry = f.geometry();
659  transformedGeometry.transform( mTransform );
660  f.setGeometry( transformedGeometry );
661  }
662  catch ( const QgsException &e )
663  {
664  Q_UNUSED( e )
665  // See https://github.com/qgis/QGIS/issues/20749
666  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
667  continue;
668  }
669  }
670 
671  const QgsRectangle bbox = f.geometry().boundingBox();
672  if ( bbox.isFinite() )
673  {
674  SpatialIndex::Region r( rect2region( bbox ) );
675  dataList << new RTree::Data( 0, nullptr, r, f.id() );
676 
677  if ( mGeoms.contains( f.id() ) )
678  delete mGeoms.take( f.id() );
679  mGeoms[f.id()] = new QgsGeometry( f.geometry() );
680  ++indexedCount;
681  }
682 
683  if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
684  {
685  qDeleteAll( dataList );
686  destroyIndex();
687  return false;
688  }
689  }
690 
691  // R-Tree parameters
692  double fillFactor = 0.7;
693  unsigned long indexCapacity = 10;
694  unsigned long leafCapacity = 10;
695  unsigned long dimension = 2;
696  RTree::RTreeVariant variant = RTree::RV_RSTAR;
697  SpatialIndex::id_type indexId;
698 
699  if ( dataList.isEmpty() )
700  {
701  mIsEmptyLayer = true;
702  return true; // no features
703  }
704 
705  QgsPointLocator_Stream stream( dataList );
706  mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
707  leafCapacity, dimension, variant, indexId ) );
708 
709  if ( ctx && renderer )
710  {
711  renderer->stopRender( *ctx );
712  }
713  return true;
714 }
715 
716 
718 {
719  mRTree.reset();
720 
721  mIsEmptyLayer = false;
722 
723  qDeleteAll( mGeoms );
724 
725  mGeoms.clear();
726 }
727 
728 void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
729 {
730  if ( !mRTree )
731  {
732  if ( mIsEmptyLayer )
733  rebuildIndex(); // first feature - let's built the index
734  return; // nothing to do if we are not initialized yet
735  }
736 
737  QgsFeature f;
738  if ( mLayer->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f ) )
739  {
740  if ( !f.hasGeometry() )
741  return;
742 
743  if ( mContext )
744  {
745  std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
746  QgsRenderContext *ctx = nullptr;
747 
748  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
749  ctx = mContext.get();
750  if ( renderer && ctx )
751  {
752  bool pass = false;
753  renderer->startRender( *ctx, mLayer->fields() );
754 
755  ctx->expressionContext().setFeature( f );
756  if ( !renderer->willRenderFeature( f, *ctx ) )
757  {
758  pass = true;
759  }
760 
761  renderer->stopRender( *ctx );
762  if ( pass )
763  return;
764  }
765  }
766 
767  if ( mTransform.isValid() )
768  {
769  try
770  {
771  QgsGeometry transformedGeom = f.geometry();
772  transformedGeom.transform( mTransform );
773  f.setGeometry( transformedGeom );
774  }
775  catch ( const QgsException &e )
776  {
777  Q_UNUSED( e )
778  // See https://github.com/qgis/QGIS/issues/20749
779  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
780  return;
781  }
782  }
783 
784  const QgsRectangle bbox = f.geometry().boundingBox();
785  if ( bbox.isFinite() )
786  {
787  SpatialIndex::Region r( rect2region( bbox ) );
788  mRTree->insertData( 0, nullptr, r, f.id() );
789 
790  if ( mGeoms.contains( f.id() ) )
791  delete mGeoms.take( f.id() );
792  mGeoms[fid] = new QgsGeometry( f.geometry() );
793  }
794  }
795 }
796 
797 void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
798 {
799  if ( !mRTree )
800  return; // nothing to do if we are not initialized yet
801 
802  if ( mGeoms.contains( fid ) )
803  {
804  mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
805  delete mGeoms.take( fid );
806  }
807 
808 }
809 
810 void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
811 {
812  Q_UNUSED( geom )
813  onFeatureDeleted( fid );
814  onFeatureAdded( fid );
815 }
816 
817 void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
818 {
819  Q_UNUSED( idx )
820  Q_UNUSED( value )
821  if ( mContext )
822  {
823  onFeatureDeleted( fid );
824  onFeatureAdded( fid );
825  }
826 }
827 
828 
830 {
831  if ( !mRTree )
832  {
833  init();
834  if ( !mRTree ) // still invalid?
835  return Match();
836  }
837 
838  Match m;
839  QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
840  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
841  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
842  if ( m.isValid() && m.distance() > tolerance )
843  return Match(); // make sure that only match strictly within the tolerance is returned
844  return m;
845 }
846 
848 {
849  if ( !mRTree )
850  {
851  init();
852  if ( !mRTree ) // still invalid?
853  return Match();
854  }
855 
856  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
857  if ( geomType == QgsWkbTypes::PointGeometry )
858  return Match();
859 
860  Match m;
861  QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
862  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
863  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
864  if ( m.isValid() && m.distance() > tolerance )
865  return Match(); // make sure that only match strictly within the tolerance is returned
866  return m;
867 }
868 
870 {
871  if ( !mRTree )
872  {
873  init();
874  if ( !mRTree ) // still invalid?
875  return Match();
876  }
877 
878  MatchList mlist = pointInPolygon( point );
879  if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() )
880  {
881  return mlist.at( 0 );
882  }
883 
884  if ( tolerance == 0 )
885  {
886  return Match();
887  }
888 
889  // discard point and line layers to keep only polygons
890  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
891  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
892  return Match();
893 
894  // use edges for adding tolerance
895  Match m = nearestEdge( point, tolerance, filter );
896  if ( m.isValid() )
897  return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
898  else
899  return Match();
900 }
901 
902 
904 {
905  if ( !mRTree )
906  {
907  init();
908  if ( !mRTree ) // still invalid?
909  return MatchList();
910  }
911 
912  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
913  if ( geomType == QgsWkbTypes::PointGeometry )
914  return MatchList();
915 
916  MatchList lst;
917  QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
918  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
919 
920  return lst;
921 }
922 
924 {
925  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
926  return edgesInRect( rect, filter );
927 }
928 
930 {
931  if ( !mRTree )
932  {
933  init();
934  if ( !mRTree ) // still invalid?
935  return MatchList();
936  }
937 
938  MatchList lst;
939  QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
940  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
941 
942  return lst;
943 }
944 
946 {
947  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
948  return verticesInRect( rect, filter );
949 }
950 
951 
953 {
954  if ( !mRTree )
955  {
956  init();
957  if ( !mRTree ) // still invalid?
958  return MatchList();
959  }
960 
961  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
962  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
963  return MatchList();
964 
965  MatchList lst;
966  QgsPointLocator_VisitorArea visitor( this, point, lst );
967  mRTree->intersectsWithQuery( point2point( point ), visitor );
968  return lst;
969 }
#define LEFT(x)
Definition: priorityqueue.h:38
The class defines interface for querying point location:
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &afterVertex, int *leftOf=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
void visitData(std::vector< const IData *> &v) override
A rectangle specified with double values.
Definition: qgsrectangle.h:41
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
uint32_t size() override
void visitData(const IData &d) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool hasIndex() const
Indicate whether the data have been already indexed.
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ...
Definition: qgsrenderer.h:245
double y
Definition: qgspointxy.h:48
QgsPointLocator_VisitorArea(QgsPointLocator *pl, const QgsPointXY &origPt, QgsPointLocator::MatchList &list)
constructor
A class to represent a 2D point.
Definition: qgspointxy.h:43
Match nearestEdge(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest edge to the specified point - up to distance specified by tolerance Optional filter may ...
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsCoordinateReferenceSystem destinationCrs() const
Gets destination CRS - may be an invalid QgsCoordinateReferenceSystem if not doing OTF reprojection...
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsPointLocator_VisitorVerticesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
Constructs the visitor.
class QList< QgsPointLocator::Match > MatchList
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:122
Match nearestArea(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest area to the specified point - up to distance specified by tolerance Optional filter may ...
bool rebuildIndex(int maxFeaturesToIndex=-1)
void visitNode(const INode &n) override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
Interface that allows rejection of some matches in intersection queries (e.g.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
void visitData(std::vector< const IData *> &v) override
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
void convertToStraightSegment(double tolerance=M_PI/180., QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle)
Converts the geometry to straight line segments, if it is a curved geometry type. ...
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
void styleChanged()
Signal emitted whenever a change affects the layer&#39;s style.
QgsPointLocator_Stream(const QLinkedList< RTree::Data *> &dataList)
QString what() const
Definition: qgsexception.h:48
QgsPointXY closestVertex(const QgsPointXY &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 ...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
~QgsPointLocator() override
QgsFields fields() const FINAL
Returns the list of fields of this layer.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsPointLocator_VisitorNearestVertex(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
OutCode computeOutCode(double x, double y)
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
void setRenderContext(const QgsRenderContext *context)
Configure render context - if not nullptr, it will use to index only visible feature.
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)
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Emitted whenever a geometry change is done in the edit buffer.
void visitNode(const INode &n) override
void setExtent(const QgsRectangle *extent)
Configure extent - if not nullptr, it will index only that area.
Helper class used when traversing the index with areas - builds a list of matches.
QgsFeatureRenderer * renderer()
Returns renderer.
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Emitted whenever an attribute value change is done in the edit buffer.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.
Contains information about the context in which a coordinate transform is executed.
The vertex_iterator class provides STL-style iterator for vertices.
void visitData(const IData &d) override
Helper class used when traversing the index looking for vertices - builds a list of matches...
const QgsRectangle * extent() const
Gets extent of the area point locator covers - if nullptr then it caches the whole layer...
void visitData(std::vector< const IData *> &v) override
void visitNode(const INode &n) override
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Definition: qgsrectangle.h:516
double x
Definition: qgspointxy.h:47
IData * getNext() override
void visitData(std::vector< const IData *> &v) override
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:139
QgsPointLocator(QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), const QgsRectangle *extent=nullptr)
Construct point locator for a layer.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords...
Contains information about the context of a rendering operation.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
MatchList verticesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr)
Find vertices within a specified recangle Optional filter may discard unwanted matches.
void visitData(std::vector< const IData *> &v) override
Transform from destination to source CRS.
QgsPointLocator_VisitorNearestEdge(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class for bulk loading of R-trees.
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
#define RIGHT(x)
Definition: priorityqueue.h:39
void visitData(const IData &d) override
MatchList pointInPolygon(const QgsPointXY &point)
find out if the point is in any polygons
void visitNode(const INode &n) override
bool init(int maxFeaturesToIndex=-1)
Prepare the index for queries.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
_CohenSutherland(const QgsRectangle &rect)
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
void getNextEntry(const IEntry &entry, id_type &nextEntry, bool &hasNext) override
Match nearestVertex(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest vertex to the specified point - up to distance specified by tolerance Optional filter ma...
void visitData(const IData &d) override
Class for doing transforms between two map coordinate systems.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
void dataChanged()
Data of layer changed.
Helper class used when traversing the index looking for vertices - builds a list of matches...
Snapped to an edge.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units...
QgsGeometry convertToType(QgsWkbTypes::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Helper class used when traversing the index looking for edges - builds a list of matches.
void visitNode(const INode &n) override
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr)
Find edges within a specified recangle Optional filter may discard unwanted matches.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:126
QgsGeometry geometry
Definition: qgsfeature.h:67
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
bool nextFeature(QgsFeature &f)
Represents a vector layer which manages a vector based data sets.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry...
Defines a QGIS exception class.
Definition: qgsexception.h:34
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
void visitData(const IData &d) override
Snapped to an area.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsPointLocator_VisitorEdgesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
bool isValid() const
Returns whether this CRS is correctly initialized and usable.