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