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