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