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