QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
qgsgeometry.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometry.cpp - Geometry (stored as Open Geospatial Consortium WKB)
3 -------------------------------------------------------------------
4Date : 02 May 2005
5Copyright : (C) 2005 by Brendan Morley
6email : morb at ozemail dot com dot au
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 <limits>
17#include <cstdarg>
18#include <cstdio>
19#include <cmath>
20#include <nlohmann/json.hpp>
21#include <QCache>
22
23#include "qgis.h"
24#include "qgsgeometry.h"
26#include "qgsgeometryfactory.h"
27
28#include <geos_c.h>
29
30#include "qgsgeometryutils.h"
32#include "qgsgeos.h"
33#include "qgsmaptopixel.h"
34#include "qgspointxy.h"
35#include "qgsrectangle.h"
36
37#include "qgsvectorlayer.h"
39
40#include "qgsmultilinestring.h"
41#include "qgsmultipoint.h"
42#include "qgsmultipolygon.h"
43#include "qgspoint.h"
44#include "qgspolygon.h"
45#include "qgslinestring.h"
46#include "qgscircle.h"
47#include "qgscurve.h"
48
50{
52 QAtomicInt ref;
53 std::unique_ptr< QgsAbstractGeometry > geometry;
54};
55
60
62{
63 if ( !d->ref.deref() )
64 delete d;
65}
66
68 : d( new QgsGeometryPrivate() )
69{
70 d->geometry.reset( geom );
71 d->ref = QAtomicInt( 1 );
72}
73
74QgsGeometry::QgsGeometry( std::unique_ptr<QgsAbstractGeometry> geom )
75 : d( new QgsGeometryPrivate() )
76{
77 d->geometry = std::move( geom );
78 d->ref = QAtomicInt( 1 );
79}
80
82 : d( other.d )
83{
84 mLastError = other.mLastError;
85 d->ref.ref();
86}
87
89{
90 if ( this != &other )
91 {
92 if ( !d->ref.deref() )
93 {
94 delete d;
95 }
96
97 mLastError = other.mLastError;
98 d = other.d;
99 d->ref.ref();
100 }
101 return *this;
102}
103
104void QgsGeometry::detach()
105{
106 if ( d->ref <= 1 )
107 return;
108
109 std::unique_ptr< QgsAbstractGeometry > cGeom;
110 if ( d->geometry )
111 cGeom.reset( d->geometry->clone() );
112
113 reset( std::move( cGeom ) );
114}
115
116void QgsGeometry::reset( std::unique_ptr<QgsAbstractGeometry> newGeometry )
117{
118 if ( d->ref > 1 )
119 {
120 ( void )d->ref.deref();
121 d = new QgsGeometryPrivate();
122 }
123 d->geometry = std::move( newGeometry );
124}
125
127{
128 return d->geometry.get();
129}
130
132{
133 detach();
134 return d->geometry.get();
135}
136
138{
139 if ( d->geometry.get() == geometry )
140 {
141 return;
142 }
143
144 reset( std::unique_ptr< QgsAbstractGeometry >( geometry ) );
145}
146
148{
149 return !d->geometry;
150}
151
152typedef QCache< QString, QgsGeometry > WktCache;
153Q_GLOBAL_STATIC_WITH_ARGS( WktCache, sWktCache, ( 2000 ) ) // store up to 2000 geometries
154Q_GLOBAL_STATIC( QMutex, sWktMutex )
155
156QgsGeometry QgsGeometry::fromWkt( const QString &wkt )
157{
158 QMutexLocker lock( sWktMutex() );
159 if ( const QgsGeometry *cached = sWktCache()->object( wkt ) )
160 return *cached;
161 const QgsGeometry result( QgsGeometryFactory::geomFromWkt( wkt ) );
162 sWktCache()->insert( wkt, new QgsGeometry( result ), 1 );
163 return result;
164}
165
167{
168 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::fromPointXY( point ) );
169 if ( geom )
170 {
171 return QgsGeometry( geom.release() );
172 }
173 return QgsGeometry();
174}
175
177{
178 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolylineXY( polyline );
179 if ( geom )
180 {
181 return QgsGeometry( std::move( geom ) );
182 }
183 return QgsGeometry();
184}
185
187{
188 return QgsGeometry( std::make_unique< QgsLineString >( polyline ) );
189}
190
192{
193 std::unique_ptr< QgsPolygon > geom = QgsGeometryFactory::fromPolygonXY( polygon );
194 if ( geom )
195 {
196 return QgsGeometry( std::move( geom ) );
197 }
198 return QgsGeometry();
199}
200
202{
203 std::unique_ptr< QgsMultiPoint > geom = QgsGeometryFactory::fromMultiPointXY( multipoint );
204 if ( geom )
205 {
206 return QgsGeometry( std::move( geom ) );
207 }
208 return QgsGeometry();
209}
210
212{
213 std::unique_ptr< QgsMultiLineString > geom = QgsGeometryFactory::fromMultiPolylineXY( multiline );
214 if ( geom )
215 {
216 return QgsGeometry( std::move( geom ) );
217 }
218 return QgsGeometry();
219}
220
222{
223 std::unique_ptr< QgsMultiPolygon > geom = QgsGeometryFactory::fromMultiPolygonXY( multipoly );
224 if ( geom )
225 {
226 return QgsGeometry( std::move( geom ) );
227 }
228 return QgsGeometry();
229}
230
232{
233 std::unique_ptr< QgsLineString > ext = std::make_unique< QgsLineString >(
234 QVector< double >() << rect.xMinimum()
235 << rect.xMaximum()
236 << rect.xMaximum()
237 << rect.xMinimum()
238 << rect.xMinimum(),
239 QVector< double >() << rect.yMinimum()
240 << rect.yMinimum()
241 << rect.yMaximum()
242 << rect.yMaximum()
243 << rect.yMinimum() );
244 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
245 polygon->setExteriorRing( ext.release() );
246 return QgsGeometry( std::move( polygon ) );
247}
248
249QgsGeometry QgsGeometry::collectGeometry( const QVector< QgsGeometry > &geometries )
250{
251 QgsGeometry collected;
252
253 for ( const QgsGeometry &g : geometries )
254 {
255 if ( collected.isNull() )
256 {
257 collected = g;
258 collected.convertToMultiType();
259 }
260 else
261 {
262 if ( g.isMultipart() )
263 {
264 for ( auto p = g.const_parts_begin(); p != g.const_parts_end(); ++p )
265 {
266 collected.addPart( ( *p )->clone() );
267 }
268 }
269 else
270 {
271 collected.addPart( g );
272 }
273 }
274 }
275 return collected;
276}
277
278QgsGeometry QgsGeometry::createWedgeBuffer( const QgsPoint &center, const double azimuth, const double angularWidth, const double outerRadius, const double innerRadius )
279{
280 if ( std::abs( angularWidth ) >= 360.0 )
281 {
282 std::unique_ptr< QgsCompoundCurve > outerCc = std::make_unique< QgsCompoundCurve >();
283
284 QgsCircle outerCircle = QgsCircle( center, outerRadius );
285 outerCc->addCurve( outerCircle.toCircularString() );
286
287 std::unique_ptr< QgsCurvePolygon > cp = std::make_unique< QgsCurvePolygon >();
288 cp->setExteriorRing( outerCc.release() );
289
290 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
291 {
292 std::unique_ptr< QgsCompoundCurve > innerCc = std::make_unique< QgsCompoundCurve >();
293
294 QgsCircle innerCircle = QgsCircle( center, innerRadius );
295 innerCc->addCurve( innerCircle.toCircularString() );
296
297 cp->setInteriorRings( { innerCc.release() } );
298 }
299
300 return QgsGeometry( std::move( cp ) );
301 }
302
303 std::unique_ptr< QgsCompoundCurve > wedge = std::make_unique< QgsCompoundCurve >();
304
305 const double startAngle = azimuth - angularWidth * 0.5;
306 const double endAngle = azimuth + angularWidth * 0.5;
307
308 const QgsPoint outerP1 = center.project( outerRadius, startAngle );
309 const QgsPoint outerP2 = center.project( outerRadius, endAngle );
310
311 const bool useShortestArc = angularWidth <= 180.0;
312
313 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( outerP1, outerP2, center, useShortestArc ) ) );
314
315 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
316 {
317 const QgsPoint innerP1 = center.project( innerRadius, startAngle );
318 const QgsPoint innerP2 = center.project( innerRadius, endAngle );
319 wedge->addCurve( new QgsLineString( outerP2, innerP2 ) );
320 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( innerP2, innerP1, center, useShortestArc ) ) );
321 wedge->addCurve( new QgsLineString( innerP1, outerP1 ) );
322 }
323 else
324 {
325 wedge->addCurve( new QgsLineString( outerP2, center ) );
326 wedge->addCurve( new QgsLineString( center, outerP1 ) );
327 }
328
329 std::unique_ptr< QgsCurvePolygon > cp = std::make_unique< QgsCurvePolygon >();
330 cp->setExteriorRing( wedge.release() );
331 return QgsGeometry( std::move( cp ) );
332}
333
334void QgsGeometry::fromWkb( unsigned char *wkb, int length )
335{
336 QgsConstWkbPtr ptr( wkb, length );
337 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
338 delete [] wkb;
339}
340
341void QgsGeometry::fromWkb( const QByteArray &wkb )
342{
343 QgsConstWkbPtr ptr( wkb );
344 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
345}
346
348{
349 if ( !d->geometry )
350 {
352 }
353 else
354 {
355 return d->geometry->wkbType();
356 }
357}
358
359
361{
362 if ( !d->geometry )
363 {
365 }
366 return static_cast< QgsWkbTypes::GeometryType >( QgsWkbTypes::geometryType( d->geometry->wkbType() ) );
367}
368
370{
371 if ( !d->geometry )
372 {
373 return true;
374 }
375
376 return d->geometry->isEmpty();
377}
378
380{
381 if ( !d->geometry )
382 {
383 return false;
384 }
385 return QgsWkbTypes::isMultiType( d->geometry->wkbType() );
386}
387QgsPointXY QgsGeometry::closestVertex( const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist ) const
388{
389 if ( !d->geometry )
390 {
391 sqrDist = -1;
392 return QgsPointXY();
393 }
394
395 QgsPoint pt( point );
396 QgsVertexId id;
397
398 QgsPoint vp = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, id );
399 if ( !id.isValid() )
400 {
401 sqrDist = -1;
402 return QgsPointXY();
403 }
404 sqrDist = QgsGeometryUtils::sqrDistance2D( pt, vp );
405
406 QgsVertexId prevVertex;
407 QgsVertexId nextVertex;
408 d->geometry->adjacentVertices( id, prevVertex, nextVertex );
409 closestVertexIndex = vertexNrFromVertexId( id );
410 previousVertexIndex = vertexNrFromVertexId( prevVertex );
411 nextVertexIndex = vertexNrFromVertexId( nextVertex );
412 return QgsPointXY( vp.x(), vp.y() );
413}
414
415double QgsGeometry::distanceToVertex( int vertex ) const
416{
417 if ( !d->geometry )
418 {
419 return -1;
420 }
421
422 QgsVertexId id;
423 if ( !vertexIdFromVertexNr( vertex, id ) )
424 {
425 return -1;
426 }
427
428 return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id );
429}
430
431double QgsGeometry::angleAtVertex( int vertex ) const
432{
433 if ( !d->geometry )
434 {
435 return 0;
436 }
437
438 QgsVertexId v2;
439 if ( !vertexIdFromVertexNr( vertex, v2 ) )
440 {
441 return 0;
442 }
443
444 return d->geometry->vertexAngle( v2 );
445}
446
447void QgsGeometry::adjacentVertices( int atVertex, int &beforeVertex, int &afterVertex ) const
448{
449 if ( !d->geometry )
450 {
451 return;
452 }
453
454 QgsVertexId id;
455 if ( !vertexIdFromVertexNr( atVertex, id ) )
456 {
457 beforeVertex = -1;
458 afterVertex = -1;
459 return;
460 }
461
462 QgsVertexId beforeVertexId, afterVertexId;
463 d->geometry->adjacentVertices( id, beforeVertexId, afterVertexId );
464 beforeVertex = vertexNrFromVertexId( beforeVertexId );
465 afterVertex = vertexNrFromVertexId( afterVertexId );
466}
467
468bool QgsGeometry::moveVertex( double x, double y, int atVertex )
469{
470 if ( !d->geometry )
471 {
472 return false;
473 }
474
475 QgsVertexId id;
476 if ( !vertexIdFromVertexNr( atVertex, id ) )
477 {
478 return false;
479 }
480
481 detach();
482
483 return d->geometry->moveVertex( id, QgsPoint( x, y ) );
484}
485
486bool QgsGeometry::moveVertex( const QgsPoint &p, int atVertex )
487{
488 if ( !d->geometry )
489 {
490 return false;
491 }
492
493 QgsVertexId id;
494 if ( !vertexIdFromVertexNr( atVertex, id ) )
495 {
496 return false;
497 }
498
499 detach();
500
501 return d->geometry->moveVertex( id, p );
502}
503
504bool QgsGeometry::deleteVertex( int atVertex )
505{
506 if ( !d->geometry )
507 {
508 return false;
509 }
510
511 //maintain compatibility with < 2.10 API
513 {
514 detach();
515 //delete geometry instead of point
516 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->removeGeometry( atVertex );
517 }
518
519 //if it is a point, set the geometry to nullptr
520 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
521 {
522 reset( nullptr );
523 return true;
524 }
525
526 QgsVertexId id;
527 if ( !vertexIdFromVertexNr( atVertex, id ) )
528 {
529 return false;
530 }
531
532 detach();
533
534 return d->geometry->deleteVertex( id );
535}
536
538{
539
540 if ( !d->geometry )
541 return false;
542
543 QgsVertexId id;
544 if ( !vertexIdFromVertexNr( atVertex, id ) )
545 return false;
546
547 detach();
548
549 QgsAbstractGeometry *geom = d->geometry.get();
550
551 // If the geom is a collection, we get the concerned part, otherwise, the part is just the whole geom
552 QgsAbstractGeometry *part = nullptr;
553 QgsGeometryCollection *owningCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
554 if ( owningCollection != nullptr )
555 part = owningCollection->geometryN( id.part );
556 else
557 part = geom;
558
559 // If the part is a polygon, we get the concerned ring, otherwise, the ring is just the whole part
560 QgsAbstractGeometry *ring = nullptr;
561 QgsCurvePolygon *owningPolygon = qgsgeometry_cast<QgsCurvePolygon *>( part );
562 if ( owningPolygon != nullptr )
563 ring = ( id.ring == 0 ) ? owningPolygon->exteriorRing() : owningPolygon->interiorRing( id.ring - 1 );
564 else
565 ring = part;
566
567 // If the ring is not a curve, we're probably on a point geometry
568 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( ring );
569 if ( curve == nullptr )
570 return false;
571
572 bool success = false;
573 QgsCompoundCurve *cpdCurve = qgsgeometry_cast<QgsCompoundCurve *>( curve );
574 if ( cpdCurve != nullptr )
575 {
576 // If the geom is a already compound curve, we convert inplace, and we're done
577 success = cpdCurve->toggleCircularAtVertex( id );
578 }
579 else
580 {
581 // TODO : move this block before the above, so we call toggleCircularAtVertex only in one place
582 // If the geom is a linestring or cirularstring, we create a compound curve
583 std::unique_ptr<QgsCompoundCurve> cpdCurve = std::make_unique<QgsCompoundCurve>();
584 cpdCurve->addCurve( curve->clone() );
585 success = cpdCurve->toggleCircularAtVertex( QgsVertexId( -1, -1, id.vertex ) );
586
587 // In that case, we must also reassign the instances
588 if ( success )
589 {
590
591 if ( owningPolygon == nullptr && owningCollection == nullptr )
592 {
593 // Standalone linestring
594 reset( std::make_unique<QgsCompoundCurve>( *cpdCurve ) ); // <- REVIEW PLZ
595 }
596 else if ( owningPolygon != nullptr )
597 {
598 // Replace the ring in the owning polygon
599 if ( id.ring == 0 )
600 {
601 owningPolygon->setExteriorRing( cpdCurve.release() );
602 }
603 else
604 {
605 owningPolygon->removeInteriorRing( id.ring - 1 );
606 owningPolygon->addInteriorRing( cpdCurve.release() );
607 }
608 }
609 else if ( owningCollection != nullptr )
610 {
611 // Replace the curve in the owning collection
612 owningCollection->removeGeometry( id.part );
613 owningCollection->insertGeometry( cpdCurve.release(), id.part );
614 }
615 }
616 }
617
618 return success;
619}
620
621bool QgsGeometry::insertVertex( double x, double y, int beforeVertex )
622{
623 if ( !d->geometry )
624 {
625 return false;
626 }
627
628 //maintain compatibility with < 2.10 API
630 {
631 detach();
632 //insert geometry instead of point
633 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( x, y ), beforeVertex );
634 }
635
636 QgsVertexId id;
637 if ( !vertexIdFromVertexNr( beforeVertex, id ) )
638 {
639 return false;
640 }
641
642 detach();
643
644 return d->geometry->insertVertex( id, QgsPoint( x, y ) );
645}
646
647bool QgsGeometry::insertVertex( const QgsPoint &point, int beforeVertex )
648{
649 if ( !d->geometry )
650 {
651 return false;
652 }
653
654 //maintain compatibility with < 2.10 API
656 {
657 detach();
658 //insert geometry instead of point
659 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( point ), beforeVertex );
660 }
661
662 QgsVertexId id;
663 if ( !vertexIdFromVertexNr( beforeVertex, id ) )
664 {
665 return false;
666 }
667
668 detach();
669
670 return d->geometry->insertVertex( id, point );
671}
672
673QgsPoint QgsGeometry::vertexAt( int atVertex ) const
674{
675 if ( !d->geometry )
676 {
677 return QgsPoint();
678 }
679
680 QgsVertexId vId;
681 ( void )vertexIdFromVertexNr( atVertex, vId );
682 if ( vId.vertex < 0 )
683 {
684 return QgsPoint();
685 }
686 return d->geometry->vertexAt( vId );
687}
688
689double QgsGeometry::sqrDistToVertexAt( QgsPointXY &point, int atVertex ) const
690{
691 QgsPointXY vertexPoint = vertexAt( atVertex );
692 return QgsGeometryUtils::sqrDistance2D( QgsPoint( vertexPoint ), QgsPoint( point ) );
693}
694
696{
697 // avoid calling geos for trivial point calculations
698 if ( d->geometry && QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
699 {
700 return QgsGeometry( qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->clone() );
701 }
702
703 QgsGeos geos( d->geometry.get() );
704 mLastError.clear();
705 QgsGeometry result = geos.closestPoint( other );
706 result.mLastError = mLastError;
707 return result;
708}
709
711{
712 // avoid calling geos for trivial point-to-point line calculations
714 {
715 return QgsGeometry( std::make_unique< QgsLineString >( *qgsgeometry_cast< const QgsPoint * >( d->geometry.get() ), *qgsgeometry_cast< const QgsPoint * >( other.constGet() ) ) );
716 }
717
718 QgsGeos geos( d->geometry.get() );
719 mLastError.clear();
720 QgsGeometry result = geos.shortestLine( other, &mLastError );
721 result.mLastError = mLastError;
722 return result;
723}
724
725double QgsGeometry::closestVertexWithContext( const QgsPointXY &point, int &atVertex ) const
726{
727 if ( !d->geometry )
728 {
729 return -1;
730 }
731
732 QgsVertexId vId;
733 QgsPoint pt( point );
734 QgsPoint closestPoint = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, vId );
735 if ( !vId.isValid() )
736 return -1;
737 atVertex = vertexNrFromVertexId( vId );
738 return QgsGeometryUtils::sqrDistance2D( closestPoint, pt );
739}
740
742 QgsPointXY &minDistPoint,
743 int &nextVertexIndex,
744 int *leftOrRightOfSegment,
745 double epsilon ) const
746{
747 if ( !d->geometry )
748 {
749 return -1;
750 }
751
752 QgsPoint segmentPt;
753 QgsVertexId vertexAfter;
754
755 double sqrDist = d->geometry->closestSegment( QgsPoint( point ), segmentPt, vertexAfter, leftOrRightOfSegment, epsilon );
756 if ( sqrDist < 0 )
757 return -1;
758
759 minDistPoint.setX( segmentPt.x() );
760 minDistPoint.setY( segmentPt.y() );
761 nextVertexIndex = vertexNrFromVertexId( vertexAfter );
762 return sqrDist;
763}
764
765Qgis::GeometryOperationResult QgsGeometry::addRing( const QVector<QgsPointXY> &ring )
766{
767 std::unique_ptr< QgsLineString > ringLine = std::make_unique< QgsLineString >( ring );
768 return addRing( ringLine.release() );
769}
770
772{
773 std::unique_ptr< QgsCurve > r( ring );
774 if ( !d->geometry )
775 {
777 }
778
779 detach();
780
781 return QgsGeometryEditUtils::addRing( d->geometry.get(), std::move( r ) );
782}
783
785{
787 convertPointList( points, l );
788 return addPart( l, geomType );
789}
790
792{
793 std::unique_ptr< QgsAbstractGeometry > partGeom;
794 if ( points.size() == 1 )
795 {
796 partGeom = std::make_unique< QgsPoint >( points[0] );
797 }
798 else if ( points.size() > 1 )
799 {
800 std::unique_ptr< QgsLineString > ringLine = std::make_unique< QgsLineString >();
801 ringLine->setPoints( points );
802 partGeom = std::move( ringLine );
803 }
804 return addPart( partGeom.release(), geomType );
805}
806
808{
809 std::unique_ptr< QgsAbstractGeometry > p( part );
810 if ( !d->geometry )
811 {
812 switch ( geomType )
813 {
815 reset( std::make_unique< QgsMultiPoint >() );
816 break;
818 reset( std::make_unique< QgsMultiLineString >() );
819 break;
821 reset( std::make_unique< QgsMultiPolygon >() );
822 break;
823 default:
824 reset( nullptr );
826 }
827 }
828 else
829 {
830 detach();
831 }
832
834 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
835}
836
838{
839 if ( !d->geometry )
840 {
842 }
843 if ( newPart.isNull() || !newPart.d->geometry )
844 {
846 }
847
848 return addPart( newPart.d->geometry->clone() );
849}
850
851QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
852{
854 {
855 return QgsGeometry();
856 }
857
858 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
859 {
860 const QVector<QgsGeometry> parts = asGeometryCollection();
861 QVector<QgsGeometry> results;
862 results.reserve( parts.count() );
863 for ( const QgsGeometry &part : parts )
864 {
865 QgsGeometry result = part.removeInteriorRings( minimumRingArea );
866 if ( !result.isNull() )
867 results << result;
868 }
869 if ( results.isEmpty() )
870 return QgsGeometry();
871
872 QgsGeometry first = results.takeAt( 0 );
873 for ( const QgsGeometry &result : std::as_const( results ) )
874 {
875 first.addPart( result );
876 }
877 return first;
878 }
879 else
880 {
881 std::unique_ptr< QgsCurvePolygon > newPoly( static_cast< QgsCurvePolygon * >( d->geometry->clone() ) );
882 newPoly->removeInteriorRings( minimumRingArea );
883 return QgsGeometry( std::move( newPoly ) );
884 }
885}
886
887Qgis::GeometryOperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
888{
889 if ( !d->geometry )
890 {
892 }
893
894 detach();
895
896 d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
898}
899
901{
902 if ( !d->geometry )
903 {
905 }
906
907 detach();
908
909 QTransform t = QTransform::fromTranslate( center.x(), center.y() );
910 t.rotate( -rotation );
911 t.translate( -center.x(), -center.y() );
912 d->geometry->transform( t );
914}
915
916Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature )
917{
918 QgsPointSequence split, topology;
919 convertPointList( splitLine, split );
920 convertPointList( topologyTestPoints, topology );
921 Qgis::GeometryOperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
922 convertPointList( topology, topologyTestPoints );
923 return result;
924}
925Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest )
926{
927 if ( !d->geometry )
928 {
930 }
931
932 QVector<QgsGeometry > newGeoms;
933 QgsLineString splitLineString( splitLine );
934
935 QgsGeos geos( d->geometry.get() );
936 mLastError.clear();
937 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
938
939 if ( result == QgsGeometryEngine::Success )
940 {
941 if ( splitFeature )
942 *this = newGeoms.takeAt( 0 );
943 newGeometries = newGeoms;
944 }
945
946 switch ( result )
947 {
962 //default: do not implement default to handle properly all cases
963 }
964
965 // this should never be reached
966 Q_ASSERT( false );
968}
969
970Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature )
971{
972 std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
973 QgsPointSequence points;
974 segmentizedLine->points( points );
975 Qgis::GeometryOperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
976
978 {
979 if ( preserveCircular )
980 {
981 for ( int i = 0; i < newGeometries.count(); ++i )
982 newGeometries[i] = newGeometries[i].convertToCurves();
983 *this = convertToCurves();
984 }
985 }
986
987 return result;
988}
989
991{
992 if ( !d->geometry )
993 {
995 }
996
997 QgsGeos geos( d->geometry.get() );
999 mLastError.clear();
1000 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1001 if ( errorCode == QgsGeometryEngine::Success && geom )
1002 {
1003 reset( std::move( geom ) );
1005 }
1006
1007 switch ( errorCode )
1008 {
1019 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1023 }
1024
1025 // should not be reached
1027}
1028
1030{
1031 if ( !d->geometry || !other.d->geometry )
1032 {
1033 return 0;
1034 }
1035
1036 QgsGeos geos( d->geometry.get() );
1037
1038 mLastError.clear();
1039 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1040 if ( !diffGeom )
1041 {
1042 return 1;
1043 }
1044
1045 reset( std::move( diffGeom ) );
1046 return 0;
1047}
1048
1050{
1051 if ( !d->geometry || other.isNull() )
1052 {
1053 return QgsGeometry();
1054 }
1055
1056 QgsGeos geos( d->geometry.get() );
1057
1058 mLastError.clear();
1059 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1060 if ( !diffGeom )
1061 {
1062 QgsGeometry result;
1063 result.mLastError = mLastError;
1064 return result;
1065 }
1066
1067 return QgsGeometry( diffGeom.release() );
1068}
1069
1071{
1072 if ( d->geometry )
1073 {
1074 return d->geometry->boundingBox();
1075 }
1076 return QgsRectangle();
1077}
1078
1079QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1080{
1081 mLastError.clear();
1082 QgsInternalGeometryEngine engine( *this );
1083 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1084 if ( res.isNull() )
1085 mLastError = engine.lastError();
1086 return res;
1087}
1088
1090{
1091 double area, angle, width, height;
1092 return orientedMinimumBoundingBox( area, angle, width, height );
1093}
1094
1095static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1096{
1097 auto l_boundary = boundary.length();
1098 QgsCircle circ_mec;
1099 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1100 {
1101 switch ( l_boundary )
1102 {
1103 case 0:
1104 circ_mec = QgsCircle();
1105 break;
1106 case 1:
1107 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1108 boundary.pop_back();
1109 break;
1110 case 2:
1111 {
1112 QgsPointXY p1 = boundary.last();
1113 boundary.pop_back();
1114 QgsPointXY p2 = boundary.last();
1115 boundary.pop_back();
1116 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1117 }
1118 break;
1119 default:
1120 QgsPoint p1( boundary.at( 0 ) );
1121 QgsPoint p2( boundary.at( 1 ) );
1122 QgsPoint p3( boundary.at( 2 ) );
1123 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1124 break;
1125 }
1126 return circ_mec;
1127 }
1128 else
1129 {
1130 QgsPointXY pxy = points.last();
1131 points.pop_back();
1132 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1133 QgsPoint p( pxy );
1134 if ( !circ_mec.contains( p ) )
1135 {
1136 boundary.append( pxy );
1137 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1138 }
1139 }
1140 return circ_mec;
1141}
1142
1143QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1144{
1145 center = QgsPointXY();
1146 radius = 0;
1147
1148 if ( isEmpty() )
1149 {
1150 return QgsGeometry();
1151 }
1152
1153 /* optimization */
1154 QgsGeometry hull = convexHull();
1155 if ( hull.isNull() )
1156 return QgsGeometry();
1157
1158 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1160
1161 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1162 center = QgsPointXY( circ.center() );
1163 radius = circ.radius();
1164 QgsGeometry geom;
1165 geom.set( circ.toPolygon( segments ) );
1166 return geom;
1167
1168}
1169
1171{
1172 QgsPointXY center;
1173 double radius;
1174 return minimalEnclosingCircle( center, radius, segments );
1175
1176}
1177
1178QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1179{
1180 QgsInternalGeometryEngine engine( *this );
1181
1182 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1183}
1184
1185QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1186{
1187 QgsInternalGeometryEngine engine( *this );
1188 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1189}
1190
1191QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1192{
1193 QgsInternalGeometryEngine engine( *this );
1194 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1195}
1196
1197QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1198{
1199 QgsInternalGeometryEngine engine( *this );
1200 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1201}
1202
1203QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1204{
1205 QgsInternalGeometryEngine engine( *this );
1206 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1207}
1208
1209QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1210{
1211 QgsInternalGeometryEngine engine( *this );
1212 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1213}
1214
1215QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1216{
1217 QgsInternalGeometryEngine engine( *this );
1218 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1219}
1220
1221QgsGeometry QgsGeometry::applyDashPattern( const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset ) const
1222{
1223 QgsInternalGeometryEngine engine( *this );
1224 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1225}
1226
1227QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1228{
1229 if ( !d->geometry )
1230 {
1231 return QgsGeometry();
1232 }
1233 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1234}
1235
1236bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1237{
1238 if ( !d->geometry )
1239 return false;
1240
1241 detach();
1242 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1243}
1244
1246{
1247 // fast case, check bounding boxes
1248 if ( !boundingBoxIntersects( r ) )
1249 return false;
1250
1251 const QgsWkbTypes::Type flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1252 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1253 if ( flatType == QgsWkbTypes::Point )
1254 {
1255 return true;
1256 }
1257
1258 // Workaround for issue issue GH #51429
1259 // in case of multi polygon, intersection with an empty rect fails
1260 if ( flatType == QgsWkbTypes::MultiPolygon && r.isEmpty() )
1261 {
1262 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1263 return contains( QgsGeometry::fromPointXY( center ) );
1264 }
1265
1266 QgsGeometry g = fromRect( r );
1267 return intersects( g );
1268}
1269
1270bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1271{
1272 if ( !d->geometry || geometry.isNull() )
1273 {
1274 return false;
1275 }
1276
1277 QgsGeos geos( d->geometry.get() );
1278 mLastError.clear();
1279 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1280}
1281
1283{
1284 if ( !d->geometry )
1285 {
1286 return false;
1287 }
1288
1289 return d->geometry->boundingBoxIntersects( rectangle );
1290}
1291
1293{
1294 if ( !d->geometry || geometry.isNull() )
1295 {
1296 return false;
1297 }
1298
1299 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1300}
1301
1302bool QgsGeometry::contains( const QgsPointXY *p ) const
1303{
1304 if ( !d->geometry || !p )
1305 {
1306 return false;
1307 }
1308
1309 QgsPoint pt( p->x(), p->y() );
1310 QgsGeos geos( d->geometry.get() );
1311 mLastError.clear();
1312 return geos.contains( &pt, &mLastError );
1313}
1314
1315bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1316{
1317 if ( !d->geometry || geometry.isNull() )
1318 {
1319 return false;
1320 }
1321
1322 QgsGeos geos( d->geometry.get() );
1323 mLastError.clear();
1324 return geos.contains( geometry.d->geometry.get(), &mLastError );
1325}
1326
1327bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1328{
1329 if ( !d->geometry || geometry.isNull() )
1330 {
1331 return false;
1332 }
1333
1334 QgsGeos geos( d->geometry.get() );
1335 mLastError.clear();
1336 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1337}
1338
1339bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1340{
1341 if ( !d->geometry || geometry.isNull() )
1342 {
1343 return false;
1344 }
1345
1346 // fast check - are they shared copies of the same underlying geometry?
1347 if ( d == geometry.d )
1348 return true;
1349
1350 // fast check - distinct geometry types?
1351 if ( type() != geometry.type() )
1352 return false;
1353
1354 // slower check - actually test the geometries
1355 return *d->geometry == *geometry.d->geometry;
1356}
1357
1358bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1359{
1360 if ( !d->geometry || geometry.isNull() )
1361 {
1362 return false;
1363 }
1364
1365 QgsGeos geos( d->geometry.get() );
1366 mLastError.clear();
1367 return geos.touches( geometry.d->geometry.get(), &mLastError );
1368}
1369
1370bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1371{
1372 if ( !d->geometry || geometry.isNull() )
1373 {
1374 return false;
1375 }
1376
1377 QgsGeos geos( d->geometry.get() );
1378 mLastError.clear();
1379 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1380}
1381
1382bool QgsGeometry::within( const QgsGeometry &geometry ) const
1383{
1384 if ( !d->geometry || geometry.isNull() )
1385 {
1386 return false;
1387 }
1388
1389 QgsGeos geos( d->geometry.get() );
1390 mLastError.clear();
1391 return geos.within( geometry.d->geometry.get(), &mLastError );
1392}
1393
1394bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1395{
1396 if ( !d->geometry || geometry.isNull() )
1397 {
1398 return false;
1399 }
1400
1401 QgsGeos geos( d->geometry.get() );
1402 mLastError.clear();
1403 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1404}
1405
1406QString QgsGeometry::asWkt( int precision ) const
1407{
1408 if ( !d->geometry )
1409 {
1410 return QString();
1411 }
1412 return d->geometry->asWkt( precision );
1413}
1414
1415QString QgsGeometry::asJson( int precision ) const
1416{
1417 return QString::fromStdString( asJsonObject( precision ).dump() );
1418}
1419
1421{
1422 if ( !d->geometry )
1423 {
1424 return nullptr;
1425 }
1426 return d->geometry->asJsonObject( precision );
1427
1428}
1429
1430QVector<QgsGeometry> QgsGeometry::coerceToType( const QgsWkbTypes::Type type, double defaultZ, double defaultM ) const
1431{
1432 QVector< QgsGeometry > res;
1433 if ( isNull() )
1434 return res;
1435
1436 if ( wkbType() == type || type == QgsWkbTypes::Unknown )
1437 {
1438 res << *this;
1439 return res;
1440 }
1441
1443 {
1444 return res;
1445 }
1446
1447 QgsGeometry newGeom = *this;
1448
1449 // Curved -> straight
1451 {
1452 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1453 }
1454
1455 // polygon -> line
1457 newGeom.type() == QgsWkbTypes::PolygonGeometry )
1458 {
1459 // boundary gives us a (multi)line string of exterior + interior rings
1460 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1461 }
1462 // line -> polygon
1464 newGeom.type() == QgsWkbTypes::LineGeometry )
1465 {
1466 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1467 const QgsGeometry source = newGeom;
1468 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1469 {
1470 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1471 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1472 {
1474 {
1475 std::unique_ptr< QgsCurvePolygon > cp = std::make_unique< QgsCurvePolygon >();
1476 cp->setExteriorRing( curve );
1477 exterior.release();
1478 gc->addGeometry( cp.release() );
1479 }
1480 else
1481 {
1482 std::unique_ptr< QgsPolygon > p = std::make_unique< QgsPolygon >();
1483 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1484 exterior.release();
1485 gc->addGeometry( p.release() );
1486 }
1487 }
1488 }
1489 newGeom = QgsGeometry( std::move( gc ) );
1490 }
1491
1492 // line/polygon -> points
1494 ( newGeom.type() == QgsWkbTypes::LineGeometry ||
1495 newGeom.type() == QgsWkbTypes::PolygonGeometry ) )
1496 {
1497 // lines/polygons to a point layer, extract all vertices
1498 std::unique_ptr< QgsMultiPoint > mp = std::make_unique< QgsMultiPoint >();
1499 const QgsGeometry source = newGeom;
1500 QSet< QgsPoint > added;
1501 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1502 {
1503 if ( added.contains( *vertex ) )
1504 continue; // avoid duplicate points, e.g. start/end of rings
1505 mp->addGeometry( ( *vertex ).clone() );
1506 added.insert( *vertex );
1507 }
1508 newGeom = QgsGeometry( std::move( mp ) );
1509 }
1510
1511 // Single -> multi
1512 if ( QgsWkbTypes::isMultiType( type ) && ! newGeom.isMultipart( ) )
1513 {
1514 newGeom.convertToMultiType();
1515 }
1516 // Drop Z/M
1517 if ( newGeom.constGet()->is3D() && ! QgsWkbTypes::hasZ( type ) )
1518 {
1519 newGeom.get()->dropZValue();
1520 }
1521 if ( newGeom.constGet()->isMeasure() && ! QgsWkbTypes::hasM( type ) )
1522 {
1523 newGeom.get()->dropMValue();
1524 }
1525 // Add Z/M back, set to 0
1526 if ( ! newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
1527 {
1528 newGeom.get()->addZValue( defaultZ );
1529 }
1530 if ( ! newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
1531 {
1532 newGeom.get()->addMValue( defaultM );
1533 }
1534
1535 // Multi -> single
1536 if ( ! QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart( ) )
1537 {
1538 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1539 res.reserve( parts->partCount() );
1540 for ( int i = 0; i < parts->partCount( ); i++ )
1541 {
1542 res << QgsGeometry( parts->geometryN( i )->clone() );
1543 }
1544 }
1545 else
1546 {
1547 res << newGeom;
1548 }
1549 return res;
1550}
1551
1553{
1554 switch ( destType )
1555 {
1557 return convertToPoint( destMultipart );
1558
1560 return convertToLine( destMultipart );
1561
1563 return convertToPolygon( destMultipart );
1564
1565 default:
1566 return QgsGeometry();
1567 }
1568}
1569
1571{
1572 if ( !d->geometry )
1573 {
1574 return false;
1575 }
1576
1577 if ( isMultipart() ) //already multitype, no need to convert
1578 {
1579 return true;
1580 }
1581
1582 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
1583 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1584 if ( !multiGeom )
1585 {
1586 return false;
1587 }
1588
1589 //try to avoid cloning existing geometry whenever we can
1590
1591 //want to see a magic trick?... gather round kiddies...
1592 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
1593 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
1594 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
1595 multiGeom->addGeometry( d->geometry.release() );
1596 // and replace it with the multi geometry.
1597 // TADA! a clone free conversion in some cases
1598 d->geometry = std::move( geom );
1599 return true;
1600}
1601
1603{
1604 if ( !d->geometry )
1605 {
1606 return false;
1607 }
1608
1609 if ( !isMultipart() ) //already single part, no need to convert
1610 {
1611 return true;
1612 }
1613
1614 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1615 if ( !multiGeom || multiGeom->partCount() < 1 )
1616 return false;
1617
1618 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
1619 reset( std::move( firstPart ) );
1620 return true;
1621}
1622
1623
1625{
1626 const QgsGeometryCollection *origGeom = qgsgeometry_cast<const QgsGeometryCollection *>( constGet() );
1627 if ( !origGeom )
1628 return false;
1629
1630 std::unique_ptr<QgsGeometryCollection> resGeom;
1631 switch ( geomType )
1632 {
1634 resGeom = std::make_unique<QgsMultiPoint>();
1635 break;
1637 resGeom = std::make_unique<QgsMultiLineString>();
1638 break;
1640 resGeom = std::make_unique<QgsMultiPolygon>();
1641 break;
1642 default:
1643 break;
1644 }
1645 if ( !resGeom )
1646 return false;
1647
1648 resGeom->reserve( origGeom->numGeometries() );
1649 for ( int i = 0; i < origGeom->numGeometries(); ++i )
1650 {
1651 const QgsAbstractGeometry *g = origGeom->geometryN( i );
1652 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
1653 resGeom->addGeometry( g->clone() );
1654 }
1655
1656 set( resGeom.release() );
1657 return true;
1658}
1659
1660
1662{
1663 if ( !d->geometry )
1664 {
1665 return QgsPointXY();
1666 }
1667 if ( QgsPoint *pt = qgsgeometry_cast<QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
1668 {
1669 return QgsPointXY( pt->x(), pt->y() );
1670 }
1671 else
1672 {
1673 return QgsPointXY();
1674 }
1675}
1676
1678{
1679 QgsPolylineXY polyLine;
1680 if ( !d->geometry )
1681 {
1682 return polyLine;
1683 }
1684
1685 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CompoundCurve
1687 std::unique_ptr< QgsLineString > segmentizedLine;
1688 QgsLineString *line = nullptr;
1689 if ( doSegmentation )
1690 {
1691 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
1692 if ( !curve )
1693 {
1694 return polyLine;
1695 }
1696 segmentizedLine.reset( curve->curveToLine() );
1697 line = segmentizedLine.get();
1698 }
1699 else
1700 {
1701 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
1702 if ( !line )
1703 {
1704 return polyLine;
1705 }
1706 }
1707
1708 int nVertices = line->numPoints();
1709 polyLine.resize( nVertices );
1710 QgsPointXY *data = polyLine.data();
1711 const double *xData = line->xData();
1712 const double *yData = line->yData();
1713 for ( int i = 0; i < nVertices; ++i )
1714 {
1715 data->setX( *xData++ );
1716 data->setY( *yData++ );
1717 data++;
1718 }
1719
1720 return polyLine;
1721}
1722
1724{
1725 if ( !d->geometry )
1726 return QgsPolygonXY();
1727
1728 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::CurvePolygon );
1729
1730 QgsPolygon *p = nullptr;
1731 std::unique_ptr< QgsPolygon > segmentized;
1732 if ( doSegmentation )
1733 {
1734 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
1735 if ( !curvePoly )
1736 {
1737 return QgsPolygonXY();
1738 }
1739 segmentized.reset( curvePoly->toPolygon() );
1740 p = segmentized.get();
1741 }
1742 else
1743 {
1744 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
1745 }
1746
1747 if ( !p )
1748 {
1749 return QgsPolygonXY();
1750 }
1751
1752 QgsPolygonXY polygon;
1753 convertPolygon( *p, polygon );
1754
1755 return polygon;
1756}
1757
1759{
1760 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != QgsWkbTypes::MultiPoint )
1761 {
1762 return QgsMultiPointXY();
1763 }
1764
1765 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
1766 if ( !mp )
1767 {
1768 return QgsMultiPointXY();
1769 }
1770
1771 int nPoints = mp->numGeometries();
1772 QgsMultiPointXY multiPoint( nPoints );
1773 for ( int i = 0; i < nPoints; ++i )
1774 {
1775 const QgsPoint *pt = mp->pointN( i );
1776 multiPoint[i].setX( pt->x() );
1777 multiPoint[i].setY( pt->y() );
1778 }
1779 return multiPoint;
1780}
1781
1783{
1784 if ( !d->geometry )
1785 {
1786 return QgsMultiPolylineXY();
1787 }
1788
1789 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1790 if ( !geomCollection )
1791 {
1792 return QgsMultiPolylineXY();
1793 }
1794
1795 int nLines = geomCollection->numGeometries();
1796 if ( nLines < 1 )
1797 {
1798 return QgsMultiPolylineXY();
1799 }
1800
1802 mpl.reserve( nLines );
1803 for ( int i = 0; i < nLines; ++i )
1804 {
1805 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
1806 std::unique_ptr< QgsLineString > segmentized;
1807 if ( !line )
1808 {
1809 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
1810 if ( !curve )
1811 {
1812 continue;
1813 }
1814 segmentized.reset( curve->curveToLine() );
1815 line = segmentized.get();
1816 }
1817
1818 QgsPolylineXY polyLine;
1819 int nVertices = line->numPoints();
1820 polyLine.resize( nVertices );
1821 QgsPointXY *data = polyLine.data();
1822 const double *xData = line->xData();
1823 const double *yData = line->yData();
1824 for ( int i = 0; i < nVertices; ++i )
1825 {
1826 data->setX( *xData++ );
1827 data->setY( *yData++ );
1828 data++;
1829 }
1830 mpl.append( polyLine );
1831 }
1832 return mpl;
1833}
1834
1836{
1837 if ( !d->geometry )
1838 {
1839 return QgsMultiPolygonXY();
1840 }
1841
1842 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
1843 if ( !geomCollection )
1844 {
1845 return QgsMultiPolygonXY();
1846 }
1847
1848 const int nPolygons = geomCollection->numGeometries();
1849 if ( nPolygons < 1 )
1850 {
1851 return QgsMultiPolygonXY();
1852 }
1853
1855 mp.reserve( nPolygons );
1856 for ( int i = 0; i < nPolygons; ++i )
1857 {
1858 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
1859 if ( !polygon )
1860 {
1861 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
1862 if ( cPolygon )
1863 {
1864 polygon = cPolygon->toPolygon();
1865 }
1866 else
1867 {
1868 continue;
1869 }
1870 }
1871
1872 QgsPolygonXY poly;
1873 convertPolygon( *polygon, poly );
1874 mp.push_back( poly );
1875 }
1876 return mp;
1877}
1878
1879double QgsGeometry::area() const
1880{
1881 if ( !d->geometry )
1882 {
1883 return -1.0;
1884 }
1885
1886 return d->geometry->area();
1887}
1888
1890{
1891 if ( !d->geometry )
1892 {
1893 return -1.0;
1894 }
1895
1896 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
1897 {
1899 return 0.0;
1900
1902 return d->geometry->length();
1903
1905 return d->geometry->perimeter();
1906
1909 return d->geometry->length();
1910 }
1911 return -1;
1912}
1913
1914double QgsGeometry::distance( const QgsGeometry &geom ) const
1915{
1916 if ( !d->geometry || !geom.d->geometry )
1917 {
1918 return -1.0;
1919 }
1920
1921 // avoid calling geos for trivial point-to-point distance calculations
1923 {
1924 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
1925 }
1926
1927 QgsGeos g( d->geometry.get() );
1928 mLastError.clear();
1929 return g.distance( geom.d->geometry.get(), &mLastError );
1930}
1931
1933{
1934 if ( !d->geometry || !geom.d->geometry )
1935 {
1936 return -1.0;
1937 }
1938
1939 QgsGeos g( d->geometry.get() );
1940 mLastError.clear();
1941 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
1942}
1943
1944double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
1945{
1946 if ( !d->geometry || !geom.d->geometry )
1947 {
1948 return -1.0;
1949 }
1950
1951 QgsGeos g( d->geometry.get() );
1952 mLastError.clear();
1953 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
1954}
1955
1956
1958{
1959 if ( !d->geometry || !geom.d->geometry )
1960 {
1961 return -1.0;
1962 }
1963
1964 QgsGeos g( d->geometry.get() );
1965 mLastError.clear();
1966 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
1967}
1968
1969double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
1970{
1971 if ( !d->geometry || !geom.d->geometry )
1972 {
1973 return -1.0;
1974 }
1975
1976 QgsGeos g( d->geometry.get() );
1977 mLastError.clear();
1978 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
1979}
1980
1982{
1983 if ( !d->geometry || d->geometry.get()->isEmpty() )
1985 return d->geometry->vertices_begin();
1986}
1987
1989{
1990 if ( !d->geometry || d->geometry.get()->isEmpty() )
1992 return d->geometry->vertices_end();
1993}
1994
1996{
1997 if ( !d->geometry || d->geometry.get()->isEmpty() )
1998 return QgsVertexIterator();
1999 return QgsVertexIterator( d->geometry.get() );
2000}
2001
2003{
2004 if ( !d->geometry )
2006
2007 detach();
2008 return d->geometry->parts_begin();
2009}
2010
2012{
2013 if ( !d->geometry )
2015 return d->geometry->parts_end();
2016}
2017
2019{
2020 if ( !d->geometry )
2022 return d->geometry->const_parts_begin();
2023}
2024
2026{
2027 if ( !d->geometry )
2029 return d->geometry->const_parts_end();
2030}
2031
2033{
2034 if ( !d->geometry )
2035 return QgsGeometryPartIterator();
2036
2037 detach();
2038 return QgsGeometryPartIterator( d->geometry.get() );
2039}
2040
2042{
2043 if ( !d->geometry )
2045
2046 return QgsGeometryConstPartIterator( d->geometry.get() );
2047}
2048
2049QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
2050{
2051 if ( !d->geometry )
2052 {
2053 return QgsGeometry();
2054 }
2055
2056 QgsGeos g( d->geometry.get() );
2057 mLastError.clear();
2058 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError ) );
2059 if ( !geom )
2060 {
2061 QgsGeometry result;
2062 result.mLastError = mLastError;
2063 return result;
2064 }
2065 return QgsGeometry( std::move( geom ) );
2066}
2067
2068QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit ) const
2069{
2070 if ( !d->geometry )
2071 {
2072 return QgsGeometry();
2073 }
2074
2075 QgsGeos g( d->geometry.get() );
2076 mLastError.clear();
2077 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError );
2078 if ( !geom )
2079 {
2080 QgsGeometry result;
2081 result.mLastError = mLastError;
2082 return result;
2083 }
2084 return QgsGeometry( geom );
2085}
2086
2087QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2088{
2089 if ( !d->geometry || type() != QgsWkbTypes::LineGeometry )
2090 {
2091 return QgsGeometry();
2092 }
2093
2094 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2095 {
2096 const QVector<QgsGeometry> parts = asGeometryCollection();
2097 QVector<QgsGeometry> results;
2098 results.reserve( parts.count() );
2099 for ( const QgsGeometry &part : parts )
2100 {
2101 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2102 if ( !result.isNull() )
2103 results << result;
2104 }
2105 if ( results.isEmpty() )
2106 return QgsGeometry();
2107
2108 QgsGeometry first = results.takeAt( 0 );
2109 for ( const QgsGeometry &result : std::as_const( results ) )
2110 {
2111 first.addPart( result );
2112 }
2113 return first;
2114 }
2115 else
2116 {
2117 QgsGeos geos( d->geometry.get() );
2118 mLastError.clear();
2119
2120 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2121 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2122
2123 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2124 if ( !offsetGeom )
2125 {
2126 QgsGeometry result;
2127 result.mLastError = mLastError;
2128 return result;
2129 }
2130
2131 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2132 {
2133 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2134 if ( newOrientation != prevOrientation )
2135 {
2136 // GEOS has flipped line orientation, flip it back
2137 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2138 offsetGeom = std::move( flipped );
2139 }
2140 }
2141 return QgsGeometry( std::move( offsetGeom ) );
2142 }
2143}
2144
2145QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2146{
2147 if ( !d->geometry || type() != QgsWkbTypes::LineGeometry )
2148 {
2149 return QgsGeometry();
2150 }
2151
2152 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2153 {
2154 const QVector<QgsGeometry> parts = asGeometryCollection();
2155 QVector<QgsGeometry> results;
2156 results.reserve( parts.count() );
2157 for ( const QgsGeometry &part : parts )
2158 {
2159 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2160 if ( !result.isNull() )
2161 results << result;
2162 }
2163 if ( results.isEmpty() )
2164 return QgsGeometry();
2165
2166 QgsGeometry first = results.takeAt( 0 );
2167 for ( const QgsGeometry &result : std::as_const( results ) )
2168 {
2169 first.addPart( result );
2170 }
2171 return first;
2172 }
2173 else
2174 {
2175 QgsGeos geos( d->geometry.get() );
2176 mLastError.clear();
2177 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side,
2178 joinStyle, miterLimit, &mLastError );
2179 if ( !bufferGeom )
2180 {
2181 QgsGeometry result;
2182 result.mLastError = mLastError;
2183 return result;
2184 }
2185 return QgsGeometry( std::move( bufferGeom ) );
2186 }
2187}
2188
2189QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2190{
2191 QgsInternalGeometryEngine engine( *this );
2192
2193 return engine.taperedBuffer( startWidth, endWidth, segments );
2194}
2195
2197{
2198 QgsInternalGeometryEngine engine( *this );
2199
2200 return engine.variableWidthBufferByM( segments );
2201}
2202
2203QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2204{
2205 if ( !d->geometry || type() != QgsWkbTypes::LineGeometry )
2206 {
2207 return QgsGeometry();
2208 }
2209
2210 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2211 {
2212 const QVector<QgsGeometry> parts = asGeometryCollection();
2213 QVector<QgsGeometry> results;
2214 results.reserve( parts.count() );
2215 for ( const QgsGeometry &part : parts )
2216 {
2217 QgsGeometry result = part.extendLine( startDistance, endDistance );
2218 if ( !result.isNull() )
2219 results << result;
2220 }
2221 if ( results.isEmpty() )
2222 return QgsGeometry();
2223
2224 QgsGeometry first = results.takeAt( 0 );
2225 for ( const QgsGeometry &result : std::as_const( results ) )
2226 {
2227 first.addPart( result );
2228 }
2229 return first;
2230 }
2231 else
2232 {
2233 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2234 if ( !line )
2235 return QgsGeometry();
2236
2237 std::unique_ptr< QgsLineString > newLine( line->clone() );
2238 newLine->extend( startDistance, endDistance );
2239 return QgsGeometry( std::move( newLine ) );
2240 }
2241}
2242
2243QgsGeometry QgsGeometry::simplify( double tolerance ) const
2244{
2245 if ( !d->geometry )
2246 {
2247 return QgsGeometry();
2248 }
2249
2250 QgsGeos geos( d->geometry.get() );
2251 mLastError.clear();
2252 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError ) );
2253 if ( !simplifiedGeom )
2254 {
2255 QgsGeometry result;
2256 result.mLastError = mLastError;
2257 return result;
2258 }
2259 return QgsGeometry( std::move( simplifiedGeom ) );
2260}
2261
2262QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2263{
2264 QgsInternalGeometryEngine engine( *this );
2265
2266 return engine.densifyByCount( extraNodesPerSegment );
2267}
2268
2270{
2271 QgsInternalGeometryEngine engine( *this );
2272
2273 return engine.densifyByDistance( distance );
2274}
2275
2276QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2277{
2278 QgsInternalGeometryEngine engine( *this );
2279
2280 return engine.convertToCurves( distanceTolerance, angleTolerance );
2281}
2282
2284{
2285 if ( !d->geometry )
2286 {
2287 return QgsGeometry();
2288 }
2289
2290 // avoid calling geos for trivial point centroids
2291 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point )
2292 {
2293 QgsGeometry c = *this;
2294 c.get()->dropZValue();
2295 c.get()->dropMValue();
2296 return c;
2297 }
2298
2299 QgsGeos geos( d->geometry.get() );
2300
2301 mLastError.clear();
2302 QgsGeometry result( geos.centroid( &mLastError ) );
2303 result.mLastError = mLastError;
2304 return result;
2305}
2306
2308{
2309 if ( !d->geometry )
2310 {
2311 return QgsGeometry();
2312 }
2313
2314 QgsGeos geos( d->geometry.get() );
2315
2316 mLastError.clear();
2317 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2318 result.mLastError = mLastError;
2319 return result;
2320}
2321
2322QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2323{
2324 QgsInternalGeometryEngine engine( *this );
2325
2326 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2327}
2328
2329QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2330{
2331 if ( !d->geometry )
2332 {
2333 return QgsGeometry();
2334 }
2335
2336 QgsGeos geos( d->geometry.get() );
2337
2338 mLastError.clear();
2339 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2340 result.mLastError = mLastError;
2341 return result;
2342}
2343
2345{
2346 if ( !d->geometry )
2347 {
2348 return QgsGeometry();
2349 }
2350
2351 QgsGeos geos( d->geometry.get() );
2352
2353 mLastError.clear();
2354 QgsGeometry result( geos.minimumWidth( &mLastError ) );
2355 result.mLastError = mLastError;
2356 return result;
2357}
2358
2360{
2361 if ( !d->geometry )
2362 {
2363 return std::numeric_limits< double >::quiet_NaN();
2364 }
2365
2366 QgsGeos geos( d->geometry.get() );
2367
2368 mLastError.clear();
2369 return geos.minimumClearance( &mLastError );
2370}
2371
2373{
2374 if ( !d->geometry )
2375 {
2376 return QgsGeometry();
2377 }
2378
2379 QgsGeos geos( d->geometry.get() );
2380
2381 mLastError.clear();
2382 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
2383 result.mLastError = mLastError;
2384 return result;
2385}
2386
2388{
2389 if ( !d->geometry )
2390 {
2391 return QgsGeometry();
2392 }
2393 QgsGeos geos( d->geometry.get() );
2394 mLastError.clear();
2395 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
2396 if ( !cHull )
2397 {
2398 QgsGeometry geom;
2399 geom.mLastError = mLastError;
2400 return geom;
2401 }
2402 return QgsGeometry( std::move( cHull ) );
2403}
2404
2405QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles ) const
2406{
2407 if ( !d->geometry )
2408 {
2409 return QgsGeometry();
2410 }
2411 QgsGeos geos( d->geometry.get() );
2412 mLastError.clear();
2413 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError ) );
2414 if ( !concaveHull )
2415 {
2416 QgsGeometry geom;
2417 geom.mLastError = mLastError;
2418 return geom;
2419 }
2420 return QgsGeometry( std::move( concaveHull ) );
2421}
2422
2423QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
2424{
2425 if ( !d->geometry )
2426 {
2427 return QgsGeometry();
2428 }
2429
2430 QgsGeos geos( d->geometry.get() );
2431 mLastError.clear();
2432 QgsGeometry result = geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError );
2433 result.mLastError = mLastError;
2434 return result;
2435}
2436
2437QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
2438{
2439 if ( !d->geometry )
2440 {
2441 return QgsGeometry();
2442 }
2443
2444 QgsGeos geos( d->geometry.get() );
2445 mLastError.clear();
2446 QgsGeometry result = geos.delaunayTriangulation( tolerance, edgesOnly );
2447 result.mLastError = mLastError;
2448 return result;
2449}
2450
2452{
2453 if ( !d->geometry )
2454 {
2455 return QgsGeometry();
2456 }
2457
2458 QgsGeos geos( d->geometry.get() );
2459 mLastError.clear();
2460 QgsGeometry result( geos.node( &mLastError ) );
2461 result.mLastError = mLastError;
2462 return result;
2463}
2464
2466{
2467 if ( !d->geometry )
2468 {
2469 return QgsGeometry();
2470 }
2471
2472 QgsGeos geos( d->geometry.get() );
2473 mLastError.clear();
2474 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
2475 result.mLastError = mLastError;
2476 return result;
2477}
2478
2479QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters ) const
2480{
2481 if ( !d->geometry )
2482 {
2483 return QgsGeometry();
2484 }
2485
2486 const QgsAbstractGeometry *geom = d->geometry.get();
2487 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
2488 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
2489 {
2490 segmentizedCopy.reset( d->geometry->segmentize() );
2491 geom = segmentizedCopy.get();
2492 }
2493
2494 QgsGeos geos( geom );
2495 mLastError.clear();
2496 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters ) );
2497 if ( !result )
2498 {
2499 QgsGeometry geom;
2500 geom.mLastError = mLastError;
2501 return geom;
2502 }
2503 return QgsGeometry( std::move( result ) );
2504}
2505
2507{
2508 if ( !d->geometry )
2509 {
2510 return QgsGeometry();
2511 }
2512
2513 QgsGeometry line = *this;
2515 return QgsGeometry();
2516 else if ( type() == QgsWkbTypes::PolygonGeometry )
2517 {
2518 line = QgsGeometry( d->geometry->boundary() );
2519 }
2520
2521 const QgsCurve *curve = nullptr;
2522 if ( line.isMultipart() )
2523 {
2524 // if multi part, iterate through parts to find target part
2525 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( line.constGet() );
2526 for ( int part = 0; part < collection->numGeometries(); ++part )
2527 {
2528 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
2529 if ( !candidate )
2530 continue;
2531 const double candidateLength = candidate->length();
2532 if ( candidateLength >= distance )
2533 {
2534 curve = candidate;
2535 break;
2536 }
2537
2538 distance -= candidateLength;
2539 }
2540 }
2541 else
2542 {
2543 curve = qgsgeometry_cast< const QgsCurve * >( line.constGet() );
2544 }
2545 if ( !curve )
2546 return QgsGeometry();
2547
2548 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
2549 if ( !result )
2550 {
2551 return QgsGeometry();
2552 }
2553 return QgsGeometry( std::move( result ) );
2554}
2555
2556double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
2557{
2559 return -1;
2560
2562 return -1;
2563
2564 QgsGeometry segmentized = *this;
2566 {
2567 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
2568 }
2569
2570 QgsGeos geos( d->geometry.get() );
2571 mLastError.clear();
2572 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
2573}
2574
2575double QgsGeometry::interpolateAngle( double distance ) const
2576{
2577 if ( !d->geometry || d->geometry->isEmpty() )
2578 return 0.0;
2579
2580 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
2582 return 0.0;
2583
2584 // always operate on segmentized geometries
2585 QgsGeometry segmentized = *this;
2586 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
2587 {
2588 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
2589 }
2590
2591 QgsVertexId previous;
2592 QgsVertexId next;
2593 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
2594 return 0.0;
2595
2596 if ( previous == next )
2597 {
2598 // distance coincided exactly with a vertex
2599 QgsVertexId v2 = previous;
2600 QgsVertexId v1;
2601 QgsVertexId v3;
2602 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
2603 if ( v1.isValid() && v3.isValid() )
2604 {
2605 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
2606 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
2607 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
2608 double angle1 = QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2609 double angle2 = QgsGeometryUtils::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
2610 return QgsGeometryUtils::averageAngle( angle1, angle2 );
2611 }
2612 else if ( v3.isValid() )
2613 {
2614 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
2615 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
2616 return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2617 }
2618 else
2619 {
2620 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
2621 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
2622 return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2623 }
2624 }
2625 else
2626 {
2627 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
2628 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
2629 return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2630 }
2631}
2632
2634{
2635 if ( !d->geometry || geometry.isNull() )
2636 {
2637 return QgsGeometry();
2638 }
2639
2640 QgsGeos geos( d->geometry.get() );
2641
2642 mLastError.clear();
2643 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters ) );
2644
2645 if ( !resultGeom )
2646 {
2647 QgsGeometry geom;
2648 geom.mLastError = mLastError;
2649 return geom;
2650 }
2651
2652 return QgsGeometry( std::move( resultGeom ) );
2653}
2654
2655QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters ) const
2656{
2657 if ( !d->geometry || geometry.isNull() )
2658 {
2659 return QgsGeometry();
2660 }
2661
2662 QgsGeos geos( d->geometry.get() );
2663 mLastError.clear();
2664 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters ) );
2665 if ( !resultGeom )
2666 {
2667 QgsGeometry geom;
2668 geom.mLastError = mLastError;
2669 return geom;
2670 }
2671 return QgsGeometry( std::move( resultGeom ) );
2672}
2673
2675{
2676 if ( !d->geometry )
2677 {
2678 return QgsGeometry();
2679 }
2680
2681 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::LineString )
2682 {
2683 // special case - a single linestring was passed
2684 return QgsGeometry( *this );
2685 }
2686
2687 QgsGeos geos( d->geometry.get() );
2688 mLastError.clear();
2689 QgsGeometry result = geos.mergeLines( &mLastError );
2690 result.mLastError = mLastError;
2691 return result;
2692}
2693
2695{
2696 if ( !d->geometry || geometry.isNull() )
2697 {
2698 return QgsGeometry();
2699 }
2700
2701 QgsGeos geos( d->geometry.get() );
2702
2703 mLastError.clear();
2704 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters ) );
2705 if ( !resultGeom )
2706 {
2707 QgsGeometry geom;
2708 geom.mLastError = mLastError;
2709 return geom;
2710 }
2711 return QgsGeometry( std::move( resultGeom ) );
2712}
2713
2715{
2716 if ( !d->geometry || geometry.isNull() )
2717 {
2718 return QgsGeometry();
2719 }
2720
2721 QgsGeos geos( d->geometry.get() );
2722
2723 mLastError.clear();
2724 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters ) );
2725 if ( !resultGeom )
2726 {
2727 QgsGeometry geom;
2728 geom.mLastError = mLastError;
2729 return geom;
2730 }
2731 return QgsGeometry( std::move( resultGeom ) );
2732}
2733
2735{
2736 QgsInternalGeometryEngine engine( *this );
2737
2738 return engine.extrude( x, y );
2739}
2740
2742
2743QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
2744{
2746 return QVector< QgsPointXY >();
2747
2748 return QgsInternalGeometryEngine::randomPointsInPolygon( *this, count, acceptPoint, seed, feedback, maxTriesPerPoint );
2749}
2750
2751QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
2752{
2754 return QVector< QgsPointXY >();
2755
2756 return QgsInternalGeometryEngine::randomPointsInPolygon( *this, count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
2757}
2759
2760int QgsGeometry::wkbSize( QgsAbstractGeometry::WkbFlags flags ) const
2761{
2762 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
2763}
2764
2765QByteArray QgsGeometry::asWkb( QgsAbstractGeometry::WkbFlags flags ) const
2766{
2767 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
2768}
2769
2770QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
2771{
2772 QVector<QgsGeometry> geometryList;
2773 if ( !d->geometry )
2774 {
2775 return geometryList;
2776 }
2777
2778 QgsGeometryCollection *gc = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2779 if ( gc )
2780 {
2781 int numGeom = gc->numGeometries();
2782 geometryList.reserve( numGeom );
2783 for ( int i = 0; i < numGeom; ++i )
2784 {
2785 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
2786 }
2787 }
2788 else //a singlepart geometry
2789 {
2790 geometryList.append( *this );
2791 }
2792
2793 return geometryList;
2794}
2795
2797{
2798 QgsPointXY point = asPoint();
2799 return point.toQPointF();
2800}
2801
2803{
2804 const QgsAbstractGeometry *part = constGet();
2805
2806 // if a geometry collection, get first part only
2807 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( part ) )
2808 {
2809 if ( collection->numGeometries() > 0 )
2810 part = collection->geometryN( 0 );
2811 else
2812 return QPolygonF();
2813 }
2814
2815 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
2816 return curve->asQPolygonF();
2817 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
2818 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
2819 return QPolygonF();
2820}
2821
2822bool QgsGeometry::deleteRing( int ringNum, int partNum )
2823{
2824 if ( !d->geometry )
2825 {
2826 return false;
2827 }
2828
2829 detach();
2830 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
2831 return ok;
2832}
2833
2834bool QgsGeometry::deletePart( int partNum )
2835{
2836 if ( !d->geometry )
2837 {
2838 return false;
2839 }
2840
2841 if ( !isMultipart() && partNum < 1 )
2842 {
2843 set( nullptr );
2844 return true;
2845 }
2846
2847 detach();
2848 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
2849 return ok;
2850}
2851
2852int QgsGeometry::avoidIntersections( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
2853{
2854 if ( !d->geometry )
2855 {
2856 return 1;
2857 }
2858
2859 QgsWkbTypes::Type geomTypeBeforeModification = wkbType();
2860
2861 bool haveInvalidGeometry = false;
2862 bool geomModified = false;
2863
2864 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
2865 if ( diffGeom )
2866 {
2867 reset( std::move( diffGeom ) );
2868 geomModified = true;
2869 }
2870
2871 if ( geomTypeBeforeModification != wkbType() )
2872 return 2;
2873 if ( haveInvalidGeometry )
2874 return 3;
2875 if ( !geomModified )
2876 return 4;
2877
2878 return 0;
2879}
2880
2881
2883{
2884 if ( !d->geometry )
2885 return QgsGeometry();
2886
2887 mLastError.clear();
2888 QgsGeos geos( d->geometry.get() );
2889 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError ) );
2890
2891 QgsGeometry result = QgsGeometry( std::move( g ) );
2892 result.mLastError = mLastError;
2893 return result;
2894}
2895
2900
2902{
2903 if ( !d->geometry )
2904 return QgsGeometry();
2905
2906 if ( isMultipart() )
2907 {
2908 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
2909 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
2910 newCollection->reserve( collection->numGeometries() );
2911 for ( int i = 0; i < collection->numGeometries(); ++i )
2912 {
2913 const QgsAbstractGeometry *g = collection->geometryN( i );
2914 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
2915 {
2916 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2917 corrected->forceClockwise();
2918 newCollection->addGeometry( corrected.release() );
2919 }
2920 else
2921 {
2922 newCollection->addGeometry( g->clone() );
2923 }
2924 }
2925 return QgsGeometry( std::move( newCollection ) );
2926 }
2927 else
2928 {
2929 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
2930 {
2931 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2932 corrected->forceClockwise();
2933 return QgsGeometry( std::move( corrected ) );
2934 }
2935 else
2936 {
2937 // not a curve polygon, so return unchanged
2938 return *this;
2939 }
2940 }
2941}
2942
2944{
2945 if ( !d->geometry )
2946 return QgsGeometry();
2947
2948 if ( isMultipart() )
2949 {
2950 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
2951 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
2952 newCollection->reserve( collection->numGeometries() );
2953 for ( int i = 0; i < collection->numGeometries(); ++i )
2954 {
2955 const QgsAbstractGeometry *g = collection->geometryN( i );
2956 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
2957 {
2958 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2959 corrected->forceCounterClockwise();
2960 newCollection->addGeometry( corrected.release() );
2961 }
2962 else
2963 {
2964 newCollection->addGeometry( g->clone() );
2965 }
2966 }
2967 return QgsGeometry( std::move( newCollection ) );
2968 }
2969 else
2970 {
2971 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
2972 {
2973 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2974 corrected->forceCounterClockwise();
2975 return QgsGeometry( std::move( corrected ) );
2976 }
2977 else
2978 {
2979 // not a curve polygon, so return unchanged
2980 return *this;
2981 }
2982 }
2983}
2984
2985
2986void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
2987{
2988 errors.clear();
2989 if ( !d->geometry )
2990 return;
2991
2992 // avoid expensive calcs for trivial point geometries
2994 {
2995 return;
2996 }
2997
2998 switch ( method )
2999 {
3001 QgsGeometryValidator::validateGeometry( *this, errors, method );
3002 return;
3003
3005 {
3006 QgsGeos geos( d->geometry.get() );
3007 QString error;
3008 QgsGeometry errorLoc;
3009 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3010 {
3011 if ( errorLoc.isNull() )
3012 {
3013 errors.append( QgsGeometry::Error( error ) );
3014 }
3015 else
3016 {
3017 const QgsPointXY point = errorLoc.asPoint();
3018 errors.append( QgsGeometry::Error( error, point ) );
3019 }
3020 return;
3021 }
3022 }
3023 }
3024}
3025
3027{
3028 if ( !d->geometry )
3029 {
3030 return;
3031 }
3032
3033 detach();
3034 d->geometry->normalize();
3035}
3036
3037bool QgsGeometry::isGeosValid( Qgis::GeometryValidityFlags flags ) const
3038{
3039 if ( !d->geometry )
3040 {
3041 return false;
3042 }
3043
3044 return d->geometry->isValid( mLastError, flags );
3045}
3046
3048{
3049 if ( !d->geometry )
3050 return false;
3051
3052 QgsGeos geos( d->geometry.get() );
3053 mLastError.clear();
3054 return geos.isSimple( &mLastError );
3055}
3056
3057bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3058{
3059 if ( !d->geometry )
3060 return false;
3061
3062 QgsInternalGeometryEngine engine( *this );
3063 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3064}
3065
3067{
3068 if ( !d->geometry || !g.d->geometry )
3069 {
3070 return false;
3071 }
3072
3073 // fast check - are they shared copies of the same underlying geometry?
3074 if ( d == g.d )
3075 return true;
3076
3077 // fast check - distinct geometry types?
3078 if ( type() != g.type() )
3079 return false;
3080
3081 // avoid calling geos for trivial point case
3082 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == QgsWkbTypes::Point
3083 && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == QgsWkbTypes::Point )
3084 {
3085 return equals( g );
3086 }
3087
3088 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3089 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3090 return false;
3091
3092 QgsGeos geos( d->geometry.get() );
3093 mLastError.clear();
3094 return geos.isEqual( g.d->geometry.get(), &mLastError );
3095}
3096
3097QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters )
3098{
3099 QgsGeos geos( nullptr );
3100
3101 QString error;
3102 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters ) );
3103 QgsGeometry result( std::move( geom ) );
3104 result.mLastError = error;
3105 return result;
3106}
3107
3108QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
3109{
3110 QVector<const QgsAbstractGeometry *> geomV2List;
3111 for ( const QgsGeometry &g : geometryList )
3112 {
3113 if ( !( g.isNull() ) )
3114 {
3115 geomV2List.append( g.constGet() );
3116 }
3117 }
3118
3119 QString error;
3120 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
3121 result.mLastError = error;
3122 return result;
3123}
3124
3126{
3128 {
3129 return;
3130 }
3131
3132 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3133 reset( std::move( straightGeom ) );
3134}
3135
3137{
3138 if ( !d->geometry )
3139 {
3140 return false;
3141 }
3142
3143 return d->geometry->hasCurvedSegments();
3144}
3145
3147{
3148 if ( !d->geometry )
3149 {
3151 }
3152
3153 detach();
3154 d->geometry->transform( ct, direction, transformZ );
3156}
3157
3158Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3159{
3160 if ( !d->geometry )
3161 {
3163 }
3164
3165 detach();
3166 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3168}
3169
3171{
3172 if ( d->geometry )
3173 {
3174 detach();
3175 d->geometry->transform( mtp.transform() );
3176 }
3177}
3178
3180{
3181 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
3182 {
3183 return QgsGeometry();
3184 }
3185
3186 QgsGeos geos( d->geometry.get() );
3187 mLastError.clear();
3188 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError );
3189 if ( !resultGeom )
3190 {
3191 QgsGeometry result;
3192 result.mLastError = mLastError;
3193 return result;
3194 }
3195 return QgsGeometry( std::move( resultGeom ) );
3196}
3197
3198void QgsGeometry::draw( QPainter &p ) const
3199{
3200 if ( d->geometry )
3201 {
3202 d->geometry->draw( p );
3203 }
3204}
3205
3206static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
3207{
3208 if ( vertexIndex < 0 )
3209 return false; // clearly something wrong
3210
3211 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3212 {
3213 partIndex = 0;
3214 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3215 {
3216 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3217
3218 // count total number of vertices in the part
3219 int numPoints = 0;
3220 for ( int k = 0; k < part->ringCount(); ++k )
3221 numPoints += part->vertexCount( 0, k );
3222
3223 if ( vertexIndex < numPoints )
3224 {
3225 int nothing;
3226 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3227 }
3228 vertexIndex -= numPoints;
3229 partIndex++;
3230 }
3231 }
3232 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3233 {
3234 const QgsCurve *ring = curvePolygon->exteriorRing();
3235 if ( vertexIndex < ring->numPoints() )
3236 {
3237 partIndex = 0;
3238 ringIndex = 0;
3239 vertex = vertexIndex;
3240 return true;
3241 }
3242 vertexIndex -= ring->numPoints();
3243 ringIndex = 1;
3244 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
3245 {
3246 const QgsCurve *ring = curvePolygon->interiorRing( i );
3247 if ( vertexIndex < ring->numPoints() )
3248 {
3249 partIndex = 0;
3250 vertex = vertexIndex;
3251 return true;
3252 }
3253 vertexIndex -= ring->numPoints();
3254 ringIndex += 1;
3255 }
3256 }
3257 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3258 {
3259 if ( vertexIndex < curve->numPoints() )
3260 {
3261 partIndex = 0;
3262 ringIndex = 0;
3263 vertex = vertexIndex;
3264 return true;
3265 }
3266 }
3267 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
3268 {
3269 if ( vertexIndex == 0 )
3270 {
3271 partIndex = 0;
3272 ringIndex = 0;
3273 vertex = 0;
3274 return true;
3275 }
3276 }
3277
3278 return false;
3279}
3280
3282{
3283 if ( !d->geometry )
3284 {
3285 return false;
3286 }
3287
3288 id.type = Qgis::VertexType::Segment;
3289
3290 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
3291 if ( !res )
3292 return false;
3293
3294 // now let's find out if it is a straight or circular segment
3295 const QgsAbstractGeometry *g = d->geometry.get();
3296 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3297 {
3298 g = geomCollection->geometryN( id.part );
3299 }
3300
3301 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3302 {
3303 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
3304 }
3305
3306 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3307 {
3308 QgsPoint p;
3309 res = curve->pointAt( id.vertex, p, id.type );
3310 if ( !res )
3311 return false;
3312 }
3313
3314 return true;
3315}
3316
3318{
3319 if ( !d->geometry )
3320 {
3321 return -1;
3322 }
3323 return d->geometry->vertexNumberFromVertexId( id );
3324}
3325
3327{
3328 return mLastError;
3329}
3330
3331void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
3332{
3333 if ( !d->geometry )
3334 return;
3335
3336 detach();
3337
3338 d->geometry->filterVertices( filter );
3339}
3340
3341void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
3342{
3343 if ( !d->geometry )
3344 return;
3345
3346 detach();
3347
3348 d->geometry->transformVertices( transform );
3349}
3350
3351void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
3352{
3353 output.clear();
3354 for ( const QgsPointXY &p : input )
3355 {
3356 output.append( QgsPoint( p ) );
3357 }
3358}
3359
3360void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
3361{
3362 output.clear();
3363 for ( const QgsPoint &p : input )
3364 {
3365 output.append( QgsPointXY( p.x(), p.y() ) );
3366 }
3367}
3368
3369void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
3370{
3371 output.clear();
3372
3373 auto convertRing = []( const QgsCurve * ring ) -> QgsPolylineXY
3374 {
3375 QgsPolylineXY res;
3376 bool doSegmentation = ( QgsWkbTypes::flatType( ring->wkbType() ) == QgsWkbTypes::CompoundCurve
3378 std::unique_ptr< QgsLineString > segmentizedLine;
3379 const QgsLineString *line = nullptr;
3380 if ( doSegmentation )
3381 {
3382 segmentizedLine.reset( ring->curveToLine() );
3383 line = segmentizedLine.get();
3384 }
3385 else
3386 {
3387 line = qgsgeometry_cast<const QgsLineString *>( ring );
3388 if ( !line )
3389 {
3390 return res;
3391 }
3392 }
3393
3394 int nVertices = line->numPoints();
3395 res.resize( nVertices );
3396 QgsPointXY *data = res.data();
3397 const double *xData = line->xData();
3398 const double *yData = line->yData();
3399 for ( int i = 0; i < nVertices; ++i )
3400 {
3401 data->setX( *xData++ );
3402 data->setY( *yData++ );
3403 data++;
3404 }
3405 return res;
3406 };
3407
3408 if ( const QgsCurve *exterior = input.exteriorRing() )
3409 {
3410 output.push_back( convertRing( exterior ) );
3411 }
3412
3413 const int interiorRingCount = input.numInteriorRings();
3414 output.reserve( output.size() + interiorRingCount );
3415 for ( int n = 0; n < interiorRingCount; ++n )
3416 {
3417 output.push_back( convertRing( input.interiorRing( n ) ) );
3418 }
3419}
3420
3422{
3423 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
3424}
3425
3426QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
3427{
3428 std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
3429
3430 if ( polygon.isClosed() )
3431 {
3432 std::unique_ptr< QgsPolygon > poly = std::make_unique< QgsPolygon >();
3433 poly->setExteriorRing( ring.release() );
3434 return QgsGeometry( std::move( poly ) );
3435 }
3436 else
3437 {
3438 return QgsGeometry( std::move( ring ) );
3439 }
3440}
3441
3443{
3445 QgsPolygonXY result;
3446 result << createPolylineFromQPolygonF( polygon );
3447 return result;
3449}
3450
3452{
3453 QgsPolylineXY result;
3454 result.reserve( polygon.count() );
3455 for ( const QPointF &p : polygon )
3456 {
3457 result.append( QgsPointXY( p ) );
3458 }
3459 return result;
3460}
3461
3462bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
3463{
3464 if ( p1.count() != p2.count() )
3465 return false;
3466
3467 for ( int i = 0; i < p1.count(); ++i )
3468 {
3469 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
3470 return false;
3471 }
3472 return true;
3473}
3474
3475bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
3476{
3477 if ( p1.count() != p2.count() )
3478 return false;
3479
3480 for ( int i = 0; i < p1.count(); ++i )
3481 {
3482 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3483 return false;
3484 }
3485 return true;
3486}
3487
3488
3489bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
3490{
3491 if ( p1.count() != p2.count() )
3492 return false;
3493
3494 for ( int i = 0; i < p1.count(); ++i )
3495 {
3496 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3497 return false;
3498 }
3499 return true;
3500}
3501
3502QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3503{
3504 if ( !d->geometry || d->geometry->isEmpty() )
3505 return QgsGeometry();
3506
3507 QgsGeometry geom = *this;
3509 geom = QgsGeometry( d->geometry->segmentize() );
3510
3511 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
3512 {
3513 case QgsWkbTypes::Point:
3515 //can't smooth a point based geometry
3516 return geom;
3517
3519 {
3520 const QgsLineString *lineString = qgsgeometry_cast< const QgsLineString * >( geom.constGet() );
3521 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
3522 }
3523
3525 {
3526 const QgsMultiLineString *multiLine = qgsgeometry_cast< const QgsMultiLineString * >( geom.constGet() );
3527
3528 std::unique_ptr< QgsMultiLineString > resultMultiline = std::make_unique< QgsMultiLineString> ();
3529 resultMultiline->reserve( multiLine->numGeometries() );
3530 for ( int i = 0; i < multiLine->numGeometries(); ++i )
3531 {
3532 resultMultiline->addGeometry( smoothLine( *( multiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3533 }
3534 return QgsGeometry( std::move( resultMultiline ) );
3535 }
3536
3538 {
3539 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( geom.constGet() );
3540 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
3541 }
3542
3544 {
3545 const QgsMultiPolygon *multiPoly = qgsgeometry_cast< const QgsMultiPolygon * >( geom.constGet() );
3546
3547 std::unique_ptr< QgsMultiPolygon > resultMultiPoly = std::make_unique< QgsMultiPolygon >();
3548 resultMultiPoly->reserve( multiPoly->numGeometries() );
3549 for ( int i = 0; i < multiPoly->numGeometries(); ++i )
3550 {
3551 resultMultiPoly->addGeometry( smoothPolygon( *( multiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3552 }
3553 return QgsGeometry( std::move( resultMultiPoly ) );
3554 }
3555
3557 default:
3558 return QgsGeometry( *this );
3559 }
3560}
3561
3562std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations,
3563 const double offset, double squareDistThreshold, double maxAngleRads,
3564 bool isRing )
3565{
3566 std::unique_ptr< QgsLineString > result = std::make_unique< QgsLineString >( line );
3567 QgsPointSequence outputLine;
3568 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
3569 {
3570 outputLine.resize( 0 );
3571 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
3572 bool skipFirst = false;
3573 bool skipLast = false;
3574 if ( isRing )
3575 {
3576 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
3577 QgsPoint p2 = result->pointN( 0 );
3578 QgsPoint p3 = result->pointN( 1 );
3579 double angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3580 p3.x(), p3.y() );
3581 angle = std::fabs( M_PI - angle );
3582 skipFirst = angle > maxAngleRads;
3583 }
3584 for ( int i = 0; i < result->numPoints() - 1; i++ )
3585 {
3586 QgsPoint p1 = result->pointN( i );
3587 QgsPoint p2 = result->pointN( i + 1 );
3588
3589 double angle = M_PI;
3590 if ( i == 0 && isRing )
3591 {
3592 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
3593 angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3594 p3.x(), p3.y() );
3595 }
3596 else if ( i < result->numPoints() - 2 )
3597 {
3598 QgsPoint p3 = result->pointN( i + 2 );
3599 angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3600 p3.x(), p3.y() );
3601 }
3602 else if ( i == result->numPoints() - 2 && isRing )
3603 {
3604 QgsPoint p3 = result->pointN( 1 );
3605 angle = QgsGeometryUtils::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3606 p3.x(), p3.y() );
3607 }
3608
3609 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
3610
3611 // don't apply distance threshold to first or last segment
3612 if ( i == 0 || i >= result->numPoints() - 2
3613 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
3614 {
3615 if ( !isRing )
3616 {
3617 if ( !skipFirst )
3618 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
3619 if ( !skipLast )
3620 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
3621 else
3622 outputLine << p2;
3623 }
3624 else
3625 {
3626 // ring
3627 if ( !skipFirst )
3628 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
3629 else if ( i == 0 )
3630 outputLine << p1;
3631 if ( !skipLast )
3632 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
3633 else
3634 outputLine << p2;
3635 }
3636 }
3637 skipFirst = skipLast;
3638 }
3639
3640 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
3641 outputLine << outputLine.at( 0 );
3642
3643 result->setPoints( outputLine );
3644 }
3645 return result;
3646}
3647
3648std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3649{
3650 double maxAngleRads = maxAngle * M_PI / 180.0;
3651 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
3652 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
3653}
3654
3655std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3656{
3657 double maxAngleRads = maxAngle * M_PI / 180.0;
3658 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
3659 std::unique_ptr< QgsPolygon > resultPoly = std::make_unique< QgsPolygon >();
3660
3661 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset,
3662 squareDistThreshold, maxAngleRads, true ).release() );
3663
3664 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
3665 {
3666 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset,
3667 squareDistThreshold, maxAngleRads, true ).release() );
3668 }
3669 return resultPoly;
3670}
3671
3672QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
3673{
3674 switch ( type() )
3675 {
3677 {
3678 bool srcIsMultipart = isMultipart();
3679
3680 if ( ( destMultipart && srcIsMultipart ) ||
3681 ( !destMultipart && !srcIsMultipart ) )
3682 {
3683 // return a copy of the same geom
3684 return QgsGeometry( *this );
3685 }
3686 if ( destMultipart )
3687 {
3688 // layer is multipart => make a multipoint with a single point
3689 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
3690 }
3691 else
3692 {
3693 // destination is singlepart => make a single part if possible
3694 QgsMultiPointXY multiPoint = asMultiPoint();
3695 if ( multiPoint.count() == 1 )
3696 {
3697 return fromPointXY( multiPoint[0] );
3698 }
3699 }
3700 return QgsGeometry();
3701 }
3702
3704 {
3705 // only possible if destination is multipart
3706 if ( !destMultipart )
3707 return QgsGeometry();
3708
3709 // input geometry is multipart
3710 if ( isMultipart() )
3711 {
3712 const QgsMultiPolylineXY multiLine = asMultiPolyline();
3713 QgsMultiPointXY multiPoint;
3714 for ( const QgsPolylineXY &l : multiLine )
3715 for ( const QgsPointXY &p : l )
3716 multiPoint << p;
3717 return fromMultiPointXY( multiPoint );
3718 }
3719 // input geometry is not multipart: copy directly the line into a multipoint
3720 else
3721 {
3722 QgsPolylineXY line = asPolyline();
3723 if ( !line.isEmpty() )
3724 return fromMultiPointXY( line );
3725 }
3726 return QgsGeometry();
3727 }
3728
3730 {
3731 // can only transform if destination is multipoint
3732 if ( !destMultipart )
3733 return QgsGeometry();
3734
3735 // input geometry is multipart: make a multipoint from multipolygon
3736 if ( isMultipart() )
3737 {
3738 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3739 QgsMultiPointXY multiPoint;
3740 for ( const QgsPolygonXY &poly : multiPolygon )
3741 for ( const QgsPolylineXY &line : poly )
3742 for ( const QgsPointXY &pt : line )
3743 multiPoint << pt;
3744 return fromMultiPointXY( multiPoint );
3745 }
3746 // input geometry is not multipart: make a multipoint from polygon
3747 else
3748 {
3749 const QgsPolygonXY polygon = asPolygon();
3750 QgsMultiPointXY multiPoint;
3751 for ( const QgsPolylineXY &line : polygon )
3752 for ( const QgsPointXY &pt : line )
3753 multiPoint << pt;
3754 return fromMultiPointXY( multiPoint );
3755 }
3756 }
3757
3758 default:
3759 return QgsGeometry();
3760 }
3761}
3762
3763QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
3764{
3765 switch ( type() )
3766 {
3768 {
3769 if ( !isMultipart() )
3770 return QgsGeometry();
3771
3772 QgsMultiPointXY multiPoint = asMultiPoint();
3773 if ( multiPoint.count() < 2 )
3774 return QgsGeometry();
3775
3776 if ( destMultipart )
3777 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
3778 else
3779 return fromPolylineXY( multiPoint );
3780 }
3781
3783 {
3784 bool srcIsMultipart = isMultipart();
3785
3786 if ( ( destMultipart && srcIsMultipart ) ||
3787 ( !destMultipart && ! srcIsMultipart ) )
3788 {
3789 // return a copy of the same geom
3790 return QgsGeometry( *this );
3791 }
3792 if ( destMultipart )
3793 {
3794 // destination is multipart => makes a multipoint with a single line
3795 QgsPolylineXY line = asPolyline();
3796 if ( !line.isEmpty() )
3797 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
3798 }
3799 else
3800 {
3801 // destination is singlepart => make a single part if possible
3802 QgsMultiPolylineXY multiLine = asMultiPolyline();
3803 if ( multiLine.count() == 1 )
3804 return fromPolylineXY( multiLine[0] );
3805 }
3806 return QgsGeometry();
3807 }
3808
3810 {
3811 // input geometry is multipolygon
3812 if ( isMultipart() )
3813 {
3814 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3815 QgsMultiPolylineXY multiLine;
3816 for ( const QgsPolygonXY &poly : multiPolygon )
3817 for ( const QgsPolylineXY &line : poly )
3818 multiLine << line;
3819
3820 if ( destMultipart )
3821 {
3822 // destination is multipart
3823 return fromMultiPolylineXY( multiLine );
3824 }
3825 else if ( multiLine.count() == 1 )
3826 {
3827 // destination is singlepart => make a single part if possible
3828 return fromPolylineXY( multiLine[0] );
3829 }
3830 }
3831 // input geometry is single polygon
3832 else
3833 {
3834 QgsPolygonXY polygon = asPolygon();
3835 // if polygon has rings
3836 if ( polygon.count() > 1 )
3837 {
3838 // cannot fit a polygon with rings in a single line layer
3839 // TODO: would it be better to remove rings?
3840 if ( destMultipart )
3841 {
3842 const QgsPolygonXY polygon = asPolygon();
3843 QgsMultiPolylineXY multiLine;
3844 multiLine.reserve( polygon.count() );
3845 for ( const QgsPolylineXY &line : polygon )
3846 multiLine << line;
3847 return fromMultiPolylineXY( multiLine );
3848 }
3849 }
3850 // no rings
3851 else if ( polygon.count() == 1 )
3852 {
3853 if ( destMultipart )
3854 {
3855 return fromMultiPolylineXY( polygon );
3856 }
3857 else
3858 {
3859 return fromPolylineXY( polygon[0] );
3860 }
3861 }
3862 }
3863 return QgsGeometry();
3864 }
3865
3866 default:
3867 return QgsGeometry();
3868 }
3869}
3870
3871QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
3872{
3873 switch ( type() )
3874 {
3876 {
3877 if ( !isMultipart() )
3878 return QgsGeometry();
3879
3880 QgsMultiPointXY multiPoint = asMultiPoint();
3881 if ( multiPoint.count() < 3 )
3882 return QgsGeometry();
3883
3884 if ( multiPoint.last() != multiPoint.first() )
3885 multiPoint << multiPoint.first();
3886
3887 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
3888 if ( destMultipart )
3889 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
3890 else
3891 return fromPolygonXY( polygon );
3892 }
3893
3895 {
3896 // input geometry is multiline
3897 if ( isMultipart() )
3898 {
3899 QgsMultiPolylineXY multiLine = asMultiPolyline();
3900 QgsMultiPolygonXY multiPolygon;
3901 for ( QgsMultiPolylineXY::iterator multiLineIt = multiLine.begin(); multiLineIt != multiLine.end(); ++multiLineIt )
3902 {
3903 // do not create polygon for a 1 segment line
3904 if ( ( *multiLineIt ).count() < 3 )
3905 return QgsGeometry();
3906 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
3907 return QgsGeometry();
3908
3909 // add closing node
3910 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
3911 *multiLineIt << ( *multiLineIt ).first();
3912 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
3913 }
3914 // check that polygons were inserted
3915 if ( !multiPolygon.isEmpty() )
3916 {
3917 if ( destMultipart )
3918 {
3919 return fromMultiPolygonXY( multiPolygon );
3920 }
3921 else if ( multiPolygon.count() == 1 )
3922 {
3923 // destination is singlepart => make a single part if possible
3924 return fromPolygonXY( multiPolygon[0] );
3925 }
3926 }
3927 }
3928 // input geometry is single line
3929 else
3930 {
3931 QgsPolylineXY line = asPolyline();
3932
3933 // do not create polygon for a 1 segment line
3934 if ( line.count() < 3 )
3935 return QgsGeometry();
3936 if ( line.count() == 3 && line.first() == line.last() )
3937 return QgsGeometry();
3938
3939 // add closing node
3940 if ( line.first() != line.last() )
3941 line << line.first();
3942
3943 // destination is multipart
3944 if ( destMultipart )
3945 {
3946 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
3947 }
3948 else
3949 {
3950 return fromPolygonXY( QgsPolygonXY() << line );
3951 }
3952 }
3953 return QgsGeometry();
3954 }
3955
3957 {
3958 bool srcIsMultipart = isMultipart();
3959
3960 if ( ( destMultipart && srcIsMultipart ) ||
3961 ( !destMultipart && ! srcIsMultipart ) )
3962 {
3963 // return a copy of the same geom
3964 return QgsGeometry( *this );
3965 }
3966 if ( destMultipart )
3967 {
3968 // destination is multipart => makes a multipoint with a single polygon
3969 QgsPolygonXY polygon = asPolygon();
3970 if ( !polygon.isEmpty() )
3971 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
3972 }
3973 else
3974 {
3975 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
3976 if ( multiPolygon.count() == 1 )
3977 {
3978 // destination is singlepart => make a single part if possible
3979 return fromPolygonXY( multiPolygon[0] );
3980 }
3981 }
3982 return QgsGeometry();
3983 }
3984
3985 default:
3986 return QgsGeometry();
3987 }
3988}
3989
3991{
3992 return new QgsGeos( geometry );
3993}
3994
3995QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
3996{
3997 out << geometry.asWkb();
3998 return out;
3999}
4000
4001QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4002{
4003 QByteArray byteArray;
4004 in >> byteArray;
4005 if ( byteArray.isEmpty() )
4006 {
4007 geometry.set( nullptr );
4008 return in;
4009 }
4010
4011 geometry.fromWkb( byteArray );
4012 return in;
4013}
4014
4015
4017{
4018 return mMessage;
4019}
4020
4022{
4023 return mLocation;
4024}
4025
4027{
4028 return mHasLocation;
4029}
4030
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
BufferSide
Side of line to buffer.
Definition qgis.h:1008
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:1716
AngularDirection
Angular directions.
Definition qgis.h:1805
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:955
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
@ Success
Operation succeeded.
@ AddPartNotMultiGeometry
The source geometry is not multi.
@ SplitCannotSplitPoint
Cannot split points.
@ GeometryEngineError
Geometry engine misses a method implemented or an error occurred in the geometry engine.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
@ Segment
The actual start or end point of a segment.
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:996
@ QgisInternal
Use internal QgsGeometryValidator method.
@ Geos
Use GEOS validation methods.
JoinStyle
Join styles for buffers.
Definition qgis.h:1033
EndCapStyle
End cap styles for buffers.
Definition qgis.h:1020
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:1701
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:1046
TransformDirection
Flags for raster layer temporal capabilities.
Definition qgis.h:1320
The part_iterator class provides STL-style iterator for const references to geometry parts.
The part_iterator class provides STL-style iterator for geometry parts.
The vertex_iterator class provides STL-style iterator for vertices.
Abstract base class for all geometries.
virtual int ringCount(int part=0) const =0
Returns the number of rings of which this geometry is built.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const =0
Returns the vertices adjacent to a specified vertex within a geometry.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
Circle geometry type.
Definition qgscircle.h:44
static QgsCircle from2Points(const QgsPoint &pt1, const QgsPoint &pt2)
Constructs a circle by 2 points on the circle.
Definition qgscircle.cpp:38
double radius() const
Returns the radius of the circle.
Definition qgscircle.h:311
bool contains(const QgsPoint &point, double epsilon=1E-8) const
Returns true if the circle contains the point.
QgsCircularString * toCircularString(bool oriented=false) const
Returns a circular string from the circle.
static QgsCircle minimalCircleFrom3Points(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon=1E-8)
Constructs the smallest circle from 3 points.
Circular string geometry type.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
Compound curve geometry type.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
A const WKB pointer.
Definition qgswkbptr.h:137
Class for doing transforms between two map coordinate systems.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
virtual QgsPolygon * toPolygon(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a new polygon geometry corresponding to a segmentized approximation of the curve.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership)
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsPoint * interpolatePoint(double distance) const =0
Returns an interpolated point on the curve at the specified distance.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
QgsPoint center() const
Returns the center point.
Definition qgsellipse.h:121
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:45
virtual bool insertGeometry(QgsAbstractGeometry *g, int index)
Inserts a geometry before a specified index and takes ownership.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Java-style iterator for const traversal of parts of a geometry.
static Qgis::GeometryOperationResult addRing(QgsAbstractGeometry *geometry, std::unique_ptr< QgsCurve > ring)
Add an interior ring to a geometry.
static std::unique_ptr< QgsAbstractGeometry > avoidIntersections(const QgsAbstractGeometry &geom, const QList< QgsVectorLayer * > &avoidIntersectionsLayers, bool &haveInvalidGeometry, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Alters a geometry so that it avoids intersections with features from all open vector layers.
static bool deletePart(QgsAbstractGeometry *geom, int partNum)
Deletes a part from a geometry.
static bool deleteRing(QgsAbstractGeometry *geom, int ringNum, int partNum=0)
Deletes a ring from a geometry.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
A geometry engine is a low-level representation of a QgsAbstractGeometry object, optimised for use wi...
EngineOperationResult
Success or failure of a geometry operation.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
@ InvalidInput
The input is not valid.
@ NodedGeometryError
Error occurred while creating a noded geometry.
@ EngineError
Error occurred in the geometry engine.
@ SplitCannotSplitPoint
Points cannot be split.
@ Success
Operation succeeded.
@ MethodNotImplemented
Method not implemented in geometry engine.
static std::unique_ptr< QgsMultiPolygon > fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Construct geometry from a multipolygon.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
static std::unique_ptr< QgsAbstractGeometry > fromPolylineXY(const QgsPolylineXY &polyline)
Construct geometry from a polyline.
static std::unique_ptr< QgsMultiPoint > fromMultiPointXY(const QgsMultiPointXY &multipoint)
Construct geometry from a multipoint.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkt(const QString &text)
Construct geometry from a WKT string.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(QgsWkbTypes::Type t)
Returns empty geometry from wkb type.
static std::unique_ptr< QgsMultiLineString > fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Construct geometry from a multipolyline.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(QgsWkbTypes::Type type)
Returns a new geometry collection matching a specified WKB type.
static std::unique_ptr< QgsAbstractGeometry > fromPointXY(const QgsPointXY &point)
Construct geometry from a point.
static std::unique_ptr< QgsPolygon > fromPolygonXY(const QgsPolygonXY &polygon)
Construct geometry from a polygon.
Encapsulates parameters under which a geometry operation is performed.
Java-style iterator for traversal of parts of a geometry.
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2)
Returns the squared 2D distance between two points.
static QgsPointXY interpolatePointOnLine(double x1, double y1, double x2, double y2, double fraction)
Interpolates the position of a point a fraction of the way along the line from (x1,...
static bool verticesAtDistance(const QgsAbstractGeometry &geometry, double distance, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
Retrieves the vertices which are before and after the interpolated point at a specified distance alon...
static double distanceToVertex(const QgsAbstractGeometry &geom, QgsVertexId id)
Returns the distance along a geometry from its first vertex to the specified vertex.
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
static double angleBetweenThreePoints(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the angle between the lines AB and BC, where AB and BC described by points a,...
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static void validateGeometry(const QgsGeometry &geometry, QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal)
Validate geometry and produce a list of geometry errors.
A geometry error.
bool hasWhere() const
true if the location available from
QgsPointXY where() const
The coordinates at which the error is located and should be visualized.
QString what() const
A human readable error message containing details about the error.
A geometry is the spatial representation of a feature.
QPolygonF asQPolygonF() const
Returns contents of the geometry as a QPolygonF.
bool deleteRing(int ringNum, int partNum=0)
Deletes a ring in polygon or multipolygon.
QVector< QgsPointXY > randomPointsInPolygon(int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr, int maxTriesPerPoint=0) const
Returns a list of count random points generated inside a (multi)polygon geometry (if acceptPoint is s...
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
QgsGeometry clipped(const QgsRectangle &rectangle)
Clips the geometry using the specified rectangle.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
int makeDifferenceInPlace(const QgsGeometry &other)
Changes this geometry such that it does not intersect the other geometry.
QVector< QgsGeometry > coerceToType(QgsWkbTypes::Type type, double defaultZ=0, double defaultM=0) const
Attempts to coerce this geometry into the specified destination type.
void adjacentVertices(int atVertex, int &beforeVertex, int &afterVertex) const
Returns the indexes of the vertices before and after the given vertex index.
QgsMultiPolygonXY asMultiPolygon() const
Returns the contents of the geometry as a multi-polygon.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
bool deleteVertex(int atVertex)
Deletes the vertex at the given position number and item (first number is index 0)
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
static bool compare(const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compares two polylines for equality within a specified tolerance.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
static QgsGeometry fromQPointF(QPointF point)
Construct geometry from a QPointF.
static QgsGeometry polygonize(const QVector< QgsGeometry > &geometries)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform)
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
QgsGeometry minimumWidth() const
Returns a linestring geometry which represents the minimum diameter of the geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
QgsGeometry makeDifference(const QgsGeometry &other) const
Returns the geometry formed by modifying this geometry such that it does not intersect the other geom...
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
double frechetDistance(const QgsGeometry &geom) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
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.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
Qgis::GeometryOperationResult addPart(const QVector< QgsPointXY > &points, QgsWkbTypes::GeometryType geomType=QgsWkbTypes::UnknownGeometry)
Adds a new part to a the geometry.
void normalize()
Reorganizes the geometry into a normalized form (or "canonical" form).
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Returns the length of the QByteArray returned by asWkb()
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static Q_DECL_DEPRECATED QgsPolylineXY createPolylineFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolylineXY from a QPolygonF.
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsGeometry subdivide(int maxNodes=256, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Subdivides the geometry.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry convertToType(QgsWkbTypes::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
QgsAbstractGeometry::part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QgsGeometry snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0) const
Returns a new geometry with all points or vertices snapped to the closest point of the grid.
virtual json asJsonObject(int precision=17) const
Exports the geometry to a json object.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter)
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
bool insertVertex(double x, double y, int beforeVertex)
Insert a new vertex before the given vertex index, ring and item (first number is index 0) If the req...
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
static Q_DECL_DEPRECATED QgsPolygonXY createPolygonFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolygonXYfrom a QPolygonF.
bool convertToSingleType()
Converts multi type geometry into single type geometry e.g.
Qgis::GeometryOperationResult addRing(const QVector< QgsPointXY > &ring)
Adds a new ring to this geometry.
QgsWkbTypes::GeometryType type
bool requiresConversionToStraightSegments() const
Returns true if the geometry is a curved geometry type which requires conversion to display as straig...
bool isSimple() const
Determines whether the geometry is simple (according to OGC definition), i.e.
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new LineString geometry from a list of QgsPoint points.
void validateGeometry(QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Validates geometry and produces a list of geometry errors.
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QPointF asQPointF() const
Returns contents of the geometry as a QPointF if wkbType is WKBPoint, otherwise returns a null QPoint...
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.
double area() const
Returns the planar, 2-dimensional area of the geometry.
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 crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
QgsGeometry & operator=(QgsGeometry const &rhs)
Creates a deep copy of the object.
QgsGeometry orthogonalize(double tolerance=1.0E-8, int maxIterations=1000, double angleThreshold=15.0) const
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries angl...
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry largestEmptyCircle(double tolerance, const QgsGeometry &boundary=QgsGeometry()) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
QgsGeometryPartIterator parts()
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry convertToCurves(double distanceTolerance=1e-8, double angleTolerance=1e-8) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry voronoiDiagram(const QgsGeometry &extent=QgsGeometry(), double tolerance=0.0, bool edgesOnly=false) const
Creates a Voronoi diagram for the nodes contained within the geometry.
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry minimumClearanceLine() const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
virtual ~QgsGeometry()
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygonXY.
double sqrDistToVertexAt(QgsPointXY &point, int atVertex) const
Returns the squared Cartesian distance between the given point to the given vertex index (vertex at t...
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsGeometry node() const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
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.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false)
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
QgsAbstractGeometry::part_iterator parts_end()
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.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QString asJson(int precision=17) const
Exports the geometry to a GeoJSON string.
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
double frechetDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
QgsGeometry orientedMinimumBoundingBox() const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometryConstPartIterator constParts() const
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
double minimumClearance() const
Computes the minimum clearance of a geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
Qgis::GeometryOperationResult reshapeGeometry(const QgsLineString &reshapeLineString)
Replaces a part of this geometry with another line.
double closestVertexWithContext(const QgsPointXY &point, int &atVertex) const
Searches for the closest vertex in this geometry to the given point.
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false) const
Returns the Delaunay triangulation for the vertices of the geometry.
void draw(QPainter &p) const
Draws the geometry onto a QPainter.
bool convertGeometryCollectionToSubclass(QgsWkbTypes::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
QString asWkt(int precision=17) const
Exports the geometry to WKT.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
bool toggleCircularAtVertex(int atVertex)
Converts the vertex at the given position from/to circular.
bool moveVertex(double x, double y, int atVertex)
Moves the vertex at the given position number and item (first number is index 0) to the given coordin...
QgsGeometry()
Constructor.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
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.
bool deletePart(int partNum)
Deletes part identified by the part number.
QgsGeometry removeInteriorRings(double minimumAllowedArea=-1) const
Removes the interior rings from a (multi)polygon geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
int avoidIntersections(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition qgsgeos.h:99
double hausdorffDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition qgsgeos.cpp:660
double hausdorffDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition qgsgeos.cpp:637
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition qgsgeos.cpp:1876
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:507
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
Definition qgsgeos.cpp:2888
double frechetDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
Definition qgsgeos.cpp:683
double frechetDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
Definition qgsgeos.cpp:706
This class offers geometry processing methods.
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry poleOfInaccessibility(double precision, double *distanceFromBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer using the m-values from a (multi)line geometry.
QgsGeometry extrude(double x, double y) const
Will extrude a line or (segmentized) curve by a given offset and return a polygon representation of i...
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
QgsGeometry orthogonalize(double tolerance=1.0E-8, int maxIterations=1000, double angleThreshold=15.0) const
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries angl...
QString lastError() const
Returns an error string referring to the last error encountered.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a tapered width buffer for a (multi)curve geometry.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Densifies the geometry by adding the specified number of extra nodes within each segment of the geome...
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
static QVector< QgsPointXY > randomPointsInPolygon(const QgsGeometry &polygon, int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr, int maxTriesPerPoint=0)
Returns a list of count random points generated inside a polygon geometry (if acceptPoint is specifie...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometry convertToCurves(double distanceTolerance, double angleTolerance) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
Line string geometry type, with support for z-dimension and m-values.
const double * yData() const
Returns a const pointer to the y vertex data.
const double * xData() const
Returns a const pointer to the x vertex data.
int numPoints() const override
Returns the number of points in the curve.
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Perform transforms between map coordinates and device coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
QgsPoint * pointN(int index)
Returns the point with the specified index.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
A class to represent a 2D point.
Definition qgspointxy.h:59
void setY(double y)
Sets the y value of the point.
Definition qgspointxy.h:132
double y
Definition qgspointxy.h:63
double x
Definition qgspointxy.h:62
void setX(double x)
Sets the x value of the point.
Definition qgspointxy.h:122
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:169
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
void setX(double x)
Sets the point's x-coordinate.
Definition qgspoint.h:280
double x
Definition qgspoint.h:52
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:735
double y
Definition qgspoint.h:53
Polygon geometry type.
Definition qgspolygon.h:34
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
bool isEmpty() const
Returns true if the rectangle is empty.
Represents a vector layer which manages a vector based data sets.
Java-style iterator for traversal of vertices of a geometry.
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Type
The WKB type describes the number of dimensions a geometry has.
Definition qgswkbtypes.h:70
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
static Type multiType(Type type)
Returns the multi type for a WKB type.
static Type flatType(Type type)
Returns the flat type for a WKB type.
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
Contains geos related utilities and functions.
Definition qgsgeos.h:37
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:3061
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:3060
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:2527
QVector< QgsPoint > QgsPointSequence
Q_GLOBAL_STATIC_WITH_ARGS(PalPropertyList, palHiddenProperties,({ QgsPalLayerSettings::PositionX, QgsPalLayerSettings::PositionY, QgsPalLayerSettings::Show, QgsPalLayerSettings::LabelRotation, QgsPalLayerSettings::Family, QgsPalLayerSettings::FontStyle, QgsPalLayerSettings::Size, QgsPalLayerSettings::Bold, QgsPalLayerSettings::Italic, QgsPalLayerSettings::Underline, QgsPalLayerSettings::Color, QgsPalLayerSettings::Strikeout, QgsPalLayerSettings::MultiLineAlignment, QgsPalLayerSettings::BufferSize, QgsPalLayerSettings::BufferDraw, QgsPalLayerSettings::BufferColor, QgsPalLayerSettings::LabelDistance, QgsPalLayerSettings::Hali, QgsPalLayerSettings::Vali, QgsPalLayerSettings::ScaleVisibility, QgsPalLayerSettings::MinScale, QgsPalLayerSettings::MaxScale, QgsPalLayerSettings::AlwaysShow, QgsPalLayerSettings::CalloutDraw, QgsPalLayerSettings::LabelAllParts })) Q_GLOBAL_STATIC_WITH_ARGS(SymbolPropertyList
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QDataStream & operator<<(QDataStream &out, const QgsGeometry &geometry)
Writes the geometry to stream out. QGIS version compatibility is not guaranteed.
std::unique_ptr< QgsLineString > smoothCurve(const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing)
QDataStream & operator>>(QDataStream &in, QgsGeometry &geometry)
Reads a geometry from stream in into geometry. QGIS version compatibility is not guaranteed.
QCache< QString, QgsGeometry > WktCache
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item.
Definition qgsgeometry.h:76
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition qgsgeometry.h:86
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:82
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:63
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition qgsgeometry.h:93
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition qgsgeometry.h:72
int precision
std::unique_ptr< QgsAbstractGeometry > geometry
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:31
int vertex
Vertex number.
Definition qgsvertexid.h:95
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:46