QGIS API Documentation 4.1.0-Master (60fea48833c)
Loading...
Searching...
No Matches
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 <spatialindex/SpatialIndex.h>
19
20#include "qgis.h"
21#include "qgsapplication.h"
22#include "qgscurvepolygon.h"
24#include "qgsfeatureiterator.h"
25#include "qgsgeometry.h"
26#include "qgslinestring.h"
27#include "qgslogger.h"
29#include "qgsrendercontext.h"
30#include "qgsrenderer.h"
32#include "qgsvectorlayer.h"
34#include "qgswkbptr.h"
35
36#include <QString>
37
38#include "moc_qgspointlocator.cpp"
39
40using namespace Qt::StringLiterals;
41
42using namespace SpatialIndex;
43
44
45static SpatialIndex::Point point2point( const QgsPointXY &point )
46{
47 double plow[2] = { point.x(), point.y() };
48 return Point( plow, 2 );
49}
50
51
52// Ahh.... another magic number. Taken from QgsVectorLayer::snapToGeometry() call to closestSegmentWithContext().
53// The default epsilon used for sqrDistToSegment (1e-8) is too high when working with lat/lon coordinates
54// I still do not fully understand why the sqrDistToSegment() code uses epsilon and if the square distance
55// is lower than epsilon it will have a special logic...
56static const double POINT_LOC_EPSILON = 1e-12;
57
59
60
66class QgsPointLocator_Stream : public IDataStream
67{
68 public:
72 explicit QgsPointLocator_Stream( const QVector<RTree::Data *> &dataList )
73 : mDataList( dataList )
74 , mIt( mDataList )
75 {}
76
77 IData *getNext() override { return mIt.next(); }
78 bool hasNext() override { return mIt.hasNext(); }
79
80 uint32_t size() override
81 {
82 Q_ASSERT( false && "not available" );
83 return 0;
84 }
85 void rewind() override { Q_ASSERT( false && "not available" ); }
86
87 private:
88 QVector<RTree::Data *> mDataList;
89 QVectorIterator<RTree::Data *> mIt;
90};
91
92
94
95
102{
103 public:
105 : mLocator( pl )
106 , mBest( m )
107 , mSrcPoint( srcPoint )
108 , mFilter( filter )
109 {}
110
111 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
112 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
113
114 void visitData( const IData &d ) override
115 {
116 const QgsFeatureId id = d.getIdentifier();
117 QgsGeometry *geom = mLocator->mGeoms.value( id );
118 if ( !geom )
119 return; // should not happen, but be safe
120 int vertexIndex, beforeVertex, afterVertex;
121 double sqrDist;
122
123 const QgsPointXY pt = geom->closestVertex( mSrcPoint, vertexIndex, beforeVertex, afterVertex, sqrDist );
124 if ( sqrDist < 0 )
125 return; // probably empty geometry
126
127 const QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, vertexIndex );
128 // in range queries the filter may reject some matches
129 if ( mFilter && !mFilter->acceptMatch( m ) )
130 return;
131
132 if ( !mBest.isValid() || m.distance() < mBest.distance() )
133 mBest = m;
134 }
135
136 private:
137 QgsPointLocator *mLocator = nullptr;
139 QgsPointXY mSrcPoint;
140 QgsPointLocator::MatchFilter *mFilter = nullptr;
141};
142
143
151{
152 public:
160 : mLocator( pl )
161 , mBest( m )
162 , mSrcPoint( srcPoint )
163 , mFilter( filter )
164 {}
165
166 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
167 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
168
169 void visitData( const IData &d ) override
170 {
171 const QgsFeatureId id = d.getIdentifier();
172 QgsGeometry *geom = mLocator->mGeoms.value( id );
173 if ( !geom )
174 return; // should not happen, but be safe
175
176 const QgsPointXY pt = geom->centroid().asPoint();
177
178 const QgsPointLocator::Match m( QgsPointLocator::Centroid, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( pt ) ), pt, -1 );
179 // in range queries the filter may reject some matches
180 if ( mFilter && !mFilter->acceptMatch( m ) )
181 return;
182
183 if ( !mBest.isValid() || m.distance() < mBest.distance() )
184 mBest = m;
185 }
186
187 private:
188 QgsPointLocator *mLocator = nullptr;
190 QgsPointXY mSrcPoint;
191 QgsPointLocator::MatchFilter *mFilter = nullptr;
192};
193
195
203{
204 public:
212 : mLocator( pl )
213 , mBest( m )
214 , mSrcPoint( srcPoint )
215 , mFilter( filter )
216 {}
217
218 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
219 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
220
221 void visitData( const IData &d ) override
222 {
223 const QgsFeatureId id = d.getIdentifier();
224 QgsGeometry *geom = mLocator->mGeoms.value( id );
225 if ( !geom )
226 return; // should not happen, but be safe
227
228 QgsPointXY pt;
229 int afterVertex;
230 const double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
231 if ( sqrDist < 0 )
232 return;
233
234 QgsPointXY edgePoints[2];
235 edgePoints[0] = geom->vertexAt( afterVertex - 1 );
236 edgePoints[1] = geom->vertexAt( afterVertex );
237 pt = QgsPointXY( ( edgePoints[0].x() + edgePoints[1].x() ) / 2.0, ( edgePoints[0].y() + edgePoints[1].y() ) / 2.0 );
238
239 const QgsPointLocator::Match m( QgsPointLocator::MiddleOfSegment, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( pt ) ), pt, afterVertex - 1 );
240 // in range queries the filter may reject some matches
241 if ( mFilter && !mFilter->acceptMatch( m ) )
242 return;
243
244 if ( !mBest.isValid() || m.distance() < mBest.distance() )
245 mBest = m;
246 }
247
248 private:
249 QgsPointLocator *mLocator = nullptr;
251 QgsPointXY mSrcPoint;
252 QgsPointLocator::MatchFilter *mFilter = nullptr;
253};
254
256
264{
265 public:
271 : mLocator( pl )
272 , mBest( m )
273 , mSrcPoint( srcPoint )
274 , mFilter( filter )
275 {}
276
277 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
278 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
279
280 void visitData( const IData &d ) override
281 {
282 const QgsFeatureId id = d.getIdentifier();
283 const QgsGeometry *geom = mLocator->mGeoms.value( id );
284 if ( !geom )
285 return; // should not happen, but be safe
286
287 QgsPointXY bestPoint;
288 int bestVertexNumber = -1;
289 auto replaceIfBetter = [this, &bestPoint, &bestVertexNumber]( const QgsPoint &candidate, int vertexNumber ) {
290 if ( bestPoint.isEmpty() || candidate.distanceSquared( mSrcPoint.x(), mSrcPoint.y() ) < bestPoint.sqrDist( mSrcPoint ) )
291 {
292 bestPoint = QgsPointXY( candidate );
293 bestVertexNumber = vertexNumber;
294 }
295 };
296
297 switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
298 {
302 return;
303
305 {
306 int partStartVertexNum = 0;
307 for ( auto partIt = geom->const_parts_begin(); partIt != geom->const_parts_end(); ++partIt )
308 {
309 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( *partIt ) )
310 {
311 replaceIfBetter( curve->startPoint(), partStartVertexNum );
312 replaceIfBetter( curve->endPoint(), partStartVertexNum + curve->numPoints() - 1 );
313 partStartVertexNum += curve->numPoints();
314 }
315 }
316 break;
317 }
318
320 {
321 int partStartVertexNum = 0;
322 for ( auto partIt = geom->const_parts_begin(); partIt != geom->const_parts_end(); ++partIt )
323 {
324 if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( *partIt ) )
325 {
326 if ( polygon->exteriorRing() )
327 {
328 replaceIfBetter( polygon->exteriorRing()->startPoint(), partStartVertexNum );
329 partStartVertexNum += polygon->exteriorRing()->numPoints();
330 }
331 for ( int i = 0; i < polygon->numInteriorRings(); ++i )
332 {
333 const QgsCurve *ring = polygon->interiorRing( i );
334 replaceIfBetter( ring->startPoint(), partStartVertexNum );
335 partStartVertexNum += ring->numPoints();
336 }
337 }
338 }
339 break;
340 }
341 }
342
343 const QgsPointLocator::Match m( QgsPointLocator::LineEndpoint, mLocator->mLayer, id, std::sqrt( mSrcPoint.sqrDist( bestPoint ) ), bestPoint, bestVertexNumber );
344 // in range queries the filter may reject some matches
345 if ( mFilter && !mFilter->acceptMatch( m ) )
346 return;
347
348 if ( !mBest.isValid() || m.distance() < mBest.distance() )
349 mBest = m;
350 }
351
352 private:
353 QgsPointLocator *mLocator = nullptr;
355 QgsPointXY mSrcPoint;
356 QgsPointLocator::MatchFilter *mFilter = nullptr;
357};
358
360
367{
368 public:
370 : mLocator( pl )
371 , mBest( m )
372 , mSrcPoint( srcPoint )
373 , mFilter( filter )
374 {}
375
376 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
377 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
378
379 void visitData( const IData &d ) override
380 {
381 const QgsFeatureId id = d.getIdentifier();
382 QgsGeometry *geom = mLocator->mGeoms.value( id );
383 if ( !geom )
384 return; // should not happen, but be safe
385
386 QgsPointXY pt;
387 int afterVertex;
388 const double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
389 if ( sqrDist < 0 )
390 return;
391
392 QgsPointXY edgePoints[2];
393 edgePoints[0] = geom->vertexAt( afterVertex - 1 );
394 edgePoints[1] = geom->vertexAt( afterVertex );
395 const QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, afterVertex - 1, edgePoints );
396 // in range queries the filter may reject some matches
397 if ( mFilter && !mFilter->acceptMatch( m ) )
398 return;
399
400 if ( !mBest.isValid() || m.distance() < mBest.distance() )
401 mBest = m;
402 }
403
404 private:
405 QgsPointLocator *mLocator = nullptr;
407 QgsPointXY mSrcPoint;
408 QgsPointLocator::MatchFilter *mFilter = nullptr;
409};
410
411
413
419class QgsPointLocator_VisitorArea : public IVisitor
420{
421 public:
424 : mLocator( pl )
425 , mList( list )
426 , mFilter( filter )
427 , mGeomPt( QgsGeometry::fromPointXY( origPt ) )
428 {}
429
430 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
431 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
432
433 void visitData( const IData &d ) override
434 {
435 const QgsFeatureId id = d.getIdentifier();
436 QgsGeometry *g = mLocator->mGeoms.value( id );
437 if ( !g )
438 return; // should not happen, but be safe
439
440 if ( g->intersects( mGeomPt ) )
441 {
442 const QgsPointLocator::Match m( QgsPointLocator::Area, mLocator->mLayer, id, 0, mGeomPt.asPoint() );
443 if ( mFilter && !mFilter->acceptMatch( m ) )
444 return;
445 mList << m;
446 }
447 }
448
449 private:
450 QgsPointLocator *mLocator = nullptr;
452 QgsPointLocator::MatchFilter *mFilter = nullptr;
453 QgsGeometry mGeomPt;
454};
455
456
458
459// code adapted from
460// http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
462{
463 explicit _CohenSutherland( const QgsRectangle &rect )
464 : mRect( rect )
465 {}
466
467 typedef int OutCode;
468
469 static const int INSIDE = 0; // 0000
470 static const int LEFT = 1; // 0001
471 static const int RIGHT = 2; // 0010
472 static const int BOTTOM = 4; // 0100
473 static const int TOP = 8; // 1000
474
476
477 OutCode computeOutCode( double x, double y )
478 {
479 OutCode code = INSIDE; // initialized as being inside of clip window
480 if ( x < mRect.xMinimum() ) // to the left of clip window
481 code |= LEFT;
482 else if ( x > mRect.xMaximum() ) // to the right of clip window
483 code |= RIGHT;
484 if ( y < mRect.yMinimum() ) // below the clip window
485 code |= BOTTOM;
486 else if ( y > mRect.yMaximum() ) // above the clip window
487 code |= TOP;
488 return code;
489 }
490
491 bool isSegmentInRect( double x0, double y0, double x1, double y1 )
492 {
493 // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
494 OutCode outcode0 = computeOutCode( x0, y0 );
495 OutCode outcode1 = computeOutCode( x1, y1 );
496 bool accept = false;
497
498 while ( true )
499 {
500 if ( !( outcode0 | outcode1 ) )
501 {
502 // Bitwise OR is 0. Trivially accept and get out of loop
503 accept = true;
504 break;
505 }
506 else if ( outcode0 & outcode1 )
507 {
508 // Bitwise AND is not 0. Trivially reject and get out of loop
509 break;
510 }
511 else
512 {
513 // failed both tests, so calculate the line segment to clip
514 // from an outside point to an intersection with clip edge
515 double x, y;
516
517 // At least one endpoint is outside the clip rectangle; pick it.
518 const OutCode outcodeOut = outcode0 ? outcode0 : outcode1;
519
520 // Now find the intersection point;
521 // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
522 if ( outcodeOut & TOP )
523 {
524 // point is above the clip rectangle
525 x = x0 + ( x1 - x0 ) * ( mRect.yMaximum() - y0 ) / ( y1 - y0 );
526 y = mRect.yMaximum();
527 }
528 else if ( outcodeOut & BOTTOM )
529 {
530 // point is below the clip rectangle
531 x = x0 + ( x1 - x0 ) * ( mRect.yMinimum() - y0 ) / ( y1 - y0 );
532 y = mRect.yMinimum();
533 }
534 else if ( outcodeOut & RIGHT )
535 {
536 // point is to the right of clip rectangle
537 y = y0 + ( y1 - y0 ) * ( mRect.xMaximum() - x0 ) / ( x1 - x0 );
538 x = mRect.xMaximum();
539 }
540 else if ( outcodeOut & LEFT )
541 {
542 // point is to the left of clip rectangle
543 y = y0 + ( y1 - y0 ) * ( mRect.xMinimum() - x0 ) / ( x1 - x0 );
544 x = mRect.xMinimum();
545 }
546 else
547 break;
548
549 // Now we move outside point to intersection point to clip
550 // and get ready for next pass.
551 if ( outcodeOut == outcode0 )
552 {
553 x0 = x;
554 y0 = y;
555 outcode0 = computeOutCode( x0, y0 );
556 }
557 else
558 {
559 x1 = x;
560 y1 = y;
561 outcode1 = computeOutCode( x1, y1 );
562 }
563 }
564 }
565 return accept;
566 }
567};
568
569
570static QgsPointLocator::MatchList _geometrySegmentsInRect( QgsGeometry *geom, const QgsRectangle &rect, QgsVectorLayer *vl, QgsFeatureId fid )
571{
572 // this code is stupidly based on QgsGeometry::closestSegmentWithContext
573 // we need iterator for segments...
574
576
577 // geom is converted to a MultiCurve
578 QgsGeometry straightGeom = geom->convertToType( Qgis::GeometryType::Line, true );
579 // and convert to straight segemnt / converts curve to linestring
580 straightGeom.convertToStraightSegment();
581
582 // so, you must have multilinestring
583 //
584 // Special case: Intersections cannot be done on an empty linestring like
585 // QgsGeometry(QgsLineString()) or QgsGeometry::fromWkt("LINESTRING EMPTY")
586 if ( straightGeom.isEmpty() || ( ( straightGeom.type() != Qgis::GeometryType::Line ) && ( !straightGeom.isMultipart() ) ) )
587 return lst;
588
589 _CohenSutherland cs( rect );
590
591 int pointIndex = 0;
592 for ( auto part = straightGeom.const_parts_begin(); part != straightGeom.const_parts_end(); ++part )
593 {
594 // Checking for invalid linestrings
595 // A linestring should/(must?) have at least two points.
596 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( *part );
597 Q_ASSERT( !curve->hasCurvedSegments() );
598 if ( curve->numPoints() < 2 )
599 continue;
600
601 QgsAbstractGeometry::vertex_iterator it = ( *part )->vertices_begin();
602 QgsPointXY prevPoint( *it );
603 it++;
604 while ( it != ( *part )->vertices_end() )
605 {
606 const QgsPointXY thisPoint( *it );
607 if ( cs.isSegmentInRect( prevPoint.x(), prevPoint.y(), thisPoint.x(), thisPoint.y() ) )
608 {
609 QgsPointXY edgePoints[2];
610 edgePoints[0] = prevPoint;
611 edgePoints[1] = thisPoint;
612 lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
613 }
614 prevPoint = QgsPointXY( *it );
615 it++;
616 pointIndex += 1;
617 }
618 }
619 return lst;
620}
621
628{
629 public:
631 : mLocator( pl )
632 , mList( lst )
633 , mSrcRect( srcRect )
634 , mFilter( filter )
635 {}
636
637 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
638 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
639
640 void visitData( const IData &d ) override
641 {
642 const QgsFeatureId id = d.getIdentifier();
643 QgsGeometry *geom = mLocator->mGeoms.value( id );
644 if ( !geom )
645 return; // should not happen, but be safe
646
647 const auto segmentsInRect { _geometrySegmentsInRect( geom, mSrcRect, mLocator->mLayer, id ) };
648 for ( const QgsPointLocator::Match &m : segmentsInRect )
649 {
650 // in range queries the filter may reject some matches
651 if ( mFilter && !mFilter->acceptMatch( m ) )
652 continue;
653
654 mList << m;
655 }
656 }
657
658 private:
659 QgsPointLocator *mLocator = nullptr;
661 QgsRectangle mSrcRect;
662 QgsPointLocator::MatchFilter *mFilter = nullptr;
663};
664
666
674{
675 public:
678 : mLocator( pl )
679 , mList( lst )
680 , mSrcRect( srcRect )
681 , mFilter( filter )
682 {}
683
684 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
685 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
686
687 void visitData( const IData &d ) override
688 {
689 const QgsFeatureId id = d.getIdentifier();
690 const QgsGeometry *geom = mLocator->mGeoms.value( id );
691 if ( !geom )
692 return; // should not happen, but be safe
693
694 for ( QgsAbstractGeometry::vertex_iterator it = geom->vertices_begin(); it != geom->vertices_end(); ++it )
695 {
696 if ( mSrcRect.contains( *it ) )
697 {
698 const QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, 0, *it, geom->vertexNrFromVertexId( it.vertexId() ) );
699
700 // in range queries the filter may reject some matches
701 if ( mFilter && !mFilter->acceptMatch( m ) )
702 continue;
703
704 mList << m;
705 }
706 }
707 }
708
709 private:
710 QgsPointLocator *mLocator = nullptr;
712 QgsRectangle mSrcRect;
713 QgsPointLocator::MatchFilter *mFilter = nullptr;
714};
715
723{
724 public:
727 : mLocator( pl )
728 , mList( lst )
729 , mSrcRect( srcRect )
730 , mFilter( filter )
731 {}
732
733 void visitNode( const INode &n ) override { Q_UNUSED( n ); }
734 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
735
736 void visitData( const IData &d ) override
737 {
738 const QgsFeatureId id = d.getIdentifier();
739 const QgsGeometry *geom = mLocator->mGeoms.value( id );
740 if ( !geom )
741 return; // should not happen, but be safe
742
743 const QgsPointXY centroid = geom->centroid().asPoint();
744 if ( mSrcRect.contains( centroid ) )
745 {
746 const QgsPointLocator::Match m( QgsPointLocator::Centroid, mLocator->mLayer, id, 0, centroid, -1 );
747
748 // in range queries the filter may reject some matches
749 if ( !( mFilter && !mFilter->acceptMatch( m ) ) )
750 mList << m;
751 }
752 }
753
754 private:
755 QgsPointLocator *mLocator = nullptr;
757 QgsRectangle mSrcRect;
758 QgsPointLocator::MatchFilter *mFilter = nullptr;
759};
760
768{
769 public:
772 : mLocator( pl )
773 , mList( lst )
774 , mSrcRect( srcRect )
775 , mFilter( filter )
776 {}
777
778 void visitNode( const INode &n ) override { Q_UNUSED( n ); }
779 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
780
781 void visitData( const IData &d ) override
782 {
783 const QgsFeatureId id = d.getIdentifier();
784 const QgsGeometry *geom = mLocator->mGeoms.value( id );
785 if ( !geom )
786 return; // should not happen, but be safe
787
788 for ( QgsAbstractGeometry::const_part_iterator itPart = geom->const_parts_begin(); itPart != geom->const_parts_end(); ++itPart )
789 {
790 QgsAbstractGeometry::vertex_iterator it = ( *itPart )->vertices_begin();
791 QgsAbstractGeometry::vertex_iterator itPrevious = ( *itPart )->vertices_begin();
792 it++;
793 for ( ; it != geom->vertices_end(); ++it, ++itPrevious )
794 {
795 const QgsPointXY pt( ( ( *itPrevious ).x() + ( *it ).x() ) / 2.0, ( ( *itPrevious ).y() + ( *it ).y() ) / 2.0 );
796 if ( mSrcRect.contains( pt ) )
797 {
798 const QgsPointLocator::Match m( QgsPointLocator::MiddleOfSegment, mLocator->mLayer, id, 0, pt, geom->vertexNrFromVertexId( it.vertexId() ) );
799
800 // in range queries the filter may reject some matches
801 if ( mFilter && !mFilter->acceptMatch( m ) )
802 continue;
803
804 mList << m;
805 }
806 }
807 }
808 }
809
810 private:
811 QgsPointLocator *mLocator = nullptr;
813 QgsRectangle mSrcRect;
814 QgsPointLocator::MatchFilter *mFilter = nullptr;
815};
816
818#include <QStack>
819#include <memory>
820
826class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy
827{
828 private:
829 QStack<id_type> ids;
830
831 public:
832 void getNextEntry( const IEntry &entry, id_type &nextEntry, bool &hasNext ) override
833 {
834 const INode *n = dynamic_cast<const INode *>( &entry );
835 if ( !n )
836 return;
837
838 QgsDebugMsgLevel( u"NODE: %1"_s.arg( n->getIdentifier() ), 4 );
839 if ( n->getLevel() > 0 )
840 {
841 // inner nodes
842 for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
843 {
844 QgsDebugMsgLevel( u"- CH: %1"_s.arg( n->getChildIdentifier( cChild ) ), 4 );
845 ids.push( n->getChildIdentifier( cChild ) );
846 }
847 }
848 else
849 {
850 // leaves
851 for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
852 {
853 QgsDebugMsgLevel( u"- L: %1"_s.arg( n->getChildIdentifier( cChild ) ), 4 );
854 }
855 }
856
857 if ( !ids.empty() )
858 {
859 nextEntry = ids.back();
860 ids.pop();
861 hasNext = true;
862 }
863 else
864 hasNext = false;
865 }
866};
867
869
870
872 : mLayer( layer )
873{
874 if ( destCRS.isValid() )
875 {
876 mTransform = QgsCoordinateTransform( layer->crs(), destCRS, transformContext );
877 }
878
879 setExtent( extent );
880
881 mStorage.reset( StorageManager::createNewMemoryStorageManager() );
882
883 connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
884 connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
885 connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
886 connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
888}
889
890
892{
893 // don't delete a locator if there is an indexing task running on it
894 mIsDestroying = true;
895 if ( mIsIndexing )
897
898 destroyIndex();
899}
900
902{
903 return mTransform.isValid() ? mTransform.destinationCrs() : QgsCoordinateReferenceSystem();
904}
905
907{
908 if ( mIsIndexing )
909 // already indexing, return!
910 return;
911
912 mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
913
914 destroyIndex();
915}
916
918{
919 if ( mIsIndexing )
920 // already indexing, return!
921 return;
922
923 disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
924
925 destroyIndex();
926 mContext.reset( nullptr );
927
928 if ( context )
929 {
930 mContext = std::make_unique<QgsRenderContext>( *context );
932 }
933}
934
935void QgsPointLocator::onInitTaskFinished()
936{
937 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsPointLocator::onInitTaskFinished", "was not called on main thread" );
938
939 // Check that we don't call this method twice, when calling waitForFinished
940 // for instance (because of taskCompleted signal)
941 if ( !mIsIndexing )
942 return;
943
944 if ( mIsDestroying )
945 return;
946
947 mIsIndexing = false;
948 mRenderer.reset();
949 mSource.reset();
950
951 // treat added and deleted feature while indexing
952 for ( const QgsFeatureId fid : std::as_const( mAddedFeatures ) )
953 onFeatureAdded( fid );
954 mAddedFeatures.clear();
955
956 for ( const QgsFeatureId fid : std::as_const( mDeletedFeatures ) )
957 onFeatureDeleted( fid );
958 mDeletedFeatures.clear();
959
960 emit initFinished( mInitTask->isBuildOK() );
961}
962
963bool QgsPointLocator::init( int maxFeaturesToIndex, bool relaxed )
964{
965 const Qgis::GeometryType geomType = mLayer->geometryType();
966 if ( geomType == Qgis::GeometryType::Null // nothing to index
967 || hasIndex()
968 || mIsIndexing ) // already indexing, return!
969 return true;
970
971 if ( !mLayer->dataProvider() || !mLayer->dataProvider()->isValid() )
972 return false;
973
974 mSource = std::make_unique<QgsVectorLayerFeatureSource>( mLayer );
975
976 if ( mContext )
977 {
978 mRenderer.reset( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
979 mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
980 }
981
982 mIsIndexing = true;
983
984 if ( relaxed )
985 {
986 mInitTask = new QgsPointLocatorInitTask( this );
987 connect( mInitTask, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskFinished );
988 connect( mInitTask, &QgsPointLocatorInitTask::taskCompleted, this, &QgsPointLocator::onInitTaskFinished );
989 QgsApplication::taskManager()->addTask( mInitTask );
990 return true;
991 }
992 else
993 {
994 const bool ok = rebuildIndex( maxFeaturesToIndex );
995 mIsIndexing = false;
996 emit initFinished( ok );
997 return ok;
998 }
999}
1000
1002{
1003 disconnect( mInitTask, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskFinished );
1004 disconnect( mInitTask, &QgsPointLocatorInitTask::taskCompleted, this, &QgsPointLocator::onInitTaskFinished );
1005 mInitTask->waitForFinished();
1006
1007 if ( !mIsDestroying )
1008 onInitTaskFinished();
1009}
1010
1012{
1013 return mIsIndexing || mRTree || mIsEmptyLayer;
1014}
1015
1016bool QgsPointLocator::prepare( bool relaxed )
1017{
1018 if ( mIsIndexing )
1019 {
1020 if ( relaxed )
1021 return false;
1022 else
1024 }
1025
1026 if ( !mRTree )
1027 {
1028 init( -1, relaxed );
1029 if ( ( relaxed && mIsIndexing ) || !mRTree ) // relaxed mode and currently indexing or still invalid?
1030 return false;
1031 }
1032
1033 return true;
1034}
1035
1036bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
1037{
1038 QElapsedTimer t;
1039 t.start();
1040
1041 QgsDebugMsgLevel( u"RebuildIndex start : %1"_s.arg( mSource->id() ), 2 );
1042
1043 destroyIndex();
1044
1045 QVector<RTree::Data *> dataList;
1046 QgsFeature f;
1047
1048 QgsFeatureRequest request;
1049 request.setNoAttributes();
1050
1051 if ( mExtent )
1052 {
1053 QgsRectangle rect = *mExtent;
1054 if ( !mTransform.isShortCircuited() )
1055 {
1056 QgsCoordinateTransform rectTransform = mTransform;
1057 rectTransform.setBallparkTransformsAreAppropriate( true );
1058 try
1059 {
1060 rect = rectTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
1061 }
1062 catch ( const QgsException &e )
1063 {
1064 Q_UNUSED( e )
1065 // See https://github.com/qgis/QGIS/issues/20749
1066 QgsDebugError( u"could not transform bounding box to map, skipping the snap filter (%1)"_s.arg( e.what() ) );
1067 }
1068 }
1069 request.setFilterRect( rect );
1070 }
1071
1072 bool filter = false;
1073 QgsRenderContext *ctx = nullptr;
1074 if ( mContext )
1075 {
1076 ctx = mContext.get();
1077 if ( mRenderer )
1078 {
1079 // setup scale for scale dependent visibility (rule based)
1080 mRenderer->startRender( *ctx, mSource->fields() );
1081 filter = mRenderer->capabilities() & QgsFeatureRenderer::Filter;
1082 request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), mSource->fields() );
1083 }
1084 }
1085
1086 QgsFeatureIterator fi = mSource->getFeatures( request );
1087 int indexedCount = 0;
1088
1089 while ( fi.nextFeature( f ) )
1090 {
1091 if ( !f.hasGeometry() )
1092 continue;
1093
1094 if ( filter && ctx && mRenderer )
1095 {
1096 ctx->expressionContext().setFeature( f );
1097 if ( !mRenderer->willRenderFeature( f, *ctx ) )
1098 {
1099 continue;
1100 }
1101 }
1102
1103 if ( mTransform.isValid() )
1104 {
1105 try
1106 {
1107 QgsGeometry transformedGeometry = f.geometry();
1108 transformedGeometry.transform( mTransform );
1109 f.setGeometry( transformedGeometry );
1110 }
1111 catch ( const QgsException &e )
1112 {
1113 Q_UNUSED( e )
1114 // See https://github.com/qgis/QGIS/issues/20749
1115 QgsDebugError( u"could not transform geometry to map, skipping the snap for it (%1)"_s.arg( e.what() ) );
1116 continue;
1117 }
1118 }
1119
1120 const QgsRectangle bbox = f.geometry().boundingBox();
1121 if ( bbox.isFinite() )
1122 {
1123 SpatialIndex::Region r( QgsSpatialIndexUtils::rectangleToRegion( bbox ) );
1124 dataList << new RTree::Data( 0, nullptr, r, f.id() );
1125
1126 auto it = mGeoms.find( f.id() );
1127 if ( it != mGeoms.end() )
1128 {
1129 delete *it;
1130 *it = new QgsGeometry( f.geometry() );
1131 }
1132 else
1133 {
1134 mGeoms[f.id()] = new QgsGeometry( f.geometry() );
1135 }
1136 ++indexedCount;
1137 }
1138
1139 if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
1140 {
1141 qDeleteAll( dataList );
1142 destroyIndex();
1143 return false;
1144 }
1145 }
1146
1147 // R-Tree parameters
1148 const double fillFactor = 0.7;
1149 const unsigned long indexCapacity = 10;
1150 const unsigned long leafCapacity = 10;
1151 const unsigned long dimension = 2;
1152 const RTree::RTreeVariant variant = RTree::RV_RSTAR;
1153 SpatialIndex::id_type indexId;
1154
1155 if ( dataList.isEmpty() )
1156 {
1157 mIsEmptyLayer = true;
1158 return true; // no features
1159 }
1160
1161 QgsPointLocator_Stream stream( dataList );
1162 try
1163 {
1164 mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId ) );
1165 }
1166 catch ( const std::exception &e )
1167 {
1168 QgsDebugError( u"An exception has occurred during the creation of RTree: %1"_s.arg( e.what() ) );
1169 destroyIndex();
1170 return false;
1171 }
1172
1173
1174 if ( ctx && mRenderer )
1175 {
1176 mRenderer->stopRender( *ctx );
1177 }
1178
1179 QgsDebugMsgLevel( u"RebuildIndex end : %1 ms (%2)"_s.arg( t.elapsed() ).arg( mSource->id() ), 2 );
1180
1181 return true;
1182}
1183
1184
1186{
1187 mRTree.reset();
1188
1189 mIsEmptyLayer = false;
1190
1191 qDeleteAll( mGeoms );
1192
1193 mGeoms.clear();
1194}
1195
1196void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
1197{
1198 if ( mIsIndexing )
1199 {
1200 // will modify index once current indexing is finished
1201 mAddedFeatures << fid;
1202 return;
1203 }
1204
1205 if ( !mRTree )
1206 {
1207 if ( mIsEmptyLayer )
1208 {
1209 // layer is not empty any more, let's build the index
1210 mIsEmptyLayer = false;
1211 init();
1212 }
1213 return; // nothing to do if we are not initialized yet
1214 }
1215
1216 QgsFeature f;
1217 if ( mLayer->getFeatures( mContext ? QgsFeatureRequest( fid ) : QgsFeatureRequest( fid ).setNoAttributes() ).nextFeature( f ) )
1218 {
1219 if ( !f.hasGeometry() )
1220 return;
1221
1222 if ( mContext )
1223 {
1224 std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
1225 QgsRenderContext *ctx = nullptr;
1226
1227 mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
1228 ctx = mContext.get();
1229 if ( renderer && ctx )
1230 {
1231 bool pass = false;
1232 renderer->startRender( *ctx, mLayer->fields() );
1233
1234 ctx->expressionContext().setFeature( f );
1235 if ( !renderer->willRenderFeature( f, *ctx ) )
1236 {
1237 pass = true;
1238 }
1239
1240 renderer->stopRender( *ctx );
1241 if ( pass )
1242 return;
1243 }
1244 }
1245
1246 if ( mTransform.isValid() )
1247 {
1248 try
1249 {
1250 QgsGeometry transformedGeom = f.geometry();
1251 transformedGeom.transform( mTransform );
1252 f.setGeometry( transformedGeom );
1253 }
1254 catch ( const QgsException &e )
1255 {
1256 Q_UNUSED( e )
1257 // See https://github.com/qgis/QGIS/issues/20749
1258 QgsDebugError( u"could not transform geometry to map, skipping the snap for it (%1)"_s.arg( e.what() ) );
1259 return;
1260 }
1261 }
1262
1263 const QgsRectangle bbox = f.geometry().boundingBox();
1264 if ( bbox.isFinite() )
1265 {
1266 const SpatialIndex::Region r( QgsSpatialIndexUtils::rectangleToRegion( bbox ) );
1267 mRTree->insertData( 0, nullptr, r, f.id() );
1268
1269 auto it = mGeoms.find( f.id() );
1270 if ( it != mGeoms.end() )
1271 {
1272 delete *it;
1273 *it = new QgsGeometry( f.geometry() );
1274 }
1275 else
1276 {
1277 mGeoms[fid] = new QgsGeometry( f.geometry() );
1278 }
1279 }
1280 }
1281}
1282
1283void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
1284{
1285 if ( mIsIndexing )
1286 {
1287 if ( mAddedFeatures.contains( fid ) )
1288 {
1289 mAddedFeatures.remove( fid );
1290 }
1291 else
1292 {
1293 // will modify index once current indexing is finished
1294 mDeletedFeatures << fid;
1295 }
1296 return;
1297 }
1298
1299 if ( !mRTree )
1300 return; // nothing to do if we are not initialized yet
1301
1302 auto it = mGeoms.find( fid );
1303 if ( it != mGeoms.end() )
1304 {
1305 mRTree->deleteData( QgsSpatialIndexUtils::rectangleToRegion( ( *it )->boundingBox() ), fid );
1306 delete *it;
1307 mGeoms.erase( it );
1308 }
1309}
1310
1311void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
1312{
1313 Q_UNUSED( geom )
1314 onFeatureDeleted( fid );
1315 onFeatureAdded( fid );
1316}
1317
1318void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
1319{
1320 Q_UNUSED( idx )
1321 Q_UNUSED( value )
1322 if ( mContext )
1323 {
1324 onFeatureDeleted( fid );
1325 onFeatureAdded( fid );
1326 }
1327}
1328
1329
1330QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1331{
1332 if ( !prepare( relaxed ) )
1333 return Match();
1334
1335 Match m;
1336 QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
1337 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1338 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1339 if ( m.isValid() && m.distance() > tolerance )
1340 return Match(); // make sure that only match strictly within the tolerance is returned
1341 return m;
1342}
1343
1344QgsPointLocator::Match QgsPointLocator::nearestCentroid( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1345{
1346 if ( !prepare( relaxed ) )
1347 return Match();
1348
1349 Match m;
1350 QgsPointLocator_VisitorNearestCentroid visitor( this, m, point, filter );
1351
1352 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1353 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1354 if ( m.isValid() && m.distance() > tolerance )
1355 return Match(); // make sure that only match strictly within the tolerance is returned
1356 return m;
1357}
1358
1359QgsPointLocator::Match QgsPointLocator::nearestMiddleOfSegment( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1360{
1361 if ( !prepare( relaxed ) )
1362 return Match();
1363
1364 Match m;
1365 QgsPointLocator_VisitorNearestMiddleOfSegment visitor( this, m, point, filter );
1366
1367 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1368 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1369 if ( m.isValid() && m.distance() > tolerance )
1370 return Match(); // make sure that only match strictly within the tolerance is returned
1371 return m;
1372}
1373
1375{
1376 if ( !prepare( relaxed ) )
1377 return Match();
1378
1379 Match m;
1380 QgsPointLocator_VisitorNearestLineEndpoint visitor( this, m, point, filter );
1381
1382 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1383 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1384 if ( m.isValid() && m.distance() > tolerance )
1385 return Match(); // make sure that only match strictly within the tolerance is returned
1386 return m;
1387}
1388
1389QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1390{
1391 if ( !prepare( relaxed ) )
1392 return Match();
1393
1394 const Qgis::GeometryType geomType = mLayer->geometryType();
1395 if ( geomType == Qgis::GeometryType::Point )
1396 return Match();
1397
1398 Match m;
1399 QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
1400 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1401 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1402 if ( m.isValid() && m.distance() > tolerance )
1403 return Match(); // make sure that only match strictly within the tolerance is returned
1404 return m;
1405}
1406
1407QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1408{
1409 if ( !prepare( relaxed ) )
1410 return Match();
1411
1412 const MatchList mlist = pointInPolygon( point, false, filter );
1413 if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() )
1414 {
1415 return mlist.at( 0 );
1416 }
1417
1418 if ( tolerance == 0 )
1419 {
1420 return Match();
1421 }
1422
1423 // discard point and line layers to keep only polygons
1424 const Qgis::GeometryType geomType = mLayer->geometryType();
1425 if ( geomType == Qgis::GeometryType::Point || geomType == Qgis::GeometryType::Line )
1426 return Match();
1427
1428 // use edges for adding tolerance
1429 const Match m = nearestEdge( point, tolerance, filter );
1430 if ( m.isValid() )
1431 return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
1432 else
1433 return Match();
1434}
1435
1436
1438{
1439 if ( !prepare( relaxed ) )
1440 return MatchList();
1441
1442 const Qgis::GeometryType geomType = mLayer->geometryType();
1443 if ( geomType == Qgis::GeometryType::Point )
1444 return MatchList();
1445
1446 MatchList lst;
1447 QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
1448 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1449
1450 return lst;
1451}
1452
1454{
1455 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1456 return edgesInRect( rect, filter, relaxed );
1457}
1458
1460{
1461 if ( !prepare( relaxed ) )
1462 return MatchList();
1463
1464 MatchList lst;
1465 QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
1466 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1467
1468 return lst;
1469}
1470
1472{
1473 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1474 return verticesInRect( rect, filter, relaxed );
1475}
1476
1478{
1479 // TODO QGIS 5: reorder relaxed & filter parameters to match other methods' signatures
1480 if ( !prepare( relaxed ) )
1481 return MatchList();
1482
1483 const Qgis::GeometryType geomType = mLayer->geometryType();
1484 if ( geomType == Qgis::GeometryType::Point || geomType == Qgis::GeometryType::Line )
1485 return MatchList();
1486
1487 MatchList lst;
1488 QgsPointLocator_VisitorArea visitor( this, point, lst, filter );
1489 mRTree->intersectsWithQuery( point2point( point ), visitor );
1490 return lst;
1491}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2766
The part_iterator class provides an STL-style iterator for const references to geometry parts.
The vertex_iterator class provides an STL-style iterator for vertices.
QgsVertexId vertexId() const
Returns vertex ID of the current item.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual QgsPoint startPoint() const =0
Returns the starting point of the curve.
Defines a QGIS exception class.
QString what() const
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ Filter
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ....
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
bool hasGeometry() const
Returns true if the feature has an associated geometry.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
A geometry is the spatial representation of a feature.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=Qgis::DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry convertToType(Qgis::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
Qgis::GeometryType type
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 isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
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::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
void styleChanged()
Signal emitted whenever a change affects the layer's style.
void dataChanged()
Data of layer changed.
Helper class to dump the R-index nodes and their content.
void getNextEntry(const IEntry &entry, id_type &nextEntry, bool &hasNext) override
Helper class for bulk loading of R-trees.
IData * getNext() override
uint32_t size() override
QgsPointLocator_Stream(const QVector< RTree::Data * > &dataList)
Construct a QgsPointLocator_Stream.
void visitNode(const INode &n) override
void visitData(const IData &d) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorArea(QgsPointLocator *pl, const QgsPointXY &origPt, QgsPointLocator::MatchList &list, QgsPointLocator::MatchFilter *filter=nullptr)
constructor
QgsPointLocator_VisitorCentroidsInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
Constructs the visitor.
void visitData(const IData &d) override
void visitData(std::vector< const IData * > &v) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorEdgesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
void visitNode(const INode &n) override
void visitData(const IData &d) override
void visitData(std::vector< const IData * > &v) override
void visitNode(const INode &n) override
void visitData(const IData &d) override
QgsPointLocator_VisitorMiddlesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
Constructs the visitor.
void visitData(const IData &d) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorNearestEdge(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
void visitData(const IData &d) override
void visitData(std::vector< const IData * > &v) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
void visitData(std::vector< const IData * > &v) override
void visitData(const IData &d) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorNearestVertex(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
void visitData(const IData &d) override
void visitNode(const INode &n) override
void visitData(std::vector< const IData * > &v) override
QgsPointLocator_VisitorVerticesInRect(QgsPointLocator *pl, QgsPointLocator::MatchList &lst, const QgsRectangle &srcRect, QgsPointLocator::MatchFilter *filter=nullptr)
Constructs the visitor.
Defines the interface for querying point locations.
friend class QgsPointLocator_VisitorNearestLineEndpoint
friend class QgsPointLocatorInitTask
friend class QgsPointLocator_VisitorEdgesInRect
Match nearestEdge(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest edge to the specified point - up to distance specified by tolerance Optional filter may ...
friend class QgsPointLocator_VisitorNearestVertex
void setRenderContext(const QgsRenderContext *context)
Configure render context - if not nullptr, it will use to index only visible feature.
QgsCoordinateReferenceSystem destinationCrs() const
Gets destination CRS - may be an invalid QgsCoordinateReferenceSystem if not doing OTF reprojection.
friend class QgsPointLocator_VisitorNearestCentroid
MatchList pointInPolygon(const QgsPointXY &point, bool relaxed=false, QgsPointLocator::MatchFilter *filter=nullptr)
Find out if the point is in any polygons.
void setExtent(const QgsRectangle *extent)
Configure extent - if not nullptr, it will index only that area.
bool init(int maxFeaturesToIndex=-1, bool relaxed=false)
Prepare the index for queries.
QgsVectorLayer * layer() const
Gets associated layer.
friend class QgsPointLocator_VisitorNearestMiddleOfSegment
MatchList verticesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find vertices within a specified rectangle This method is either blocking or non blocking according t...
class QList< QgsPointLocator::Match > MatchList
Match nearestVertex(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest vertex to the specified point - up to distance specified by tolerance Optional filter ma...
friend class QgsPointLocator_VisitorNearestEdge
const QgsRectangle * extent() const
Gets extent of the area point locator covers - if nullptr then it caches the whole layer.
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find edges within a specified rectangle Optional filter may discard unwanted matches.
bool hasIndex() const
Indicate whether the data have been already indexed.
friend class QgsPointLocator_VisitorVerticesInRect
Match nearestMiddleOfSegment(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest middle of segment to the specified point - up to distance specified by tolerance Optiona...
void waitForIndexingFinished()
If the point locator has been initialized relaxedly and is currently indexing, this methods waits for...
Match nearestCentroid(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest centroid to the specified point - up to distance specified by tolerance Optional filter ...
void initFinished(bool ok)
Emitted whenever index has been built and initialization is finished.
bool rebuildIndex(int maxFeaturesToIndex=-1)
Match nearestArea(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest area to the specified point - up to distance specified by tolerance Optional filter may ...
@ Area
Snapped to an area.
@ MiddleOfSegment
Snapped to the middle of a segment.
@ Vertex
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
@ Centroid
Snapped to a centroid.
@ Edge
Snapped to an edge.
@ LineEndpoint
Start or end points of lines only.
QgsPointLocator(QgsVectorLayer *layer, const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem(), const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), const QgsRectangle *extent=nullptr)
Construct point locator for a layer.
~QgsPointLocator() override
friend class QgsPointLocator_VisitorArea
Match nearestLineEndpoints(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest line endpoint (start or end vertex) to the specified point - up to distance specified by...
Represents a 2D point.
Definition qgspointxy.h:62
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition qgspointxy.h:189
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:245
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
A rectangle specified with double values.
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
static SpatialIndex::Region rectangleToRegion(const QgsRectangle &rectangle)
Converts a QGIS rectangle to a SpatialIndex region.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
Represents a vector layer which manages a vector based dataset.
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.
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Emitted whenever a geometry change is done in the edit buffer.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
QgsPointLocator_VisitorNearestCentroid(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class used when traversing the index looking for centroid - builds a list of matches.
QgsPointLocator_VisitorNearestMiddleOfSegment(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class used when traversing the index looking for middle segment - builds a list of matches.
QgsPointLocator_VisitorNearestLineEndpoint(QgsPointLocator *pl, QgsPointLocator::Match &m, const QgsPointXY &srcPoint, QgsPointLocator::MatchFilter *filter=nullptr)
Helper class used when traversing the index looking for line endpoints (start or end vertex) - builds...
T qgsgeometry_cast(QgsAbstractGeometry *geom)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
Interface that allows rejection of some matches in intersection queries (e.g.
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units,...
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
static const int TOP
static const int RIGHT
OutCode computeOutCode(double x, double y)
static const int LEFT
bool isSegmentInRect(double x0, double y0, double x1, double y1)
_CohenSutherland(const QgsRectangle &rect)
static const int INSIDE
static const int BOTTOM