QGIS API Documentation 3.99.0-Master (8e76e220402)
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
45
46static SpatialIndex::Point point2point( const QgsPointXY &point )
47{
48 double plow[2] = { point.x(), point.y() };
49 return Point( plow, 2 );
50}
51
52
53// Ahh.... another magic number. Taken from QgsVectorLayer::snapToGeometry() call to closestSegmentWithContext().
54// The default epsilon used for sqrDistToSegment (1e-8) is too high when working with lat/lon coordinates
55// I still do not fully understand why the sqrDistToSegment() code uses epsilon and if the square distance
56// is lower than epsilon it will have a special logic...
57static const double POINT_LOC_EPSILON = 1e-12;
58
60
61
67class QgsPointLocator_Stream : public IDataStream
68{
69 public:
73 explicit QgsPointLocator_Stream( const QVector<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 QVector<RTree::Data *> mDataList;
86 QVectorIterator<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
364
371{
372 public:
374 : mLocator( pl )
375 , mBest( m )
376 , mSrcPoint( srcPoint )
377 , mFilter( filter )
378 {}
379
380 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
381 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
382
383 void visitData( const IData &d ) override
384 {
385 const QgsFeatureId id = d.getIdentifier();
386 QgsGeometry *geom = mLocator->mGeoms.value( id );
387 if ( !geom )
388 return; // should not happen, but be safe
389
390 QgsPointXY pt;
391 int afterVertex;
392 const double sqrDist = geom->closestSegmentWithContext( mSrcPoint, pt, afterVertex, nullptr, POINT_LOC_EPSILON );
393 if ( sqrDist < 0 )
394 return;
395
396 QgsPointXY edgePoints[2];
397 edgePoints[0] = geom->vertexAt( afterVertex - 1 );
398 edgePoints[1] = geom->vertexAt( afterVertex );
399 const QgsPointLocator::Match m( QgsPointLocator::Edge, mLocator->mLayer, id, std::sqrt( sqrDist ), pt, afterVertex - 1, edgePoints );
400 // in range queries the filter may reject some matches
401 if ( mFilter && !mFilter->acceptMatch( m ) )
402 return;
403
404 if ( !mBest.isValid() || m.distance() < mBest.distance() )
405 mBest = m;
406 }
407
408 private:
409 QgsPointLocator *mLocator = nullptr;
411 QgsPointXY mSrcPoint;
412 QgsPointLocator::MatchFilter *mFilter = nullptr;
413};
414
415
417
423class QgsPointLocator_VisitorArea : public IVisitor
424{
425 public:
428 : mLocator( pl )
429 , mList( list )
430 , mFilter( filter )
431 , mGeomPt( QgsGeometry::fromPointXY( origPt ) )
432 {}
433
434 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
435 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
436
437 void visitData( const IData &d ) override
438 {
439 const QgsFeatureId id = d.getIdentifier();
440 QgsGeometry *g = mLocator->mGeoms.value( id );
441 if ( !g )
442 return; // should not happen, but be safe
443
444 if ( g->intersects( mGeomPt ) )
445 {
446 const QgsPointLocator::Match m( QgsPointLocator::Area, mLocator->mLayer, id, 0, mGeomPt.asPoint() );
447 if ( mFilter && !mFilter->acceptMatch( m ) )
448 return;
449 mList << m;
450 }
451 }
452 private:
453 QgsPointLocator *mLocator = nullptr;
455 QgsPointLocator::MatchFilter *mFilter = nullptr;
456 QgsGeometry mGeomPt;
457};
458
459
461
462// code adapted from
463// http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
465{
466 explicit _CohenSutherland( const QgsRectangle &rect ) : mRect( rect ) {}
467
468 typedef int OutCode;
469
470 static const int INSIDE = 0; // 0000
471 static const int LEFT = 1; // 0001
472 static const int RIGHT = 2; // 0010
473 static const int BOTTOM = 4; // 0100
474 static const int TOP = 8; // 1000
475
477
478 OutCode computeOutCode( double x, double y )
479 {
480 OutCode code = INSIDE; // initialized as being inside of clip window
481 if ( x < mRect.xMinimum() ) // to the left of clip window
482 code |= LEFT;
483 else if ( x > mRect.xMaximum() ) // to the right of clip window
484 code |= RIGHT;
485 if ( y < mRect.yMinimum() ) // below the clip window
486 code |= BOTTOM;
487 else if ( y > mRect.yMaximum() ) // above the clip window
488 code |= TOP;
489 return code;
490 }
491
492 bool isSegmentInRect( double x0, double y0, double x1, double y1 )
493 {
494 // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
495 OutCode outcode0 = computeOutCode( x0, y0 );
496 OutCode outcode1 = computeOutCode( x1, y1 );
497 bool accept = false;
498
499 while ( true )
500 {
501 if ( !( outcode0 | outcode1 ) )
502 {
503 // Bitwise OR is 0. Trivially accept and get out of loop
504 accept = true;
505 break;
506 }
507 else if ( outcode0 & outcode1 )
508 {
509 // Bitwise AND is not 0. Trivially reject and get out of loop
510 break;
511 }
512 else
513 {
514 // failed both tests, so calculate the line segment to clip
515 // from an outside point to an intersection with clip edge
516 double x, y;
517
518 // At least one endpoint is outside the clip rectangle; pick it.
519 const OutCode outcodeOut = outcode0 ? outcode0 : outcode1;
520
521 // Now find the intersection point;
522 // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
523 if ( outcodeOut & TOP )
524 {
525 // point is above the clip rectangle
526 x = x0 + ( x1 - x0 ) * ( mRect.yMaximum() - y0 ) / ( y1 - y0 );
527 y = mRect.yMaximum();
528 }
529 else if ( outcodeOut & BOTTOM )
530 {
531 // point is below the clip rectangle
532 x = x0 + ( x1 - x0 ) * ( mRect.yMinimum() - y0 ) / ( y1 - y0 );
533 y = mRect.yMinimum();
534 }
535 else if ( outcodeOut & RIGHT )
536 {
537 // point is to the right of clip rectangle
538 y = y0 + ( y1 - y0 ) * ( mRect.xMaximum() - x0 ) / ( x1 - x0 );
539 x = mRect.xMaximum();
540 }
541 else if ( outcodeOut & LEFT )
542 {
543 // point is to the left of clip rectangle
544 y = y0 + ( y1 - y0 ) * ( mRect.xMinimum() - x0 ) / ( x1 - x0 );
545 x = mRect.xMinimum();
546 }
547 else
548 break;
549
550 // Now we move outside point to intersection point to clip
551 // and get ready for next pass.
552 if ( outcodeOut == outcode0 )
553 {
554 x0 = x;
555 y0 = y;
556 outcode0 = computeOutCode( x0, y0 );
557 }
558 else
559 {
560 x1 = x;
561 y1 = y;
562 outcode1 = computeOutCode( x1, y1 );
563 }
564 }
565 }
566 return accept;
567 }
568};
569
570
571static QgsPointLocator::MatchList _geometrySegmentsInRect( QgsGeometry *geom, const QgsRectangle &rect, QgsVectorLayer *vl, QgsFeatureId fid )
572{
573 // this code is stupidly based on QgsGeometry::closestSegmentWithContext
574 // we need iterator for segments...
575
577
578 // geom is converted to a MultiCurve
579 QgsGeometry straightGeom = geom->convertToType( Qgis::GeometryType::Line, true );
580 // and convert to straight segemnt / converts curve to linestring
581 straightGeom.convertToStraightSegment();
582
583 // so, you must have multilinestring
584 //
585 // Special case: Intersections cannot be done on an empty linestring like
586 // QgsGeometry(QgsLineString()) or QgsGeometry::fromWkt("LINESTRING EMPTY")
587 if ( straightGeom.isEmpty() || ( ( straightGeom.type() != Qgis::GeometryType::Line ) && ( !straightGeom.isMultipart() ) ) )
588 return lst;
589
590 _CohenSutherland cs( rect );
591
592 int pointIndex = 0;
593 for ( auto part = straightGeom.const_parts_begin(); part != straightGeom.const_parts_end(); ++part )
594 {
595 // Checking for invalid linestrings
596 // A linestring should/(must?) have at least two points.
597 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( *part );
598 Q_ASSERT( !curve->hasCurvedSegments() );
599 if ( curve->numPoints() < 2 )
600 continue;
601
602 QgsAbstractGeometry::vertex_iterator it = ( *part )->vertices_begin();
603 QgsPointXY prevPoint( *it );
604 it++;
605 while ( it != ( *part )->vertices_end() )
606 {
607 const QgsPointXY thisPoint( *it );
608 if ( cs.isSegmentInRect( prevPoint.x(), prevPoint.y(), thisPoint.x(), thisPoint.y() ) )
609 {
610 QgsPointXY edgePoints[2];
611 edgePoints[0] = prevPoint;
612 edgePoints[1] = thisPoint;
613 lst << QgsPointLocator::Match( QgsPointLocator::Edge, vl, fid, 0, QgsPointXY(), pointIndex - 1, edgePoints );
614 }
615 prevPoint = QgsPointXY( *it );
616 it++;
617 pointIndex += 1;
618
619 }
620 }
621 return lst;
622}
623
630{
631 public:
633 : mLocator( pl )
634 , mList( lst )
635 , mSrcRect( srcRect )
636 , mFilter( filter )
637 {}
638
639 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
640 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
641
642 void visitData( const IData &d ) override
643 {
644 const QgsFeatureId id = d.getIdentifier();
645 QgsGeometry *geom = mLocator->mGeoms.value( id );
646 if ( !geom )
647 return; // should not happen, but be safe
648
649 const auto segmentsInRect {_geometrySegmentsInRect( geom, mSrcRect, mLocator->mLayer, id )};
650 for ( const QgsPointLocator::Match &m : segmentsInRect )
651 {
652 // in range queries the filter may reject some matches
653 if ( mFilter && !mFilter->acceptMatch( m ) )
654 continue;
655
656 mList << m;
657 }
658 }
659
660 private:
661 QgsPointLocator *mLocator = nullptr;
663 QgsRectangle mSrcRect;
664 QgsPointLocator::MatchFilter *mFilter = nullptr;
665};
666
668
676{
677 public:
680 : mLocator( pl )
681 , mList( lst )
682 , mSrcRect( srcRect )
683 , mFilter( filter )
684 {}
685
686 void visitNode( const INode &n ) override { Q_UNUSED( n ) }
687 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ) }
688
689 void visitData( const IData &d ) override
690 {
691 const QgsFeatureId id = d.getIdentifier();
692 const QgsGeometry *geom = mLocator->mGeoms.value( id );
693 if ( !geom )
694 return; // should not happen, but be safe
695
696 for ( QgsAbstractGeometry::vertex_iterator it = geom->vertices_begin(); it != geom->vertices_end(); ++it )
697 {
698 if ( mSrcRect.contains( *it ) )
699 {
700 const QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, 0, *it, geom->vertexNrFromVertexId( it.vertexId() ) );
701
702 // in range queries the filter may reject some matches
703 if ( mFilter && !mFilter->acceptMatch( m ) )
704 continue;
705
706 mList << m;
707 }
708 }
709 }
710
711 private:
712 QgsPointLocator *mLocator = nullptr;
714 QgsRectangle mSrcRect;
715 QgsPointLocator::MatchFilter *mFilter = nullptr;
716};
717
725{
726 public:
729 : mLocator( pl )
730 , mList( lst )
731 , mSrcRect( srcRect )
732 , mFilter( filter )
733 {}
734
735 void visitNode( const INode &n ) override { Q_UNUSED( n ); }
736 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
737
738 void visitData( const IData &d ) override
739 {
740 const QgsFeatureId id = d.getIdentifier();
741 const QgsGeometry *geom = mLocator->mGeoms.value( id );
742 if ( !geom )
743 return; // should not happen, but be safe
744
745 const QgsPointXY centroid = geom->centroid().asPoint();
746 if ( mSrcRect.contains( centroid ) )
747 {
748 const QgsPointLocator::Match m( QgsPointLocator::Centroid, mLocator->mLayer, id, 0, centroid, -1 );
749
750 // in range queries the filter may reject some matches
751 if ( !( mFilter && !mFilter->acceptMatch( m ) ) )
752 mList << m;
753 }
754 }
755
756 private:
757 QgsPointLocator *mLocator = nullptr;
759 QgsRectangle mSrcRect;
760 QgsPointLocator::MatchFilter *mFilter = nullptr;
761};
762
770{
771 public:
774 : mLocator( pl )
775 , mList( lst )
776 , mSrcRect( srcRect )
777 , mFilter( filter )
778 {}
779
780 void visitNode( const INode &n ) override { Q_UNUSED( n ); }
781 void visitData( std::vector<const IData *> &v ) override { Q_UNUSED( v ); }
782
783 void visitData( const IData &d ) override
784 {
785 const QgsFeatureId id = d.getIdentifier();
786 const QgsGeometry *geom = mLocator->mGeoms.value( id );
787 if ( !geom )
788 return; // should not happen, but be safe
789
790 for ( QgsAbstractGeometry::const_part_iterator itPart = geom->const_parts_begin() ; itPart != geom->const_parts_end() ; ++itPart )
791 {
792 QgsAbstractGeometry::vertex_iterator it = ( *itPart )->vertices_begin();
793 QgsAbstractGeometry::vertex_iterator itPrevious = ( *itPart )->vertices_begin();
794 it++;
795 for ( ; it != geom->vertices_end(); ++it, ++itPrevious )
796 {
797 const QgsPointXY pt( ( ( *itPrevious ).x() + ( *it ).x() ) / 2.0, ( ( *itPrevious ).y() + ( *it ).y() ) / 2.0 );
798 if ( mSrcRect.contains( pt ) )
799 {
800 const QgsPointLocator::Match m( QgsPointLocator::MiddleOfSegment, mLocator->mLayer, id, 0, pt, geom->vertexNrFromVertexId( it.vertexId() ) );
801
802 // in range queries the filter may reject some matches
803 if ( mFilter && !mFilter->acceptMatch( m ) )
804 continue;
805
806 mList << m;
807 }
808 }
809 }
810 }
811
812 private:
813 QgsPointLocator *mLocator = nullptr;
815 QgsRectangle mSrcRect;
816 QgsPointLocator::MatchFilter *mFilter = nullptr;
817};
818
820#include <QStack>
821#include <memory>
822
828class QgsPointLocator_DumpTree : public SpatialIndex::IQueryStrategy
829{
830 private:
831 QStack<id_type> ids;
832
833 public:
834
835 void getNextEntry( const IEntry &entry, id_type &nextEntry, bool &hasNext ) override
836 {
837 const INode *n = dynamic_cast<const INode *>( &entry );
838 if ( !n )
839 return;
840
841 QgsDebugMsgLevel( u"NODE: %1"_s.arg( n->getIdentifier() ), 4 );
842 if ( n->getLevel() > 0 )
843 {
844 // inner nodes
845 for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
846 {
847 QgsDebugMsgLevel( u"- CH: %1"_s.arg( n->getChildIdentifier( cChild ) ), 4 );
848 ids.push( n->getChildIdentifier( cChild ) );
849 }
850 }
851 else
852 {
853 // leaves
854 for ( uint32_t cChild = 0; cChild < n->getChildrenCount(); cChild++ )
855 {
856 QgsDebugMsgLevel( u"- L: %1"_s.arg( n->getChildIdentifier( cChild ) ), 4 );
857 }
858 }
859
860 if ( ! ids.empty() )
861 {
862 nextEntry = ids.back();
863 ids.pop();
864 hasNext = true;
865 }
866 else
867 hasNext = false;
868 }
869};
870
872
873
875 : mLayer( layer )
876{
877 if ( destCRS.isValid() )
878 {
879 mTransform = QgsCoordinateTransform( layer->crs(), destCRS, transformContext );
880 }
881
882 setExtent( extent );
883
884 mStorage.reset( StorageManager::createNewMemoryStorageManager() );
885
886 connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
887 connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
888 connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
889 connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
891}
892
893
895{
896 // don't delete a locator if there is an indexing task running on it
897 mIsDestroying = true;
898 if ( mIsIndexing )
900
901 destroyIndex();
902}
903
905{
906 return mTransform.isValid() ? mTransform.destinationCrs() : QgsCoordinateReferenceSystem();
907}
908
910{
911 if ( mIsIndexing )
912 // already indexing, return!
913 return;
914
915 mExtent.reset( extent ? new QgsRectangle( *extent ) : nullptr );
916
917 destroyIndex();
918}
919
921{
922 if ( mIsIndexing )
923 // already indexing, return!
924 return;
925
926 disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
927
928 destroyIndex();
929 mContext.reset( nullptr );
930
931 if ( context )
932 {
933 mContext = std::make_unique<QgsRenderContext>( *context );
935 }
936
937}
938
939void QgsPointLocator::onInitTaskFinished()
940{
941 Q_ASSERT_X( QThread::currentThread() == qApp->thread(), "QgsPointLocator::onInitTaskFinished", "was not called on main thread" );
942
943 // Check that we don't call this method twice, when calling waitForFinished
944 // for instance (because of taskCompleted signal)
945 if ( !mIsIndexing )
946 return;
947
948 if ( mIsDestroying )
949 return;
950
951 mIsIndexing = false;
952 mRenderer.reset();
953 mSource.reset();
954
955 // treat added and deleted feature while indexing
956 for ( const QgsFeatureId fid : std::as_const( mAddedFeatures ) )
957 onFeatureAdded( fid );
958 mAddedFeatures.clear();
959
960 for ( const QgsFeatureId fid : std::as_const( mDeletedFeatures ) )
961 onFeatureDeleted( fid );
962 mDeletedFeatures.clear();
963
964 emit initFinished( mInitTask->isBuildOK() );
965}
966
967bool QgsPointLocator::init( int maxFeaturesToIndex, bool relaxed )
968{
969 const Qgis::GeometryType geomType = mLayer->geometryType();
970 if ( geomType == Qgis::GeometryType::Null // nothing to index
971 || hasIndex()
972 || mIsIndexing ) // already indexing, return!
973 return true;
974
975 if ( !mLayer->dataProvider()
976 || !mLayer->dataProvider()->isValid() )
977 return false;
978
979 mSource = std::make_unique<QgsVectorLayerFeatureSource>( mLayer );
980
981 if ( mContext )
982 {
983 mRenderer.reset( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
984 mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
985 }
986
987 mIsIndexing = true;
988
989 if ( relaxed )
990 {
991 mInitTask = new QgsPointLocatorInitTask( this );
992 connect( mInitTask, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskFinished );
993 connect( mInitTask, &QgsPointLocatorInitTask::taskCompleted, this, &QgsPointLocator::onInitTaskFinished );
994 QgsApplication::taskManager()->addTask( mInitTask );
995 return true;
996 }
997 else
998 {
999 const bool ok = rebuildIndex( maxFeaturesToIndex );
1000 mIsIndexing = false;
1001 emit initFinished( ok );
1002 return ok;
1003 }
1004}
1005
1007{
1008
1009 disconnect( mInitTask, &QgsPointLocatorInitTask::taskTerminated, this, &QgsPointLocator::onInitTaskFinished );
1010 disconnect( mInitTask, &QgsPointLocatorInitTask::taskCompleted, this, &QgsPointLocator::onInitTaskFinished );
1011 mInitTask->waitForFinished();
1012
1013 if ( !mIsDestroying )
1014 onInitTaskFinished();
1015}
1016
1018{
1019 return mIsIndexing || mRTree || mIsEmptyLayer;
1020}
1021
1022bool QgsPointLocator::prepare( bool relaxed )
1023{
1024 if ( mIsIndexing )
1025 {
1026 if ( relaxed )
1027 return false;
1028 else
1030 }
1031
1032 if ( !mRTree )
1033 {
1034 init( -1, relaxed );
1035 if ( ( relaxed && mIsIndexing ) || !mRTree ) // relaxed mode and currently indexing or still invalid?
1036 return false;
1037 }
1038
1039 return true;
1040}
1041
1042bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
1043{
1044 QElapsedTimer t;
1045 t.start();
1046
1047 QgsDebugMsgLevel( u"RebuildIndex start : %1"_s.arg( mSource->id() ), 2 );
1048
1049 destroyIndex();
1050
1051 QVector<RTree::Data *> dataList;
1052 QgsFeature f;
1053
1054 QgsFeatureRequest request;
1055 request.setNoAttributes();
1056
1057 if ( mExtent )
1058 {
1059 QgsRectangle rect = *mExtent;
1060 if ( !mTransform.isShortCircuited() )
1061 {
1062 QgsCoordinateTransform rectTransform = mTransform;
1063 rectTransform.setBallparkTransformsAreAppropriate( true );
1064 try
1065 {
1066 rect = rectTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
1067 }
1068 catch ( const QgsException &e )
1069 {
1070 Q_UNUSED( e )
1071 // See https://github.com/qgis/QGIS/issues/20749
1072 QgsDebugError( u"could not transform bounding box to map, skipping the snap filter (%1)"_s.arg( e.what() ) );
1073 }
1074 }
1075 request.setFilterRect( rect );
1076 }
1077
1078 bool filter = false;
1079 QgsRenderContext *ctx = nullptr;
1080 if ( mContext )
1081 {
1082 ctx = mContext.get();
1083 if ( mRenderer )
1084 {
1085 // setup scale for scale dependent visibility (rule based)
1086 mRenderer->startRender( *ctx, mSource->fields() );
1087 filter = mRenderer->capabilities() & QgsFeatureRenderer::Filter;
1088 request.setSubsetOfAttributes( mRenderer->usedAttributes( *ctx ), mSource->fields() );
1089 }
1090 }
1091
1092 QgsFeatureIterator fi = mSource->getFeatures( request );
1093 int indexedCount = 0;
1094
1095 while ( fi.nextFeature( f ) )
1096 {
1097 if ( !f.hasGeometry() )
1098 continue;
1099
1100 if ( filter && ctx && mRenderer )
1101 {
1102 ctx->expressionContext().setFeature( f );
1103 if ( !mRenderer->willRenderFeature( f, *ctx ) )
1104 {
1105 continue;
1106 }
1107 }
1108
1109 if ( mTransform.isValid() )
1110 {
1111 try
1112 {
1113 QgsGeometry transformedGeometry = f.geometry();
1114 transformedGeometry.transform( mTransform );
1115 f.setGeometry( transformedGeometry );
1116 }
1117 catch ( const QgsException &e )
1118 {
1119 Q_UNUSED( e )
1120 // See https://github.com/qgis/QGIS/issues/20749
1121 QgsDebugError( u"could not transform geometry to map, skipping the snap for it (%1)"_s.arg( e.what() ) );
1122 continue;
1123 }
1124 }
1125
1126 const QgsRectangle bbox = f.geometry().boundingBox();
1127 if ( bbox.isFinite() )
1128 {
1129 SpatialIndex::Region r( QgsSpatialIndexUtils::rectangleToRegion( bbox ) );
1130 dataList << new RTree::Data( 0, nullptr, r, f.id() );
1131
1132 auto it = mGeoms.find( f.id() );
1133 if ( it != mGeoms.end() )
1134 {
1135 delete *it;
1136 *it = new QgsGeometry( f.geometry() );
1137 }
1138 else
1139 {
1140 mGeoms[f.id()] = new QgsGeometry( f.geometry() );
1141 }
1142 ++indexedCount;
1143 }
1144
1145 if ( maxFeaturesToIndex != -1 && indexedCount > maxFeaturesToIndex )
1146 {
1147 qDeleteAll( dataList );
1148 destroyIndex();
1149 return false;
1150 }
1151 }
1152
1153 // R-Tree parameters
1154 const double fillFactor = 0.7;
1155 const unsigned long indexCapacity = 10;
1156 const unsigned long leafCapacity = 10;
1157 const unsigned long dimension = 2;
1158 const RTree::RTreeVariant variant = RTree::RV_RSTAR;
1159 SpatialIndex::id_type indexId;
1160
1161 if ( dataList.isEmpty() )
1162 {
1163 mIsEmptyLayer = true;
1164 return true; // no features
1165 }
1166
1167 QgsPointLocator_Stream stream( dataList );
1168 try
1169 {
1170 mRTree.reset( RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
1171 leafCapacity, dimension, variant, indexId ) );
1172 }
1173 catch ( const std::exception &e )
1174 {
1175 QgsDebugError( u"An exception has occurred during the creation of RTree: %1"_s.arg( e.what() ) );
1176 destroyIndex();
1177 return false;
1178 }
1179
1180
1181 if ( ctx && mRenderer )
1182 {
1183 mRenderer->stopRender( *ctx );
1184 }
1185
1186 QgsDebugMsgLevel( u"RebuildIndex end : %1 ms (%2)"_s.arg( t.elapsed() ).arg( mSource->id() ), 2 );
1187
1188 return true;
1189}
1190
1191
1193{
1194 mRTree.reset();
1195
1196 mIsEmptyLayer = false;
1197
1198 qDeleteAll( mGeoms );
1199
1200 mGeoms.clear();
1201}
1202
1203void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
1204{
1205 if ( mIsIndexing )
1206 {
1207 // will modify index once current indexing is finished
1208 mAddedFeatures << fid;
1209 return;
1210 }
1211
1212 if ( !mRTree )
1213 {
1214 if ( mIsEmptyLayer )
1215 {
1216 // layer is not empty any more, let's build the index
1217 mIsEmptyLayer = false;
1218 init();
1219 }
1220 return; // nothing to do if we are not initialized yet
1221 }
1222
1223 QgsFeature f;
1224 if ( mLayer->getFeatures( mContext ? QgsFeatureRequest( fid ) : QgsFeatureRequest( fid ).setNoAttributes() ).nextFeature( f ) )
1225 {
1226 if ( !f.hasGeometry() )
1227 return;
1228
1229 if ( mContext )
1230 {
1231 std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
1232 QgsRenderContext *ctx = nullptr;
1233
1234 mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
1235 ctx = mContext.get();
1236 if ( renderer && ctx )
1237 {
1238 bool pass = false;
1239 renderer->startRender( *ctx, mLayer->fields() );
1240
1241 ctx->expressionContext().setFeature( f );
1242 if ( !renderer->willRenderFeature( f, *ctx ) )
1243 {
1244 pass = true;
1245 }
1246
1247 renderer->stopRender( *ctx );
1248 if ( pass )
1249 return;
1250 }
1251 }
1252
1253 if ( mTransform.isValid() )
1254 {
1255 try
1256 {
1257 QgsGeometry transformedGeom = f.geometry();
1258 transformedGeom.transform( mTransform );
1259 f.setGeometry( transformedGeom );
1260 }
1261 catch ( const QgsException &e )
1262 {
1263 Q_UNUSED( e )
1264 // See https://github.com/qgis/QGIS/issues/20749
1265 QgsDebugError( u"could not transform geometry to map, skipping the snap for it (%1)"_s.arg( e.what() ) );
1266 return;
1267 }
1268 }
1269
1270 const QgsRectangle bbox = f.geometry().boundingBox();
1271 if ( bbox.isFinite() )
1272 {
1273 const SpatialIndex::Region r( QgsSpatialIndexUtils::rectangleToRegion( bbox ) );
1274 mRTree->insertData( 0, nullptr, r, f.id() );
1275
1276 auto it = mGeoms.find( f.id() );
1277 if ( it != mGeoms.end() )
1278 {
1279 delete *it;
1280 *it = new QgsGeometry( f.geometry() );
1281 }
1282 else
1283 {
1284 mGeoms[fid] = new QgsGeometry( f.geometry() );
1285 }
1286 }
1287 }
1288}
1289
1290void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
1291{
1292 if ( mIsIndexing )
1293 {
1294 if ( mAddedFeatures.contains( fid ) )
1295 {
1296 mAddedFeatures.remove( fid );
1297 }
1298 else
1299 {
1300 // will modify index once current indexing is finished
1301 mDeletedFeatures << fid;
1302 }
1303 return;
1304 }
1305
1306 if ( !mRTree )
1307 return; // nothing to do if we are not initialized yet
1308
1309 auto it = mGeoms.find( fid );
1310 if ( it != mGeoms.end() )
1311 {
1312 mRTree->deleteData( QgsSpatialIndexUtils::rectangleToRegion( ( *it )->boundingBox() ), fid );
1313 delete *it;
1314 mGeoms.erase( it );
1315 }
1316
1317}
1318
1319void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
1320{
1321 Q_UNUSED( geom )
1322 onFeatureDeleted( fid );
1323 onFeatureAdded( fid );
1324}
1325
1326void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
1327{
1328 Q_UNUSED( idx )
1329 Q_UNUSED( value )
1330 if ( mContext )
1331 {
1332 onFeatureDeleted( fid );
1333 onFeatureAdded( fid );
1334 }
1335}
1336
1337
1338QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1339{
1340 if ( !prepare( relaxed ) )
1341 return Match();
1342
1343 Match m;
1344 QgsPointLocator_VisitorNearestVertex visitor( this, m, point, filter );
1345 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1346 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1347 if ( m.isValid() && m.distance() > tolerance )
1348 return Match(); // make sure that only match strictly within the tolerance is returned
1349 return m;
1350}
1351
1352QgsPointLocator::Match QgsPointLocator::nearestCentroid( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1353{
1354 if ( !prepare( relaxed ) )
1355 return Match();
1356
1357 Match m;
1358 QgsPointLocator_VisitorNearestCentroid visitor( this, m, point, filter );
1359
1360 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1361 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1362 if ( m.isValid() && m.distance() > tolerance )
1363 return Match(); // make sure that only match strictly within the tolerance is returned
1364 return m;
1365}
1366
1367QgsPointLocator::Match QgsPointLocator::nearestMiddleOfSegment( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1368{
1369 if ( !prepare( relaxed ) )
1370 return Match();
1371
1372 Match m;
1373 QgsPointLocator_VisitorNearestMiddleOfSegment visitor( this, m, point, filter );
1374
1375 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1376 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1377 if ( m.isValid() && m.distance() > tolerance )
1378 return Match(); // make sure that only match strictly within the tolerance is returned
1379 return m;
1380}
1381
1383{
1384 if ( !prepare( relaxed ) )
1385 return Match();
1386
1387 Match m;
1388 QgsPointLocator_VisitorNearestLineEndpoint visitor( this, m, point, filter );
1389
1390 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1391 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1392 if ( m.isValid() && m.distance() > tolerance )
1393 return Match(); // make sure that only match strictly within the tolerance is returned
1394 return m;
1395}
1396
1397QgsPointLocator::Match QgsPointLocator::nearestEdge( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1398{
1399 if ( !prepare( relaxed ) )
1400 return Match();
1401
1402 const Qgis::GeometryType geomType = mLayer->geometryType();
1403 if ( geomType == Qgis::GeometryType::Point )
1404 return Match();
1405
1406 Match m;
1407 QgsPointLocator_VisitorNearestEdge visitor( this, m, point, filter );
1408 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1409 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1410 if ( m.isValid() && m.distance() > tolerance )
1411 return Match(); // make sure that only match strictly within the tolerance is returned
1412 return m;
1413}
1414
1415QgsPointLocator::Match QgsPointLocator::nearestArea( const QgsPointXY &point, double tolerance, MatchFilter *filter, bool relaxed )
1416{
1417 if ( !prepare( relaxed ) )
1418 return Match();
1419
1420 const MatchList mlist = pointInPolygon( point, false, filter );
1421 if ( !mlist.isEmpty() && mlist.at( 0 ).isValid() )
1422 {
1423 return mlist.at( 0 );
1424 }
1425
1426 if ( tolerance == 0 )
1427 {
1428 return Match();
1429 }
1430
1431 // discard point and line layers to keep only polygons
1432 const Qgis::GeometryType geomType = mLayer->geometryType();
1433 if ( geomType == Qgis::GeometryType::Point || geomType == Qgis::GeometryType::Line )
1434 return Match();
1435
1436 // use edges for adding tolerance
1437 const Match m = nearestEdge( point, tolerance, filter );
1438 if ( m.isValid() )
1439 return Match( Area, m.layer(), m.featureId(), m.distance(), m.point() );
1440 else
1441 return Match();
1442}
1443
1444
1446{
1447 if ( !prepare( relaxed ) )
1448 return MatchList();
1449
1450 const Qgis::GeometryType geomType = mLayer->geometryType();
1451 if ( geomType == Qgis::GeometryType::Point )
1452 return MatchList();
1453
1454 MatchList lst;
1455 QgsPointLocator_VisitorEdgesInRect visitor( this, lst, rect, filter );
1456 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1457
1458 return lst;
1459}
1460
1462{
1463 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1464 return edgesInRect( rect, filter, relaxed );
1465}
1466
1468{
1469 if ( !prepare( relaxed ) )
1470 return MatchList();
1471
1472 MatchList lst;
1473 QgsPointLocator_VisitorVerticesInRect visitor( this, lst, rect, filter );
1474 mRTree->intersectsWithQuery( QgsSpatialIndexUtils::rectangleToRegion( rect ), visitor );
1475
1476 return lst;
1477}
1478
1480{
1481 const QgsRectangle rect( point.x() - tolerance, point.y() - tolerance, point.x() + tolerance, point.y() + tolerance );
1482 return verticesInRect( rect, filter, relaxed );
1483}
1484
1486{
1487 // TODO QGIS 5: reorder relaxed & filter parameters to match other methods' signatures
1488 if ( !prepare( relaxed ) )
1489 return MatchList();
1490
1491 const Qgis::GeometryType geomType = mLayer->geometryType();
1492 if ( geomType == Qgis::GeometryType::Point || geomType == Qgis::GeometryType::Line )
1493 return MatchList();
1494
1495 MatchList lst;
1496 QgsPointLocator_VisitorArea visitor( this, point, lst, filter );
1497 mRTree->intersectsWithQuery( point2point( point ), visitor );
1498 return lst;
1499}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:365
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
@ Unknown
Unknown types.
Definition qgis.h:369
@ Null
No geometry.
Definition qgis.h:370
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
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:188
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:244
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