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