QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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"
25
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  Q_FOREACH ( const QgsPointLocator::Match &m, _geometrySegmentsInRect( geom, mSrcRect, mLocator->mLayer, id ) )
404  {
405  // in range queries the filter may reject some matches
406  if ( mFilter && !mFilter->acceptMatch( m ) )
407  continue;
408
409  mList << m;
410  }
411  }
412
413  private:
414  QgsPointLocator *mLocator = nullptr;
416  QgsRectangle mSrcRect;
417  QgsPointLocator::MatchFilter *mFilter = nullptr;
418 };
419
421
429 {
430  public:
433  : mLocator( pl )
434  , mList( lst )
435  , mSrcRect( srcRect )
436  , mFilter( filter )
437  {}
438
439  void visitNode( const INode &n ) override { Q_UNUSED( n ); }
440  void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
441
442  void visitData( const IData &d ) override
443  {
444  QgsFeatureId id = d.getIdentifier();
445  const QgsGeometry *geom = mLocator->mGeoms.value( id );
446
447  for ( QgsAbstractGeometry::vertex_iterator it = geom->vertices_begin(); it != geom->vertices_end(); ++it )
448  {
449  if ( mSrcRect.contains( *it ) )
450  {
451  QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, 0, *it, geom->vertexNrFromVertexId( it.vertexId() ) );
452
453  // in range queries the filter may reject some matches
454  if ( mFilter && !mFilter->acceptMatch( m ) )
455  continue;
456
457  mList << m;
458  }
459  }
460  }
461
462  private:
463  QgsPointLocator *mLocator = nullptr;
465  QgsRectangle mSrcRect;
466  QgsPointLocator::MatchFilter *mFilter = nullptr;
467 };
468
469
471 #include <QStack>
472
478 class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy
479 {
480  private:
481  QStack<id_type> ids;
482
483  public:
484
485  void getNextEntry( const IEntry &entry, id_type &nextEntry, bool &hasNext ) override
486  {
487  const INode *n = dynamic_cast<const INode *>( &entry );
488  if ( !n )
489  return;
490
491  QgsDebugMsgLevel( QStringLiteral( "NODE: %1" ).arg( n->getIdentifier() ), 4 );
492  if ( n->getLevel() > 0 )
493  {
494  // inner nodes
495  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
496  {
497  QgsDebugMsgLevel( QStringLiteral( "- CH: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
498  ids.push( n->getChildIdentifier( cChild ) );
499  }
500  }
501  else
502  {
503  // leaves
504  for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
505  {
506  QgsDebugMsgLevel( QStringLiteral( "- L: %1" ).arg( n->getChildIdentifier( cChild ) ), 4 );
507  }
508  }
509
510  if ( ! ids.empty() )
511  {
512  nextEntry = ids.back();
513  ids.pop();
514  hasNext = true;
515  }
516  else
517  hasNext = false;
518  }
519 };
520
522
523
525  : mLayer( layer )
526 {
527  if ( destCRS.isValid() )
528  {
529  mTransform = QgsCoordinateTransform( layer->crs(), destCRS, transformContext );
530  }
531
532  setExtent( extent );
533
534  mStorage.reset( StorageManager::createNewMemoryStorageManager() );
535
536  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
537  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
538  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
539  connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
541 }
542
543
545 {
546  destroyIndex();
547 }
548
550 {
551  return mTransform.isValid() ? mTransform.destinationCrs() : QgsCoordinateReferenceSystem();
552 }
553
555 {
556  mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
557
558  destroyIndex();
559 }
560
562 {
563  disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
564
565  destroyIndex();
566  mContext.reset( nullptr );
567
568  if ( context )
569  {
570  mContext = std::unique_ptr<QgsRenderContext>( new QgsRenderContext( *context ) );
572  }
573
574 }
575
576 bool QgsPointLocator::init( int maxFeaturesToIndex )
577 {
578  return hasIndex() ? true : rebuildIndex( maxFeaturesToIndex );
579 }
580
581
583 {
584  return mRTree || mIsEmptyLayer;
585 }
586
587
588 bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
589 {
590  destroyIndex();
591
592  QLinkedList<RTree::Data *> dataList;
593  QgsFeature f;
594  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
595  if ( geomType == QgsWkbTypes::NullGeometry )
596  return true; // nothing to index
597
598  QgsFeatureRequest request;
599  request.setNoAttributes();
600
601  if ( mExtent )
602  {
603  QgsRectangle rect = *mExtent;
604  if ( mTransform.isValid() )
605  {
606  try
607  {
609  }
610  catch ( const QgsException &e )
611  {
612  Q_UNUSED( e );
613  // See https://issues.qgis.org/issues/12634
614  QgsDebugMsg( QStringLiteral( "could not transform bounding box to map, skipping the snap filter (%1)" ).arg( e.what() ) );
615  }
616  }
617  request.setFilterRect( rect );
618  }
619
620  bool filter = false;
621  std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
622  QgsRenderContext *ctx = nullptr;
623  if ( mContext )
624  {
625  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
626  ctx = mContext.get();
627  if ( renderer )
628  {
629  // setup scale for scale dependent visibility (rule based)
630  renderer->startRender( *ctx, mLayer->fields() );
631  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
632  request.setSubsetOfAttributes( renderer->usedAttributes( *ctx ), mLayer->fields() );
633  }
634  }
635
636  QgsFeatureIterator fi = mLayer->getFeatures( request );
637  int indexedCount = 0;
638
639  while ( fi.nextFeature( f ) )
640  {
641  if ( !f.hasGeometry() )
642  continue;
643
644  if ( filter && ctx && renderer )
645  {
646  ctx->expressionContext().setFeature( f );
647  if ( !renderer->willRenderFeature( f, *ctx ) )
648  {
649  continue;
650  }
651  }
652
653  if ( mTransform.isValid() )
654  {
655  try
656  {
657  QgsGeometry transformedGeometry = f.geometry();
658  transformedGeometry.transform( mTransform );
659  f.setGeometry( transformedGeometry );
660  }
661  catch ( const QgsException &e )
662  {
663  Q_UNUSED( e );
664  // See https://issues.qgis.org/issues/12634
665  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
666  continue;
667  }
668  }
669
670  const QgsRectangle bbox = f.geometry().boundingBox();
671  if ( bbox.isFinite() )
672  {
673  SpatialIndex::Region r( rect2region( bbox ) );
674  dataList << new RTree::Data( 0, nullptr, r, f.id() );
675
676  if ( mGeoms.contains( f.id() ) )
677  delete mGeoms.take( f.id() );
678  mGeoms[f.id()] = new QgsGeometry( f.geometry() );
679  ++indexedCount;
680  }
681
682  if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
683  {
684  qDeleteAll( dataList );
685  destroyIndex();
686  return false;
687  }
688  }
689
690  // R-Tree parameters
691  double fillFactor = 0.7;
692  unsigned long indexCapacity = 10;
693  unsigned long leafCapacity = 10;
694  unsigned long dimension = 2;
695  RTree::RTreeVariant variant = RTree::RV_RSTAR;
696  SpatialIndex::id_type indexId;
697
698  if ( dataList.isEmpty() )
699  {
700  mIsEmptyLayer = true;
701  return true; // no features
702  }
703
704  QgsPointLocator_Stream stream( dataList );
705  mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
706  leafCapacity, dimension, variant, indexId ) );
707
708  if ( ctx && renderer )
709  {
710  renderer->stopRender( *ctx );
711  }
712  return true;
713 }
714
715
717 {
718  mRTree.reset();
719
720  mIsEmptyLayer = false;
721
722  qDeleteAll( mGeoms );
723
724  mGeoms.clear();
725 }
726
727 void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
728 {
729  if ( !mRTree )
730  {
731  if ( mIsEmptyLayer )
732  rebuildIndex(); // first feature - let's built the index
733  return; // nothing to do if we are not initialized yet
734  }
735
736  QgsFeature f;
737  if ( mLayer->getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f ) )
738  {
739  if ( !f.hasGeometry() )
740  return;
741
742  if ( mContext )
743  {
744  std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
745  QgsRenderContext *ctx = nullptr;
746
747  mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
748  ctx = mContext.get();
749  if ( renderer && ctx )
750  {
751  bool pass = false;
752  renderer->startRender( *ctx, mLayer->fields() );
753
754  ctx->expressionContext().setFeature( f );
755  if ( !renderer->willRenderFeature( f, *ctx ) )
756  {
757  pass = true;
758  }
759
760  renderer->stopRender( *ctx );
761  if ( pass )
762  return;
763  }
764  }
765
766  if ( mTransform.isValid() )
767  {
768  try
769  {
770  QgsGeometry transformedGeom = f.geometry();
771  transformedGeom.transform( mTransform );
772  f.setGeometry( transformedGeom );
773  }
774  catch ( const QgsException &e )
775  {
776  Q_UNUSED( e );
777  // See https://issues.qgis.org/issues/12634
778  QgsDebugMsg( QStringLiteral( "could not transform geometry to map, skipping the snap for it (%1)" ).arg( e.what() ) );
779  return;
780  }
781  }
782
783  const QgsRectangle bbox = f.geometry().boundingBox();
784  if ( bbox.isFinite() )
785  {
786  SpatialIndex::Region r( rect2region( bbox ) );
787  mRTree->insertData( 0, nullptr, r, f.id() );
788
789  if ( mGeoms.contains( f.id() ) )
790  delete mGeoms.take( f.id() );
791  mGeoms[fid] = new QgsGeometry( f.geometry() );
792  }
793  }
794 }
795
796 void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
797 {
798  if ( !mRTree )
799  return; // nothing to do if we are not initialized yet
800
801  if ( mGeoms.contains( fid ) )
802  {
803  mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
804  delete mGeoms.take( fid );
805  }
806
807 }
808
809 void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
810 {
811  Q_UNUSED( geom );
812  onFeatureDeleted( fid );
813  onFeatureAdded( fid );
814 }
815
816 void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
817 {
818  Q_UNUSED( idx );
819  Q_UNUSED( value );
820  if ( mContext )
821  {
822  onFeatureDeleted( fid );
823  onFeatureAdded( fid );
824  }
825 }
826
827
829 {
830  if ( !mRTree )
831  {
832  init();
833  if ( !mRTree ) // still invalid?
834  return Match();
835  }
836
837  Match m;
838  QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
839  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
840  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
841  if ( m.isValid() && m.distance() > tolerance )
842  return Match(); // make sure that only match strictly within the tolerance is returned
843  return m;
844 }
845
847 {
848  if ( !mRTree )
849  {
850  init();
851  if ( !mRTree ) // still invalid?
852  return Match();
853  }
854
855  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
856  if ( geomType == QgsWkbTypes::PointGeometry )
857  return Match();
858
859  Match m;
860  QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
861  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
862  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
863  if ( m.isValid() && m.distance() > tolerance )
864  return Match(); // make sure that only match strictly within the tolerance is returned
865  return m;
866 }
867
869 {
870  if ( !mRTree )
871  {
872  init();
873  if ( !mRTree ) // still invalid?
874  return Match();
875  }
876
877  MatchList mlist = pointInPolygon( point );
878  if ( mlist.count() && mlist.at( 0 ).isValid() )
879  {
880  return mlist.at( 0 );
881  }
882
883  if ( tolerance == 0 )
884  {
885  return Match();
886  }
887
888  // discard point and line layers to keep only polygons
889  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
890  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
891  return Match();
892
893  // use edges for adding tolerance
894  Match m = nearestEdge( point, tolerance, filter );
895  if ( m.isValid() )
896  return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
897  else
898  return Match();
899 }
900
901
903 {
904  if ( !mRTree )
905  {
906  init();
907  if ( !mRTree ) // still invalid?
908  return MatchList();
909  }
910
911  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
912  if ( geomType == QgsWkbTypes::PointGeometry )
913  return MatchList();
914
915  MatchList lst;
916  QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
917  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
918
919  return lst;
920 }
921
923 {
924  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
925  return edgesInRect( rect, filter );
926 }
927
929 {
930  if ( !mRTree )
931  {
932  init();
933  if ( !mRTree ) // still invalid?
934  return MatchList();
935  }
936
937  MatchList lst;
938  QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
939  mRTree->intersectsWithQuery( rect2region( rect ), visitor );
940
941  return lst;
942 }
943
945 {
946  QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
947  return verticesInRect( rect, filter );
948 }
949
950
952 {
953  if ( !mRTree )
954  {
955  init();
956  if ( !mRTree ) // still invalid?
957  return MatchList();
958  }
959
960  QgsWkbTypes::GeometryType geomType = mLayer->geometryType();
961  if ( geomType == QgsWkbTypes::PointGeometry || geomType == QgsWkbTypes::LineGeometry )
962  return MatchList();
963
964  MatchList lst;
965  QgsPointLocator_VisitorArea visitor( this, point, lst );
966  mRTree->intersectsWithQuery( point2point( point ), visitor );
967  return lst;
968 }
#define LEFT(x)
Definition: priorityqueue.h:38
The class defines interface for querying point location:
QgsFeatureId id
Definition: qgsfeature.h:64
QgsPointLocator_Stream(const QLinkedList< RTree::Data * > &dataList)
Wrapper for iterator of features from vector data provider or vector layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords...
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry...
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.
uint32_t size() override
void visitData(const IData &d) override
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Definition: qgsrectangle.h:515
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:171
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ...
Definition: qgsrenderer.h:244
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 ...
void visitData(std::vector< const IData * > &v) override
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
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:106
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 ...
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
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.
void visitData(std::vector< const IData * > &v) override
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
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 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. ...
void styleChanged()
Signal emitted whenever a change affects the layer&#39;s style.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
const QgsRectangle * extent() const
Gets extent of the area point locator covers - if null then it caches the whole layer.
~QgsPointLocator() override
QgsFields fields() const FINAL
Returns the list of fields of this layer.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
#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 null, 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.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Is 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 null, it will index only that area.
Helper class used when traversing the index with areas - builds a list of matches.
QgsFeatureRenderer * renderer()
Returns renderer.
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Is 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.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
Contains information about the context in which a coordinate transform is executed.
The vertex_iterator class provides STL-style iterator for vertices.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units...
void visitData(const IData &d) override
Helper class used when traversing the index looking for vertices - builds a list of matches...
void visitNode(const INode &n) override
double x
Definition: qgspointxy.h:47
IData * getNext() override
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:138
QgsPointLocator(QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), const QgsRectangle *extent=nullptr)
Construct point locator for a layer.
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString what() const
Definition: qgsexception.h:48
Contains information about the context of a rendering operation.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry...
MatchList verticesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr)
Find vertices within a specified recangle Optional filter may discard unwanted matches.
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.
#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.
QgsGeometry convertToType(QgsWkbTypes::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
void visitData(std::vector< const IData * > &v) override
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
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
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.
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query 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
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr)
Find edges within a specified recangle Optional filter may discard unwanted matches.
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.
bool nextFeature(QgsFeature &f)
bool hasIndex() const
Indicate whether the data have been already indexed.
Represents a vector layer which manages a vector based data sets.
QgsCoordinateReferenceSystem destinationCrs() const
Gets destination CRS - may be an invalid QgsCoordinateReferenceSystem if not doing OTF reprojection...
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
Defines a QGIS exception class.
Definition: qgsexception.h:34
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
void visitData(const IData &d) override
Snapped to an area.
void visitData(std::vector< const IData * > &v) override
void visitData(std::vector< const IData * > &v) override
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:70
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 isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...