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