QGIS API Documentation 4.3.0-Master (16eddb2670e)
Loading...
Searching...
No Matches
qgsgeometry.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometry.cpp - Geometry (stored as Open Geospatial Consortium WKB)
3 -------------------------------------------------------------------
4Date : 02 May 2005
5Copyright : (C) 2005 by Brendan Morley
6email : morb at ozemail dot com dot au
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsgeometry.h"
17
18#include <cmath>
19#include <cstdarg>
20#include <cstdio>
21#include <geos_c.h>
22#include <limits>
23#include <nlohmann/json.hpp>
24
25#include "qgis.h"
26#include "qgsabstractgeometry.h"
27#include "qgscircle.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
31#include "qgsgeometryfactory.h"
32#include "qgsgeometryutils.h"
34#include "qgsgeos.h"
36#include "qgslinestring.h"
37#include "qgsmaptopixel.h"
38#include "qgsmulticurve.h"
39#include "qgsmultilinestring.h"
40#include "qgsmultipoint.h"
41#include "qgsmultipolygon.h"
42#include "qgsmultisurface.h"
43#include "qgsnurbsutils.h"
44#include "qgspoint.h"
45#include "qgspointxy.h"
46#include "qgspolygon.h"
48#include "qgsrectangle.h"
49#include "qgstriangle.h"
51#include "qgsvectorlayer.h"
52
53#include <QString>
54
55#ifdef WITH_SFCGAL
56#include "qgssfcgalgeometry.h"
57#endif
58
59#include <QCache>
60#include <QString>
61
62#include "moc_qgsgeometry.cpp"
63
64using namespace Qt::StringLiterals;
65
67{
69 : ref( 1 )
70 {}
71 QgsGeometryPrivate( std::unique_ptr< QgsAbstractGeometry > geometry )
72 : ref( 1 )
73 , geometry( std::move( geometry ) )
74 {}
75 QAtomicInt ref;
76 std::unique_ptr< QgsAbstractGeometry > geometry;
77};
78
82
84{
85 if ( !d->ref.deref() )
86 delete d;
87}
88
90 : d( new QgsGeometryPrivate() )
91{
92 d->geometry.reset( geom );
93}
94
95QgsGeometry::QgsGeometry( std::unique_ptr<QgsAbstractGeometry> geom )
96 : d( new QgsGeometryPrivate( std::move( geom ) ) )
97{}
98
100 : d( other.d )
101{
102 mLastError = other.mLastError;
103 d->ref.ref();
104}
105
107{
108 if ( this != &other )
109 {
110 if ( !d->ref.deref() )
111 {
112 delete d;
113 }
114
115 mLastError = other.mLastError;
116 d = other.d;
117 d->ref.ref();
118 }
119 return *this;
120}
121
122void QgsGeometry::detach()
123{
124 if ( d->ref <= 1 )
125 return;
126
127 std::unique_ptr< QgsAbstractGeometry > cGeom;
128 if ( d->geometry )
129 cGeom.reset( d->geometry->clone() );
130
131 reset( std::move( cGeom ) );
132}
133
134void QgsGeometry::reset( std::unique_ptr<QgsAbstractGeometry> newGeometry )
135{
136 if ( d->ref > 1 )
137 {
138 ( void ) d->ref.deref();
139 d = new QgsGeometryPrivate();
140 }
141 d->geometry = std::move( newGeometry );
142}
143
145{
146 return d->geometry.get();
147}
148
150{
151 detach();
152 return d->geometry.get();
153}
154
156{
157 if ( d->geometry.get() == geometry )
158 {
159 return;
160 }
161
162 reset( std::unique_ptr< QgsAbstractGeometry >( geometry ) );
163}
164
166{
167 return !d->geometry;
168}
169
170typedef QCache< QString, QgsGeometry > WktCache;
171Q_GLOBAL_STATIC_WITH_ARGS( WktCache, sWktCache, ( 2000 ) ) // store up to 2000 geometries
172Q_GLOBAL_STATIC( QMutex, sWktMutex )
173
174QgsGeometry QgsGeometry::fromWkt( const QString &wkt )
175{
176 QMutexLocker lock( sWktMutex() );
177 if ( const QgsGeometry *cached = sWktCache()->object( wkt ) )
178 return *cached;
179 const QgsGeometry result( QgsGeometryFactory::geomFromWkt( wkt ) );
180 sWktCache()->insert( wkt, new QgsGeometry( result ), 1 );
181 return result;
182}
183
185{
186 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::fromPointXY( point ) );
187 if ( geom )
188 {
189 return QgsGeometry( geom.release() );
190 }
191 return QgsGeometry();
192}
193
195{
196 return QgsGeometry( point.clone() );
197}
198
200{
201 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolylineXY( polyline );
202 if ( geom )
203 {
204 return QgsGeometry( std::move( geom ) );
205 }
206 return QgsGeometry();
207}
208
210{
211 return QgsGeometry( std::make_unique< QgsLineString >( polyline ) );
212}
213
215{
216 std::unique_ptr< QgsPolygon > geom = QgsGeometryFactory::fromPolygonXY( polygon );
217 if ( geom )
218 {
219 return QgsGeometry( std::move( geom ) );
220 }
221 return QgsGeometry();
222}
223
225{
226 std::unique_ptr< QgsMultiPoint > geom = QgsGeometryFactory::fromMultiPointXY( multipoint );
227 if ( geom )
228 {
229 return QgsGeometry( std::move( geom ) );
230 }
231 return QgsGeometry();
232}
233
235{
236 std::unique_ptr< QgsMultiLineString > geom = QgsGeometryFactory::fromMultiPolylineXY( multiline );
237 if ( geom )
238 {
239 return QgsGeometry( std::move( geom ) );
240 }
241 return QgsGeometry();
242}
243
245{
246 std::unique_ptr< QgsMultiPolygon > geom = QgsGeometryFactory::fromMultiPolygonXY( multipoly );
247 if ( geom )
248 {
249 return QgsGeometry( std::move( geom ) );
250 }
251 return QgsGeometry();
252}
253
255{
256 if ( rect.isNull() )
257 return QgsGeometry();
258
259 auto ext = std::make_unique< QgsLineString >(
260 QVector< double >() << rect.xMinimum() << rect.xMaximum() << rect.xMaximum() << rect.xMinimum() << rect.xMinimum(),
261 QVector< double >() << rect.yMinimum() << rect.yMinimum() << rect.yMaximum() << rect.yMaximum() << rect.yMinimum()
262 );
263 auto polygon = std::make_unique< QgsPolygon >();
264 polygon->setExteriorRing( ext.release() );
265 return QgsGeometry( std::move( polygon ) );
266}
267
269{
270 if ( box.is2d() )
271 {
272 return fromRect( box.toRectangle() );
273 }
274
275 auto polyhedralSurface = std::make_unique< QgsPolyhedralSurface >();
276
277 auto ext1 = std::make_unique< QgsLineString >(
278 QVector< double >() << box.xMinimum() << box.xMinimum() << box.xMaximum() << box.xMaximum() << box.xMinimum(),
279 QVector< double >() << box.yMinimum() << box.yMaximum() << box.yMaximum() << box.yMinimum() << box.yMinimum(),
280 QVector< double >() << box.zMinimum() << box.zMinimum() << box.zMinimum() << box.zMinimum() << box.zMinimum()
281 );
282 auto polygon1 = std::make_unique< QgsPolygon >( ext1.release() );
283 polyhedralSurface->addPatch( polygon1.release() );
284
285 auto ext2 = std::make_unique< QgsLineString >(
286 QVector< double >() << box.xMinimum() << box.xMinimum() << box.xMinimum() << box.xMinimum() << box.xMinimum(),
287 QVector< double >() << box.yMinimum() << box.yMaximum() << box.yMaximum() << box.yMinimum() << box.yMinimum(),
288 QVector< double >() << box.zMinimum() << box.zMinimum() << box.zMaximum() << box.zMaximum() << box.zMinimum()
289 );
290 auto polygon2 = std::make_unique< QgsPolygon >( ext2.release() );
291 polyhedralSurface->addPatch( polygon2.release() );
292
293 auto ext3 = std::make_unique< QgsLineString >(
294 QVector< double >() << box.xMinimum() << box.xMaximum() << box.xMaximum() << box.xMinimum() << box.xMinimum(),
295 QVector< double >() << box.yMinimum() << box.yMinimum() << box.yMinimum() << box.yMinimum() << box.yMinimum(),
296 QVector< double >() << box.zMinimum() << box.zMinimum() << box.zMaximum() << box.zMaximum() << box.zMinimum()
297 );
298 auto polygon3 = std::make_unique< QgsPolygon >( ext3.release() );
299 polyhedralSurface->addPatch( polygon3.release() );
300
301 auto ext4 = std::make_unique< QgsLineString >(
302 QVector< double >() << box.xMaximum() << box.xMaximum() << box.xMinimum() << box.xMinimum() << box.xMaximum(),
303 QVector< double >() << box.yMaximum() << box.yMinimum() << box.yMinimum() << box.yMaximum() << box.yMaximum(),
304 QVector< double >() << box.zMaximum() << box.zMaximum() << box.zMaximum() << box.zMaximum() << box.zMaximum()
305 );
306 auto polygon4 = std::make_unique< QgsPolygon >( ext4.release() );
307 polyhedralSurface->addPatch( polygon4.release() );
308
309 auto ext5 = std::make_unique< QgsLineString >(
310 QVector< double >() << box.xMaximum() << box.xMaximum() << box.xMaximum() << box.xMaximum() << box.xMaximum(),
311 QVector< double >() << box.yMaximum() << box.yMinimum() << box.yMinimum() << box.yMaximum() << box.yMaximum(),
312 QVector< double >() << box.zMaximum() << box.zMaximum() << box.zMinimum() << box.zMinimum() << box.zMaximum()
313 );
314 auto polygon5 = std::make_unique< QgsPolygon >( ext5.release() );
315 polyhedralSurface->addPatch( polygon5.release() );
316
317 auto ext6 = std::make_unique< QgsLineString >(
318 QVector< double >() << box.xMaximum() << box.xMaximum() << box.xMinimum() << box.xMinimum() << box.xMaximum(),
319 QVector< double >() << box.yMaximum() << box.yMaximum() << box.yMaximum() << box.yMaximum() << box.yMaximum(),
320 QVector< double >() << box.zMaximum() << box.zMinimum() << box.zMinimum() << box.zMaximum() << box.zMaximum()
321 );
322 auto polygon6 = std::make_unique< QgsPolygon >( ext6.release() );
323 polyhedralSurface->addPatch( polygon6.release() );
324
325 return QgsGeometry( std::move( polyhedralSurface ) );
326}
327
328QgsGeometry QgsGeometry::collectGeometry( const QVector< QgsGeometry > &geometries )
329{
330 QgsGeometry collected;
331
332 for ( const QgsGeometry &g : geometries )
333 {
334 if ( collected.isNull() )
335 {
336 collected = g;
337 collected.convertToMultiType();
338 }
339 else
340 {
341 if ( g.isMultipart() )
342 {
343 for ( auto p = g.const_parts_begin(); p != g.const_parts_end(); ++p )
344 {
345 collected.addPartV2( ( *p )->clone() );
346 }
347 }
348 else
349 {
350 collected.addPart( g );
351 }
352 }
353 }
354 return collected;
355}
356
357QgsGeometry QgsGeometry::collectTinPatches( const QVector<QgsGeometry> &geometries )
358{
359 auto resultTin = std::make_unique<QgsTriangulatedSurface>();
360 bool first = true;
361
362 for ( const QgsGeometry &geom : geometries )
363 {
364 if ( geom.isNull() )
365 continue;
366
367 const QgsAbstractGeometry *abstractGeom = geom.constGet();
368
370 {
371 // Preserve Z/M from first valid geometry
372 if ( first )
373 {
374 if ( tin->is3D() )
375 resultTin->addZValue( 0 );
376 if ( tin->isMeasure() )
377 resultTin->addMValue( 0 );
378 first = false;
379 }
380
381 // Copy all patches (triangles) from the TIN
382 for ( int j = 0; j < tin->numPatches(); ++j )
383 {
384 if ( const QgsPolygon *patch = tin->patchN( j ) )
385 {
386 resultTin->addPatch( patch->clone() );
387 }
388 }
389 }
390 else if ( const QgsTriangle *triangle = qgsgeometry_cast<const QgsTriangle *>( abstractGeom ) )
391 {
392 // Preserve Z/M from first valid geometry
393 if ( first )
394 {
395 if ( triangle->is3D() )
396 resultTin->addZValue( 0 );
397 if ( triangle->isMeasure() )
398 resultTin->addMValue( 0 );
399 first = false;
400 }
401
402 resultTin->addPatch( triangle->clone() );
403 }
404 }
405
406 if ( resultTin->numPatches() == 0 )
407 return QgsGeometry();
408
409 return QgsGeometry( std::move( resultTin ) );
410}
411
412QgsGeometry QgsGeometry::createWedgeBuffer( const QgsPoint &center, const double azimuth, const double angularWidth, const double outerRadius, const double innerRadius )
413{
414 const double startAngle = azimuth - angularWidth * 0.5;
415 const double endAngle = azimuth + angularWidth * 0.5;
416
417 return createWedgeBufferFromAngles( center, startAngle, endAngle, outerRadius, innerRadius );
418}
419
420QgsGeometry QgsGeometry::createWedgeBufferFromAngles( const QgsPoint &center, double startAngle, double endAngle, double outerRadius, double innerRadius )
421{
422 auto wedge = std::make_unique< QgsCompoundCurve >();
423
424 const double DEG_TO_RAD = M_PI / 180.0;
425 const double RAD_TO_DEG = 180.0 / M_PI;
426
427 const double angularWidth = endAngle - startAngle;
428 const bool useShortestArc = QgsGeometryUtilsBase::normalizedAngle( angularWidth * DEG_TO_RAD ) * RAD_TO_DEG <= 180.0;
429
430 if ( std::abs( angularWidth ) >= 360.0 )
431 {
432 auto outerCc = std::make_unique< QgsCompoundCurve >();
433
434 QgsCircle outerCircle = QgsCircle( center, outerRadius );
435 outerCc->addCurve( outerCircle.toCircularString().release() );
436
437 auto cp = std::make_unique< QgsCurvePolygon >();
438 cp->setExteriorRing( outerCc.release() );
439
440 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
441 {
442 auto innerCc = std::make_unique< QgsCompoundCurve >();
443
444 QgsCircle innerCircle = QgsCircle( center, innerRadius );
445 innerCc->addCurve( innerCircle.toCircularString().release() );
446
447 cp->setInteriorRings( { innerCc.release() } );
448 }
449
450 return QgsGeometry( std::move( cp ) );
451 }
452
453 const QgsPoint outerP1 = center.project( outerRadius, startAngle );
454 const QgsPoint outerP2 = center.project( outerRadius, endAngle );
455
456 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( outerP1, outerP2, center, useShortestArc ) ) );
457
458 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
459 {
460 const QgsPoint innerP1 = center.project( innerRadius, startAngle );
461 const QgsPoint innerP2 = center.project( innerRadius, endAngle );
462 wedge->addCurve( new QgsLineString( outerP2, innerP2 ) );
463 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( innerP2, innerP1, center, useShortestArc ) ) );
464 wedge->addCurve( new QgsLineString( innerP1, outerP1 ) );
465 }
466 else
467 {
468 wedge->addCurve( new QgsLineString( outerP2, center ) );
469 wedge->addCurve( new QgsLineString( center, outerP1 ) );
470 }
471
472 auto cp = std::make_unique< QgsCurvePolygon >();
473 cp->setExteriorRing( wedge.release() );
474 return QgsGeometry( std::move( cp ) );
475}
476
477void QgsGeometry::fromWkb( unsigned char *wkb, int length )
478{
479 QgsConstWkbPtr ptr( wkb, length );
480 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
481 delete[] wkb;
482}
483
484void QgsGeometry::fromWkb( const QByteArray &wkb )
485{
486 QgsConstWkbPtr ptr( wkb );
487 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
488}
489
491{
492 if ( !d->geometry )
493 {
495 }
496 else
497 {
498 return d->geometry->wkbType();
499 }
500}
501
503{
504 if ( !d->geometry )
505 {
507 }
508 return QgsWkbTypes::geometryType( d->geometry->wkbType() );
509}
510
512{
513 if ( !d->geometry )
514 {
515 return true;
516 }
517
518 return d->geometry->isEmpty();
519}
520
522{
523 if ( !d->geometry )
524 {
525 return false;
526 }
527 return QgsWkbTypes::isMultiType( d->geometry->wkbType() );
528}
529QgsPointXY QgsGeometry::closestVertex( const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist ) const
530{
531 if ( !d->geometry )
532 {
533 sqrDist = -1;
534 return QgsPointXY();
535 }
536
537 QgsPoint pt( point );
538 QgsVertexId id;
539
540 QgsPoint vp = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, id );
541 if ( !id.isValid() )
542 {
543 sqrDist = -1;
544 return QgsPointXY();
545 }
546 sqrDist = QgsGeometryUtils::sqrDistance2D( pt, vp );
547
548 QgsVertexId prevVertex;
549 QgsVertexId nextVertex;
550 d->geometry->adjacentVertices( id, prevVertex, nextVertex );
551 closestVertexIndex = vertexNrFromVertexId( id );
552 previousVertexIndex = vertexNrFromVertexId( prevVertex );
553 nextVertexIndex = vertexNrFromVertexId( nextVertex );
554 return QgsPointXY( vp.x(), vp.y() );
555}
556
557double QgsGeometry::distanceToVertex( int vertex ) const
558{
559 if ( !d->geometry )
560 {
561 return -1;
562 }
563
564 QgsVertexId id;
565 if ( !vertexIdFromVertexNr( vertex, id ) )
566 {
567 return -1;
568 }
569
570 return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id );
571}
572
573double QgsGeometry::angleAtVertex( int vertex ) const
574{
575 if ( !d->geometry )
576 {
577 return 0;
578 }
579
580 QgsVertexId v2;
581 if ( !vertexIdFromVertexNr( vertex, v2 ) )
582 {
583 return 0;
584 }
585
586 return d->geometry->vertexAngle( v2 );
587}
588
589void QgsGeometry::adjacentVertices( int atVertex, int &beforeVertex, int &afterVertex ) const
590{
591 if ( !d->geometry )
592 {
593 return;
594 }
595
596 QgsVertexId id;
597 if ( !vertexIdFromVertexNr( atVertex, id ) )
598 {
599 beforeVertex = -1;
600 afterVertex = -1;
601 return;
602 }
603
604 QgsVertexId beforeVertexId, afterVertexId;
605 d->geometry->adjacentVertices( id, beforeVertexId, afterVertexId );
606 beforeVertex = vertexNrFromVertexId( beforeVertexId );
607 afterVertex = vertexNrFromVertexId( afterVertexId );
608}
609
610bool QgsGeometry::moveVertex( double x, double y, int atVertex )
611{
612 if ( !d->geometry )
613 {
614 return false;
615 }
616
617 QgsVertexId id;
618 if ( !vertexIdFromVertexNr( atVertex, id ) )
619 {
620 return false;
621 }
622
623 detach();
624
625 return d->geometry->moveVertex( id, QgsPoint( x, y ) );
626}
627
628bool QgsGeometry::moveVertex( const QgsPoint &p, int atVertex )
629{
630 if ( !d->geometry )
631 {
632 return false;
633 }
634
635 QgsVertexId id;
636 if ( !vertexIdFromVertexNr( atVertex, id ) )
637 {
638 return false;
639 }
640
641 detach();
642
643 return d->geometry->moveVertex( id, p );
644}
645
646bool QgsGeometry::deleteVertex( int atVertex )
647{
648 if ( !d->geometry )
649 {
650 return false;
651 }
652
653 //maintain compatibility with < 2.10 API
654 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::MultiPoint )
655 {
656 detach();
657 //delete geometry instead of point
658 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->removeGeometry( atVertex );
659 }
660
661 //if it is a point, set the geometry to nullptr
662 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
663 {
664 reset( nullptr );
665 return true;
666 }
667
668 QgsVertexId id;
669 if ( !vertexIdFromVertexNr( atVertex, id ) )
670 {
671 return false;
672 }
673
674 detach();
675
676 return d->geometry->deleteVertex( id );
677}
678
679bool QgsGeometry::deleteVertices( const QSet<int> &atVertices )
680{
681 if ( !d->geometry )
682 {
683 return false;
684 }
685
686 // if it is a point, set the geometry to nullptr
687 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
688 {
689 if ( atVertices.size() != 1 && !atVertices.contains( 0 ) )
690 return false;
691
692 reset( nullptr );
693 return true;
694 }
695
696 QSet<QgsVertexId> vertexIds;
697 for ( int vertex : atVertices )
698 {
699 QgsVertexId id;
700 if ( !vertexIdFromVertexNr( vertex, id ) )
701 return false;
702
703 vertexIds.insert( id );
704 }
705
706 // create a copy of the original geometry to restore it in case of failure
707 std::unique_ptr< QgsAbstractGeometry > originalGeometry( d->geometry->clone() );
708
709 detach();
710
711 if ( !d->geometry->deleteVertices( vertexIds ) )
712 {
713 reset( std::move( originalGeometry ) );
714 return false;
715 }
716
717 return true;
718}
719
721{
722 if ( !d->geometry )
723 return false;
724
725 QgsVertexId id;
726 if ( !vertexIdFromVertexNr( atVertex, id ) )
727 return false;
728
729 detach();
730
731 QgsAbstractGeometry *geom = d->geometry.get();
732
733 // If the geom is a collection, we get the concerned part, otherwise, the part is just the whole geom
734 QgsAbstractGeometry *part = nullptr;
736 if ( owningCollection )
737 part = owningCollection->geometryN( id.part );
738 else
739 part = geom;
740
741 // If the part is a polygon, we get the concerned ring, otherwise, the ring is just the whole part
742 QgsAbstractGeometry *ring = nullptr;
744 if ( owningPolygon )
745 ring = ( id.ring == 0 ) ? owningPolygon->exteriorRing() : owningPolygon->interiorRing( id.ring - 1 );
746 else
747 ring = part;
748
749 // If the ring is not a curve, we're probably on a point geometry
750 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( ring );
751 if ( !curve )
752 return false;
753
754 bool success = false;
756 if ( cpdCurve )
757 {
758 // If the geom is a already compound curve, we convert inplace, and we're done
759 success = cpdCurve->toggleCircularAtVertex( id );
760 }
761 else
762 {
763 // TODO : move this block before the above, so we call toggleCircularAtVertex only in one place
764 // If the geom is a linestring or cirularstring, we create a compound curve
765 auto cpdCurve = std::make_unique<QgsCompoundCurve>();
766 cpdCurve->addCurve( curve->clone() );
767 success = cpdCurve->toggleCircularAtVertex( QgsVertexId( -1, -1, id.vertex ) );
768
769 // In that case, we must also reassign the instances
770 if ( success )
771 {
772 if ( !owningPolygon && !owningCollection )
773 {
774 // Standalone linestring
775 reset( std::make_unique<QgsCompoundCurve>( *cpdCurve ) ); // <- REVIEW PLZ
776 }
777 else if ( owningPolygon )
778 {
779 // Replace the ring in the owning polygon
780 if ( id.ring == 0 )
781 {
782 owningPolygon->setExteriorRing( cpdCurve.release() );
783 }
784 else
785 {
786 owningPolygon->removeInteriorRing( id.ring - 1 );
787 owningPolygon->addInteriorRing( cpdCurve.release() );
788 }
789 }
790 else if ( owningCollection )
791 {
792 // Replace the curve in the owning collection
793 owningCollection->removeGeometry( id.part );
794 owningCollection->insertGeometry( cpdCurve.release(), id.part );
795 }
796 }
797 }
798
799 return success;
800}
801
802bool QgsGeometry::insertVertex( double x, double y, int beforeVertex )
803{
804 if ( !d->geometry )
805 {
806 return false;
807 }
808
809 //maintain compatibility with < 2.10 API
810 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::MultiPoint )
811 {
812 detach();
813 //insert geometry instead of point
814 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( x, y ), beforeVertex );
815 }
816
817 QgsVertexId id;
818 if ( !vertexIdFromVertexNr( beforeVertex, id ) )
819 {
820 return false;
821 }
822
823 detach();
824
825 return d->geometry->insertVertex( id, QgsPoint( x, y ) );
826}
827
828bool QgsGeometry::insertVertex( const QgsPoint &point, int beforeVertex )
829{
830 if ( !d->geometry )
831 {
832 return false;
833 }
834
835 //maintain compatibility with < 2.10 API
836 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::MultiPoint )
837 {
838 detach();
839 //insert geometry instead of point
840 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( point ), beforeVertex );
841 }
842
843 QgsVertexId id;
844 if ( !vertexIdFromVertexNr( beforeVertex, id ) )
845 {
846 return false;
847 }
848
849 detach();
850
851 return d->geometry->insertVertex( id, point );
852}
853
854bool QgsGeometry::addTopologicalPoint( const QgsPoint &point, double snappingTolerance, double segmentSearchEpsilon )
855{
856 if ( !d->geometry )
857 {
858 return false;
859 }
860
861 const double sqrSnappingTolerance = snappingTolerance * snappingTolerance;
862 int segmentAfterVertex;
863 QgsPointXY snappedPoint;
864 const double sqrDistSegmentSnap = closestSegmentWithContext( point, snappedPoint, segmentAfterVertex, nullptr, segmentSearchEpsilon );
865
866 if ( sqrDistSegmentSnap > sqrSnappingTolerance )
867 return false;
868
869 int atVertex, beforeVertex, afterVertex;
870 double sqrDistVertexSnap;
871 closestVertex( point, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap );
872
873 if ( sqrDistVertexSnap < sqrSnappingTolerance )
874 return false; // the vertex already exists - do not insert it
875
876 // Let's ignore the Z and M values of the supplied topological point and calculate
877 // interpolated values instead, using the previous and next geometry vertices.
878 // This should make sure that the geometry's Z and M values are preserved when adding
879 // topological points and splitting
880 QgsPoint interpolatedPoint( point );
881 if ( d->geometry.get()->is3D() || d->geometry.get()->isMeasure() )
882 {
883 const QgsPoint vertexBefore = vertexAt( segmentAfterVertex - 1 );
884 const QgsPoint vertexAfter = vertexAt( segmentAfterVertex );
885 interpolatedPoint = QgsGeometryUtils::interpolatePointOnSegment( point.x(), point.y(), vertexBefore, vertexAfter );
886 }
887
888 if ( !insertVertex( interpolatedPoint, segmentAfterVertex ) )
889 {
890 QgsDebugError( u"failed to insert topo point"_s );
891 return false;
892 }
893
894 return true;
895}
896
897QgsPoint QgsGeometry::vertexAt( int atVertex ) const
898{
899 if ( !d->geometry )
900 {
901 return QgsPoint();
902 }
903
904 QgsVertexId vId;
905 ( void ) vertexIdFromVertexNr( atVertex, vId );
906 if ( vId.vertex < 0 )
907 {
908 return QgsPoint();
909 }
910 return d->geometry->vertexAt( vId );
911}
912
913double QgsGeometry::sqrDistToVertexAt( QgsPointXY &point, int atVertex ) const
914{
915 QgsPointXY vertexPoint = vertexAt( atVertex );
916 return QgsGeometryUtils::sqrDistance2D( QgsPoint( vertexPoint ), QgsPoint( point ) );
917}
918
920{
921 // avoid calling geos for trivial point calculations
922 if ( d->geometry && QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
923 {
924 return QgsGeometry( qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->clone() );
925 }
926
927 QgsGeos geos( d->geometry.get() );
928 mLastError.clear();
929 QgsGeometry result = QgsGeometry( geos.closestPoint( other ) );
930 result.mLastError = mLastError;
931 return result;
932}
933
935{
936 // avoid calling geos for trivial point-to-point line calculations
937 if ( d->geometry && QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( other.wkbType() ) == Qgis::WkbType::Point )
938 {
939 return QgsGeometry( std::make_unique< QgsLineString >( *qgsgeometry_cast< const QgsPoint * >( d->geometry.get() ), *qgsgeometry_cast< const QgsPoint * >( other.constGet() ) ) );
940 }
941
942 QgsGeos geos( d->geometry.get() );
943 mLastError.clear();
944 QgsGeometry result = QgsGeometry( geos.shortestLine( other, &mLastError ) );
945 result.mLastError = mLastError;
946 return result;
947}
948
949double QgsGeometry::closestVertexWithContext( const QgsPointXY &point, int &atVertex ) const
950{
951 if ( !d->geometry )
952 {
953 return -1;
954 }
955
956 QgsVertexId vId;
957 QgsPoint pt( point );
958 QgsPoint closestPoint = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, vId );
959 if ( !vId.isValid() )
960 return -1;
961 atVertex = vertexNrFromVertexId( vId );
962 return QgsGeometryUtils::sqrDistance2D( closestPoint, pt );
963}
964
965double QgsGeometry::closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment, double epsilon ) const
966{
967 if ( !d->geometry )
968 {
969 return -1;
970 }
971
972 QgsPoint segmentPt;
973 QgsVertexId vertexAfter;
974
975 double sqrDist = d->geometry->closestSegment( QgsPoint( point ), segmentPt, vertexAfter, leftOrRightOfSegment, epsilon );
976 if ( sqrDist < 0 )
977 return -1;
978
979 minDistPoint.setX( segmentPt.x() );
980 minDistPoint.setY( segmentPt.y() );
981 nextVertexIndex = vertexNrFromVertexId( vertexAfter );
982 return sqrDist;
983}
984
985Qgis::GeometryOperationResult QgsGeometry::addRing( const QVector<QgsPointXY> &ring )
986{
987 auto ringLine = std::make_unique< QgsLineString >( ring );
988 return addRing( ringLine.release() );
989}
990
992{
993 std::unique_ptr< QgsCurve > r( ring );
994 if ( !d->geometry )
995 {
997 }
998
999 detach();
1000
1001 return QgsGeometryEditUtils::addRing( d->geometry.get(), std::move( r ) );
1002}
1003
1004Qgis::GeometryOperationResult QgsGeometry::addPart( const QVector<QgsPointXY> &points, Qgis::GeometryType geomType )
1005{
1007 convertPointList( points, l );
1009 return addPart( l, geomType );
1011}
1012
1014{
1016 convertPointList( points, l );
1017 return addPartV2( l, wkbType );
1018}
1019
1021{
1022 std::unique_ptr< QgsAbstractGeometry > partGeom;
1023 if ( points.size() == 1 )
1024 {
1025 partGeom = std::make_unique< QgsPoint >( points[0] );
1026 }
1027 else if ( points.size() > 1 )
1028 {
1029 auto ringLine = std::make_unique< QgsLineString >();
1030 ringLine->setPoints( points );
1031 partGeom = std::move( ringLine );
1032 }
1034 return addPart( partGeom.release(), geomType );
1036}
1037
1039{
1040 std::unique_ptr< QgsAbstractGeometry > partGeom;
1041 if ( points.size() == 1 )
1042 {
1043 partGeom = std::make_unique< QgsPoint >( points[0] );
1044 }
1045 else if ( points.size() > 1 )
1046 {
1047 auto ringLine = std::make_unique< QgsLineString >();
1048 ringLine->setPoints( points );
1049 partGeom = std::move( ringLine );
1050 }
1051 return addPartV2( partGeom.release(), wkbType );
1052}
1053
1055{
1056 std::unique_ptr< QgsAbstractGeometry > p( part );
1057 if ( !d->geometry )
1058 {
1059 switch ( geomType )
1060 {
1062 reset( std::make_unique< QgsMultiPoint >() );
1063 break;
1065 reset( std::make_unique< QgsMultiLineString >() );
1066 break;
1068 reset( std::make_unique< QgsMultiPolygon >() );
1069 break;
1070 default:
1071 reset( nullptr );
1073 }
1074 }
1075 else
1076 {
1077 detach();
1078 }
1079
1081 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
1082}
1083
1085{
1086 std::unique_ptr< QgsAbstractGeometry > p( part );
1087 if ( !d->geometry )
1088 {
1090 {
1092 reset( std::make_unique< QgsMultiPoint >() );
1093 break;
1095 reset( std::make_unique< QgsMultiLineString >() );
1096 break;
1099 reset( std::make_unique< QgsMultiPolygon >() );
1100 break;
1102 reset( std::make_unique< QgsMultiSurface >() );
1103 break;
1106 reset( std::make_unique< QgsMultiCurve >() );
1107 break;
1109 reset( std::make_unique< QgsPolyhedralSurface >() );
1110 break;
1111 case Qgis::WkbType::TIN:
1112 reset( std::make_unique< QgsTriangulatedSurface >() );
1113 break;
1114 default:
1115 reset( nullptr );
1117 }
1118 }
1119 else
1120 {
1121 detach();
1122 // For TIN and PolyhedralSurface, they already support multiple patches, no conversion needed
1123 const Qgis::WkbType flatType = QgsWkbTypes::flatType( d->geometry->wkbType() );
1124 if ( flatType != Qgis::WkbType::TIN && flatType != Qgis::WkbType::PolyhedralSurface )
1125 {
1127 }
1128 }
1129
1130 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
1131}
1132
1134{
1135 if ( !d->geometry )
1136 {
1138 }
1139 if ( newPart.isNull() || !newPart.d->geometry )
1140 {
1142 }
1143
1144 return addPartV2( newPart.d->geometry->clone() );
1145}
1146
1147QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
1148{
1149 if ( !d->geometry || type() != Qgis::GeometryType::Polygon )
1150 {
1151 return QgsGeometry();
1152 }
1153
1154 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
1155 {
1156 const QVector<QgsGeometry> parts = asGeometryCollection();
1157 QVector<QgsGeometry> results;
1158 results.reserve( parts.count() );
1159 for ( const QgsGeometry &part : parts )
1160 {
1161 QgsGeometry result = part.removeInteriorRings( minimumRingArea );
1162 if ( !result.isNull() )
1163 results << result;
1164 }
1165 if ( results.isEmpty() )
1166 return QgsGeometry();
1167
1168 QgsGeometry first = results.takeAt( 0 );
1169 for ( const QgsGeometry &result : std::as_const( results ) )
1170 {
1171 first.addPart( result );
1172 }
1173 return first;
1174 }
1175 else
1176 {
1177 std::unique_ptr< QgsCurvePolygon > newPoly( static_cast< QgsCurvePolygon * >( d->geometry->clone() ) );
1178 newPoly->removeInteriorRings( minimumRingArea );
1179 return QgsGeometry( std::move( newPoly ) );
1180 }
1181}
1182
1183Qgis::GeometryOperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
1184{
1185 if ( !d->geometry )
1186 {
1188 }
1189
1190 detach();
1191
1192 d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
1194}
1195
1197{
1198 if ( !d->geometry )
1199 {
1201 }
1202
1203 detach();
1204
1205 QTransform t = QTransform::fromTranslate( center.x(), center.y() );
1206 t.rotate( -rotation );
1207 t.translate( -center.x(), -center.y() );
1208 d->geometry->transform( t );
1210}
1211
1212// TODO: Remove this method when reshape() no longer calls it.
1213// i.e., when adapted to GEOS 3.15, since GEOS does the right thing.
1214static void removeDuplicateAdjacentPointsAt( QgsAbstractGeometry *geom, const QgsPointSequence &points )
1215{
1216 // this is a workaround for removing duplicated points introduced by GEOS when splitting 3d geometries
1217 // on topologically added points. It makes no sense to be called for 2d geometries, so it shouldn't.
1218 if ( !geom->is3D() )
1219 {
1220 Q_ASSERT( false );
1221 return;
1222 }
1223
1224 for ( const QgsPoint &pt : points )
1225 {
1226 QgsVertexId vertexId, prevVertexId, nextVertexId;
1227 const QgsPoint closestPt = QgsGeometryUtils::closestVertex( *geom, pt, vertexId );
1228 geom->adjacentVertices( vertexId, prevVertexId, nextVertexId );
1229 const double dist = QgsGeometryUtils::sqrDistance2D( pt, closestPt );
1230 if ( dist == 0 )
1231 {
1232 // make sure the geometry is snapped (z) to the topo point
1233 ( void ) geom->moveVertex( vertexId, pt );
1234 // remove adjacent vertices which are duplicates on the XY plane
1235 if ( const QgsPoint v = geom->vertexAt( prevVertexId ); v.x() == pt.x() && v.y() == pt.y() )
1236 ( void ) geom->deleteVertex( prevVertexId );
1237 else if ( const QgsPoint v = geom->vertexAt( nextVertexId ); v.x() == pt.x() && v.y() == pt.y() )
1238 ( void ) geom->deleteVertex( nextVertexId );
1239 }
1240 }
1241}
1242
1244 const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature
1245)
1246{
1247 QgsPointSequence split, topology;
1248 convertPointList( splitLine, split );
1249 convertPointList( topologyTestPoints, topology );
1250 Qgis::GeometryOperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
1251 convertPointList( topology, topologyTestPoints );
1252 return result;
1253}
1255 const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest
1256)
1257{
1258#if GEOS_VERSION_NUM >= GEOS_COMPUTE_VERSION( 3, 15, 0 )
1259 Q_UNUSED( skipIntersectionTest )
1260#endif
1261 if ( !d->geometry )
1262 {
1264 }
1265 if ( splitLine.isEmpty() )
1266 {
1268 }
1269
1270 // We're trying adding the split line's vertices to the geometry so that
1271 // snap to segment always produces a valid split (see https://github.com/qgis/QGIS/issues/29270)
1272 QgsGeometry tmpGeom( *this );
1273 QVector<QgsGeometry > newGeomsTemp;
1274
1275#if GEOS_VERSION_NUM >= GEOS_COMPUTE_VERSION( 3, 15, 0 )
1276 for ( const QgsPoint &v : splitLine )
1277 {
1278 tmpGeom.addTopologicalPoint( v );
1279 }
1280
1281 std::unique_ptr< QgsAbstractGeometry > splitGeom;
1282 if ( splitLine.size() > 1 )
1283 {
1284 splitGeom = std::make_unique< QgsLineString >( splitLine );
1285 splitGeom->dropZValue();
1286 splitGeom->dropMValue();
1287 }
1288 else if ( splitLine.size() == 1 )
1289 {
1290 splitGeom = std::make_unique< QgsPoint >( splitLine[0].x(), splitLine[0].y() );
1291 }
1292 QgsGeos geos( tmpGeom.get() );
1293 mLastError.clear();
1294 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( *splitGeom.get(), newGeomsTemp, topological, topologyTestPoints, &mLastError );
1295
1296 switch ( result )
1297 {
1299 {
1300 newGeometries = collectSplitGeometries( newGeomsTemp, splitFeature );
1301#else
1302 QgsPointSequence addedTopologicalPoints;
1303 for ( const QgsPoint &v : splitLine )
1304 {
1305 if ( tmpGeom.addTopologicalPoint( v ) )
1306 {
1307 // POLYGON Z geometries need special handling to cater for GEOS limitations.
1308 // Splitting of polygons relies on GEOS extracting lines, unioning with the split line and then polygonizing.
1309 // The problem is that during the union operation GEOS will interpolate new Z values where the split line intersects
1310 // the polygon rings, even though we have added topological points with the correct Z values at that location.
1311 // This results in duplicate vertices and/or wrong Z values on the split geometry.
1312 // Our solution for that is:
1313 // 1. Collect the topo points that were added (these have the desired interpolated Z values).
1314 // 2. Visit the split geometries at the XY location of those topo points and make sure they still have the desired Z value.
1315 // 3. Remove the adjacent vertex to the topo point if it has same XY coordinates. Any vertex with XY coordinates same as a
1316 // topo point was introduced by GEOS and is not wanted.
1317 if ( tmpGeom.constGet()->is3D() && tmpGeom.constGet()->dimension() == 2 )
1318 {
1319 QgsVertexId vId;
1320 const QgsPoint topoPoint = QgsGeometryUtils::closestVertex( *tmpGeom.constGet(), v, vId );
1321 addedTopologicalPoints.append( topoPoint );
1322 }
1323 }
1324 }
1325
1326 QgsLineString splitLineString( splitLine );
1327 splitLineString.dropZValue();
1328 splitLineString.dropMValue();
1329
1330 QgsGeos geos( tmpGeom.get() );
1331 mLastError.clear();
1332 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeomsTemp, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
1333
1334 switch ( result )
1335 {
1337 {
1338 if ( !addedTopologicalPoints.isEmpty() )
1339 {
1340 for ( int i = 0; i < newGeomsTemp.size(); ++i )
1341 {
1342 QgsAbstractGeometry *geom = newGeomsTemp[i].get();
1343 removeDuplicateAdjacentPointsAt( geom, addedTopologicalPoints );
1344 }
1345 }
1346 if ( splitFeature && !newGeomsTemp.isEmpty() )
1347 *this = newGeomsTemp.takeAt( 0 );
1348
1349 newGeometries = newGeomsTemp;
1350#endif
1352 }
1365 //default: do not implement default to handle properly all cases
1366 }
1367
1368 // this should never be reached
1369 Q_ASSERT( false );
1371}
1372
1374 const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature
1375)
1376{
1377#if GEOS_VERSION_NUM >= GEOS_COMPUTE_VERSION( 3, 15, 0 )
1378 Q_UNUSED( preserveCircular );
1379
1380 if ( !d->geometry )
1381 {
1383 }
1384 if ( curve->isEmpty() )
1385 {
1387 }
1388
1389 // We're trying adding the split line's vertices to the geometry so that
1390 // snap to segment always produces a valid split (see https://github.com/qgis/QGIS/issues/29270)
1391 QgsGeometry tmpGeom( *this );
1392 QgsPointSequence curvePoints;
1393 curve->points( curvePoints );
1394
1395 for ( const QgsPoint &v : std::as_const( curvePoints ) )
1396 {
1397 tmpGeom.addTopologicalPoint( v );
1398 }
1399
1400 QVector< QgsGeometry> newGeomsTemp;
1401
1402 QgsGeos geos( tmpGeom.get() );
1403 mLastError.clear();
1404 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( *curve, newGeomsTemp, topological, topologyTestPoints, &mLastError );
1405
1406 switch ( result )
1407 {
1409 {
1410 newGeometries = collectSplitGeometries( newGeomsTemp, splitFeature );
1412 }
1425 //default: do not implement default to handle properly all cases
1426 }
1427
1429#else
1430 std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
1431 QgsPointSequence points;
1432 segmentizedLine->points( points );
1433 Qgis::GeometryOperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
1434
1436 {
1437 if ( preserveCircular )
1438 {
1439 for ( int i = 0; i < newGeometries.count(); ++i )
1440 newGeometries[i] = newGeometries[i].convertToCurves();
1441 *this = convertToCurves();
1442 }
1443 }
1444 return result;
1445#endif
1446}
1447
1449{
1450 if ( !d->geometry )
1451 {
1453 }
1454
1455 // We're trying adding the reshape line's vertices to the geometry so that
1456 // snap to segment always produces a valid reshape
1457 QgsPointSequence reshapePoints;
1458 reshapeLineString.points( reshapePoints );
1459 QgsGeometry tmpGeom( *this );
1460 QgsPointSequence addedTopologicalPoints;
1461 for ( const QgsPoint &v : std::as_const( reshapePoints ) )
1462 {
1463 // TODO: Remove this if block when reshape gets curve support, i.e.,
1464 // when adapted to GEOS 3.15, since GEOS does the right thing.
1465 if ( tmpGeom.addTopologicalPoint( v ) )
1466 {
1467 // When reshaping 3D lines or polygons we want to make sure that any topological points added
1468 // are preserved in the final geometry. GEOS will interpolate between geometry and reshapeLineString
1469 // and may create duplicate vertices with different Z values. We will manually snap Z to those topo
1470 // points later and remove any duplicated vertices.
1471 if ( tmpGeom.constGet()->is3D() )
1472 {
1473 QgsVertexId vId;
1474 const QgsPoint topoPoint = QgsGeometryUtils::closestVertex( *tmpGeom.constGet(), v, vId );
1475 addedTopologicalPoints.append( topoPoint );
1476 }
1477 }
1478 }
1479
1480 QgsGeos geos( tmpGeom.get() );
1482 mLastError.clear();
1483 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1484 if ( errorCode == QgsGeometryEngine::Success && geom )
1485 {
1486 // TODO: Remove this inner if block when reshape gets curve support,
1487 // i.e., when adapted to GEOS 3.15, since GEOS does the right thing.
1488 if ( !addedTopologicalPoints.isEmpty() )
1489 {
1490 removeDuplicateAdjacentPointsAt( geom.get(), addedTopologicalPoints );
1491 }
1492 reset( std::move( geom ) );
1494 }
1495
1496 switch ( errorCode )
1497 {
1508 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1512 }
1513
1514 // should not be reached
1516}
1517
1519{
1520 if ( !d->geometry || !other.d->geometry )
1521 {
1522 return 0;
1523 }
1524
1525 QgsGeos geos( d->geometry.get() );
1526
1527 mLastError.clear();
1528 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.difference( other.constGet(), &mLastError, QgsGeometryParameters(), feedback ) );
1529 if ( !diffGeom )
1530 {
1531 return 1;
1532 }
1533
1534 reset( std::move( diffGeom ) );
1535 return 0;
1536}
1537
1539{
1540 return difference( other, QgsGeometryParameters(), feedback );
1541}
1542
1544{
1545 if ( d->geometry )
1546 {
1547 return d->geometry->boundingBox();
1548 }
1549 return QgsRectangle();
1550}
1551
1553{
1554 if ( d->geometry )
1555 {
1556 return d->geometry->boundingBox3D();
1557 }
1558 return QgsBox3D();
1559}
1560
1561
1562QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1563{
1564 mLastError.clear();
1565
1566 if ( isNull() )
1567 return QgsGeometry();
1568
1569 if ( type() == Qgis::GeometryType::Point && d->geometry->partCount() == 1 )
1570 {
1571 area = 0;
1572 angle = 0;
1573 width = 0;
1574 height = 0;
1575 return QgsGeometry::fromRect( d->geometry->boundingBox() );
1576 }
1577
1578 QgsInternalGeometryEngine engine( *this );
1579 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1580 if ( res.isNull() )
1581 mLastError = engine.lastError();
1582 return res;
1583}
1584
1586{
1587 double area, angle, width, height;
1588 return orientedMinimumBoundingBox( area, angle, width, height );
1589}
1590
1591static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1592{
1593 auto l_boundary = boundary.length();
1594 QgsCircle circ_mec;
1595 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1596 {
1597 switch ( l_boundary )
1598 {
1599 case 0:
1600 circ_mec = QgsCircle();
1601 break;
1602 case 1:
1603 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1604 boundary.pop_back();
1605 break;
1606 case 2:
1607 {
1608 QgsPointXY p1 = boundary.last();
1609 boundary.pop_back();
1610 QgsPointXY p2 = boundary.last();
1611 boundary.pop_back();
1612 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1613 }
1614 break;
1615 default:
1616 QgsPoint p1( boundary.at( 0 ) );
1617 QgsPoint p2( boundary.at( 1 ) );
1618 QgsPoint p3( boundary.at( 2 ) );
1619 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1620 break;
1621 }
1622 return circ_mec;
1623 }
1624 else
1625 {
1626 QgsPointXY pxy = points.last();
1627 points.pop_back();
1628 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1629 QgsPoint p( pxy );
1630 if ( !circ_mec.contains( p ) )
1631 {
1632 boundary.append( pxy );
1633 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1634 }
1635 }
1636 return circ_mec;
1637}
1638
1639QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1640{
1641 center = QgsPointXY();
1642 radius = 0;
1643
1644 if ( isEmpty() )
1645 {
1646 return QgsGeometry();
1647 }
1648
1649 /* optimization */
1650 QgsGeometry hull = convexHull();
1651 if ( hull.isNull() )
1652 return QgsGeometry();
1653
1654 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1656
1657 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1658 center = QgsPointXY( circ.center() );
1659 radius = circ.radius();
1660 QgsGeometry geom;
1661 geom.set( circ.toPolygon( segments ) );
1662 return geom;
1663}
1664
1666{
1667 QgsPointXY center;
1668 double radius;
1669 return minimalEnclosingCircle( center, radius, segments );
1670}
1671
1672QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1673{
1674 QgsInternalGeometryEngine engine( *this );
1675
1676 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1677}
1678
1679QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1680{
1681 QgsInternalGeometryEngine engine( *this );
1682 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1683}
1684
1685QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1686{
1687 QgsInternalGeometryEngine engine( *this );
1688 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1689}
1690
1691QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1692{
1693 QgsInternalGeometryEngine engine( *this );
1694 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1695}
1696
1697QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1698{
1699 QgsInternalGeometryEngine engine( *this );
1700 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1701}
1702
1703QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1704{
1705 QgsInternalGeometryEngine engine( *this );
1706 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1707}
1708
1709QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1710{
1711 QgsInternalGeometryEngine engine( *this );
1712 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1713}
1714
1716 const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset
1717) const
1718{
1719 QgsInternalGeometryEngine engine( *this );
1720 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1721}
1722
1723QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1724{
1725 if ( !d->geometry )
1726 {
1727 return QgsGeometry();
1728 }
1729 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1730}
1731
1732bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1733{
1734 if ( !d->geometry )
1735 return false;
1736
1737 detach();
1738 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1739}
1740
1742{
1743 // fast case, check bounding boxes
1744 if ( !boundingBoxIntersects( r ) )
1745 return false;
1746
1747 const Qgis::WkbType flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1748 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1749 if ( flatType == Qgis::WkbType::Point )
1750 {
1751 return true;
1752 }
1753
1754#if GEOS_VERSION_NUM < GEOS_COMPUTE_VERSION( 3, 12, 0 )
1755 // Workaround for issue issue GH #51492
1756 // in case of multi polygon, intersection with an empty rect fails
1757 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1758 {
1759 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1760 return contains( QgsGeometry::fromPointXY( center ) );
1761 }
1762#endif
1763
1764 QgsGeometry g = fromRect( r );
1765 return intersects( g );
1766}
1767
1768bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1769{
1770 if ( !d->geometry || geometry.isNull() )
1771 {
1772 return false;
1773 }
1774
1775 QgsGeos geos( d->geometry.get() );
1776 mLastError.clear();
1777 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1778}
1779
1781{
1782 if ( !d->geometry )
1783 {
1784 return false;
1785 }
1786
1787 return d->geometry->boundingBoxIntersects( rectangle );
1788}
1789
1791{
1792 if ( !d->geometry || geometry.isNull() )
1793 {
1794 return false;
1795 }
1796
1797 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1798}
1799
1800bool QgsGeometry::contains( const QgsPointXY *p ) const
1801{
1802 if ( !d->geometry || !p )
1803 {
1804 return false;
1805 }
1806
1807 QgsGeos geos( d->geometry.get() );
1808 mLastError.clear();
1809 return geos.contains( p->x(), p->y(), &mLastError );
1810}
1811
1812bool QgsGeometry::contains( double x, double y ) const
1813{
1814 if ( !d->geometry )
1815 {
1816 return false;
1817 }
1818
1819 QgsGeos geos( d->geometry.get() );
1820 mLastError.clear();
1821 return geos.contains( x, y, &mLastError );
1822}
1823
1824bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1825{
1826 if ( !d->geometry || geometry.isNull() )
1827 {
1828 return false;
1829 }
1830
1831 QgsGeos geos( d->geometry.get() );
1832 mLastError.clear();
1833 return geos.contains( geometry.d->geometry.get(), &mLastError );
1834}
1835
1836bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1837{
1838 if ( !d->geometry || geometry.isNull() )
1839 {
1840 return false;
1841 }
1842
1843 QgsGeos geos( d->geometry.get() );
1844 mLastError.clear();
1845 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1846}
1847
1848bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1849{
1850 return isExactlyEqual( geometry );
1851}
1852
1853bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1854{
1855 if ( !d->geometry || geometry.isNull() )
1856 {
1857 return false;
1858 }
1859
1860 QgsGeos geos( d->geometry.get() );
1861 mLastError.clear();
1862 return geos.touches( geometry.d->geometry.get(), &mLastError );
1863}
1864
1865bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1866{
1867 if ( !d->geometry || geometry.isNull() )
1868 {
1869 return false;
1870 }
1871
1872 QgsGeos geos( d->geometry.get() );
1873 mLastError.clear();
1874 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1875}
1876
1877bool QgsGeometry::within( const QgsGeometry &geometry ) const
1878{
1879 if ( !d->geometry || geometry.isNull() )
1880 {
1881 return false;
1882 }
1883
1884 QgsGeos geos( d->geometry.get() );
1885 mLastError.clear();
1886 return geos.within( geometry.d->geometry.get(), &mLastError );
1887}
1888
1889bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1890{
1891 if ( !d->geometry || geometry.isNull() )
1892 {
1893 return false;
1894 }
1895
1896 QgsGeos geos( d->geometry.get() );
1897 mLastError.clear();
1898 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1899}
1900
1901QString QgsGeometry::asWkt( int precision ) const
1902{
1903 if ( !d->geometry )
1904 {
1905 return QString();
1906 }
1907 return d->geometry->asWkt( precision );
1908}
1909
1910QString QgsGeometry::asJson( int precision ) const
1911{
1912 return asGeoJson( precision, Qgis::GeoJsonProfile::Rfc7946 );
1913}
1914
1915QString QgsGeometry::asGeoJson( int precision, Qgis::GeoJsonProfile profile ) const
1916{
1917 return QString::fromStdString( asJsonObject( precision, profile ).dump() );
1918}
1919
1920json QgsGeometry::asJsonObject( int precision, Qgis::GeoJsonProfile profile ) const
1921{
1922 if ( !d->geometry )
1923 {
1924 return nullptr;
1925 }
1926 return d->geometry->asJsonObject( precision, profile );
1927}
1928
1929QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM, bool avoidDuplicates ) const
1930{
1931 mLastError.clear();
1932 QVector< QgsGeometry > res;
1933 if ( isNull() )
1934 return res;
1935
1936 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1937 {
1938 res << *this;
1939 return res;
1940 }
1941
1943 {
1944 return res;
1945 }
1946
1947 QgsGeometry newGeom = *this;
1948
1949 // Curved -> straight
1951 {
1952 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1953 }
1954
1955 // Handle NurbsCurve: if target is curved but NOT NurbsCurve, and source contains NurbsCurve,
1956 // we need to segmentize the NURBS parts first
1958 {
1959 // Check if geometry contains NurbsCurve that needs conversion
1960 bool hasNurbs = false;
1961 if ( QgsWkbTypes::isNurbsType( newGeom.wkbType() ) )
1962 {
1963 hasNurbs = true;
1964 }
1965 else if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( newGeom.constGet() ) )
1966 {
1967 for ( int i = 0; i < collection->numGeometries(); ++i )
1968 {
1969 if ( QgsWkbTypes::isNurbsType( collection->geometryN( i )->wkbType() ) )
1970 {
1971 hasNurbs = true;
1972 break;
1973 }
1974 }
1975 }
1976 else if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( newGeom.constGet() ) )
1977 {
1978 if ( cp->exteriorRing() && QgsWkbTypes::isNurbsType( cp->exteriorRing()->wkbType() ) )
1979 hasNurbs = true;
1980 for ( int i = 0; !hasNurbs && i < cp->numInteriorRings(); ++i )
1981 {
1982 if ( QgsWkbTypes::isNurbsType( cp->interiorRing( i )->wkbType() ) )
1983 hasNurbs = true;
1984 }
1985 }
1987 {
1988 for ( int i = 0; i < cc->nCurves(); ++i )
1989 {
1990 if ( QgsWkbTypes::isNurbsType( cc->curveAt( i )->wkbType() ) )
1991 {
1992 hasNurbs = true;
1993 break;
1994 }
1995 }
1996 }
1997
1998 if ( hasNurbs )
1999 {
2000 // Segmentize to remove NURBS, then we'll convert back to curve type below
2001 newGeom = QgsGeometry( newGeom.constGet()->segmentize() );
2002 }
2003 }
2004
2005 // polygon -> line
2007 {
2008 // boundary gives us a (multi)line string of exterior + interior rings
2009 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
2010 }
2011 // line -> polygon
2013 {
2014 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
2015 const QgsGeometry source = newGeom;
2016 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
2017 {
2018 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
2019 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
2020 {
2022 {
2023 auto cp = std::make_unique< QgsCurvePolygon >();
2024 cp->setExteriorRing( curve );
2025 ( void ) exterior.release();
2026 gc->addGeometry( cp.release() );
2027 }
2028 else
2029 {
2030 auto p = std::make_unique< QgsPolygon >();
2031 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
2032 ( void ) exterior.release();
2033 gc->addGeometry( p.release() );
2034 }
2035 }
2036 }
2037 newGeom = QgsGeometry( std::move( gc ) );
2038 }
2039
2040 // line/polygon -> points
2042 {
2043 // lines/polygons to a point layer, extract all vertices
2044 auto mp = std::make_unique< QgsMultiPoint >();
2045 const QgsGeometry source = newGeom;
2046 QSet< QgsPoint > added;
2047 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
2048 {
2049 if ( avoidDuplicates && added.contains( *vertex ) )
2050 continue; // avoid duplicate points, e.g. start/end of rings
2051 mp->addGeometry( ( *vertex ).clone() );
2052 added.insert( *vertex );
2053 }
2054 newGeom = QgsGeometry( std::move( mp ) );
2055 }
2056
2057 //(Multi)Polygon to PolyhedralSurface
2059 {
2060 auto polySurface = std::make_unique< QgsPolyhedralSurface >();
2061 const QgsGeometry source = newGeom;
2062 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
2063 {
2064 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
2065 {
2066 polySurface->addPatch( polygon->clone() );
2067 }
2068 }
2069 newGeom = QgsGeometry( std::move( polySurface ) );
2070 }
2071
2072 //(Multi)Polygon/Triangle to TIN
2075 {
2076 auto tin = std::make_unique< QgsTriangulatedSurface >();
2077 const QgsGeometry source = newGeom;
2078 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
2079 {
2080 if ( const QgsTriangle *triangle = qgsgeometry_cast< const QgsTriangle * >( *part ) )
2081 {
2082 tin->addPatch( triangle->clone() );
2083 }
2084 else if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
2085 {
2086 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
2087 if ( polygon->exteriorRing() )
2088 {
2089 const int numPoints = polygon->exteriorRing()->numPoints();
2090 if ( numPoints != 4 )
2091 {
2092 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
2093 return res;
2094 }
2095 auto triangle = std::make_unique< QgsTriangle >();
2096 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
2097 tin->addPatch( triangle.release() );
2098 }
2099 }
2100 }
2101 newGeom = QgsGeometry( std::move( tin ) );
2102 }
2103
2104 // PolyhedralSurface/TIN to (Multi)Polygon
2107 {
2108 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
2110 {
2111 for ( int i = 0; i < polySurface->numPatches(); ++i )
2112 {
2113 const QgsPolygon *patch = polySurface->patchN( i );
2114 auto polygon = std::make_unique< QgsPolygon >();
2115 polygon->setExteriorRing( patch->exteriorRing()->clone() );
2116 for ( int j = 0; j < patch->numInteriorRings(); ++j )
2117 {
2118 polygon->addInteriorRing( patch->interiorRing( j )->clone() );
2119 }
2120 multiPolygon->addGeometry( polygon.release() );
2121 }
2122 }
2123 newGeom = QgsGeometry( std::move( multiPolygon ) );
2124 }
2125
2126 // Polygon -> Triangle
2128 {
2129 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( newGeom.constGet() ) )
2130 {
2131 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
2132 if ( polygon->exteriorRing() )
2133 {
2134 const int numPoints = polygon->exteriorRing()->numPoints();
2135 if ( numPoints != 4 )
2136 {
2137 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
2138 return res;
2139 }
2140 auto triangle = std::make_unique< QgsTriangle >();
2141 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
2142 newGeom = QgsGeometry( std::move( triangle ) );
2143 }
2144 }
2145 }
2146
2147
2148 // Single -> multi
2149 if ( QgsWkbTypes::isMultiType( type ) && !newGeom.isMultipart() )
2150 {
2151 newGeom.convertToMultiType();
2152 }
2153 // Drop Z/M
2154 if ( newGeom.constGet()->is3D() && !QgsWkbTypes::hasZ( type ) )
2155 {
2156 newGeom.get()->dropZValue();
2157 }
2158 if ( newGeom.constGet()->isMeasure() && !QgsWkbTypes::hasM( type ) )
2159 {
2160 newGeom.get()->dropMValue();
2161 }
2162 // Add Z/M back, set to 0
2163 if ( !newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
2164 {
2165 newGeom.get()->addZValue( defaultZ );
2166 }
2167 if ( !newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
2168 {
2169 newGeom.get()->addMValue( defaultM );
2170 }
2171
2172 // Straight -> curve
2174 {
2175 newGeom.convertToCurvedMultiType();
2176 }
2177
2178 // Multi -> single
2179 if ( !QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart() )
2180 {
2181 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
2182 res.reserve( parts->partCount() );
2183 for ( int i = 0; i < parts->partCount(); i++ )
2184 {
2185 const QgsAbstractGeometry *part = parts->geometryN( i );
2187 res << QgsGeometry( part->toCurveType() );
2188 else
2189 res << QgsGeometry( part->clone() );
2190 }
2191 }
2192 // GeometryCollection (of Point/LineString/Polygon) -> MultiPoint/MultiLineString/MultiPolygon
2194 {
2196 const QgsGeometryCollection *geomColl( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
2197
2198 bool allExpectedType = true;
2199 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2200 {
2201 if ( geomColl->geometryN( i )->wkbType() != singleType )
2202 {
2203 allExpectedType = false;
2204 break;
2205 }
2206 }
2207 if ( allExpectedType )
2208 {
2209 std::unique_ptr< QgsGeometryCollection > newGeomCol;
2211 {
2212 newGeomCol = std::make_unique< QgsMultiPoint >();
2213 }
2215 {
2216 newGeomCol = std::make_unique< QgsMultiLineString >();
2217 }
2218 else
2219 {
2220 newGeomCol = std::make_unique< QgsMultiPolygon >();
2221 }
2222 newGeomCol->reserve( geomColl->numGeometries() );
2223 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2224 {
2225 newGeomCol->addGeometry( geomColl->geometryN( i )->clone() );
2226 }
2227 res << QgsGeometry( std::move( newGeomCol ) );
2228 }
2229 else
2230 {
2231 res << newGeom;
2232 }
2233 }
2234 else
2235 {
2236 res << newGeom;
2237 }
2238 return res;
2239}
2240
2241QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
2242{
2243 switch ( destType )
2244 {
2246 return convertToPoint( destMultipart );
2247
2249 return convertToLine( destMultipart );
2250
2252 return convertToPolygon( destMultipart );
2253
2254 default:
2255 return QgsGeometry();
2256 }
2257}
2258
2260{
2261 if ( !d->geometry )
2262 {
2263 return false;
2264 }
2265
2266 if ( isMultipart() ) //already multitype, no need to convert
2267 {
2268 return true;
2269 }
2270
2271 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
2273 if ( !multiGeom )
2274 {
2275 return false;
2276 }
2277
2278 //try to avoid cloning existing geometry whenever we can
2279
2280 //want to see a magic trick?... gather round kiddies...
2281 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
2282 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
2283 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
2284 multiGeom->addGeometry( d->geometry.release() );
2285 // and replace it with the multi geometry.
2286 // TADA! a clone free conversion in some cases
2287 d->geometry = std::move( geom );
2288 return true;
2289}
2290
2292{
2293 if ( !d->geometry )
2294 {
2295 return false;
2296 }
2297
2298 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
2299 {
2304 {
2305 return true;
2306 }
2307 default:
2308 break;
2309 }
2310
2311 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
2313 if ( !multiGeom )
2314 {
2315 return false;
2316 }
2317
2318 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2319 if ( sourceMultiGeom )
2320 {
2321 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
2322 {
2323 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
2324 return false;
2325 }
2326 }
2327 else
2328 {
2329 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
2330 return false;
2331 }
2332
2333 reset( std::move( geom ) );
2334 return true;
2335}
2336
2338{
2339 if ( !d->geometry )
2340 {
2341 return false;
2342 }
2343
2344 if ( !isMultipart() ) //already single part, no need to convert
2345 {
2346 return true;
2347 }
2348
2349 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2350 if ( !multiGeom || multiGeom->partCount() < 1 )
2351 return false;
2352
2353 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
2354 reset( std::move( firstPart ) );
2355 return true;
2356}
2357
2358
2360{
2362 if ( !origGeom )
2363 return false;
2364
2365 std::unique_ptr<QgsGeometryCollection> resGeom;
2366 switch ( geomType )
2367 {
2369 resGeom = std::make_unique<QgsMultiPoint>();
2370 break;
2372 resGeom = std::make_unique<QgsMultiLineString>();
2373 break;
2375 resGeom = std::make_unique<QgsMultiPolygon>();
2376 break;
2377 default:
2378 break;
2379 }
2380 if ( !resGeom )
2381 return false;
2382
2383 resGeom->reserve( origGeom->numGeometries() );
2384 for ( int i = 0; i < origGeom->numGeometries(); ++i )
2385 {
2386 const QgsAbstractGeometry *g = origGeom->geometryN( i );
2387 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
2388 resGeom->addGeometry( g->clone() );
2389 }
2390
2391 set( resGeom.release() );
2392 return true;
2393}
2394
2395
2397{
2398 if ( !d->geometry )
2399 {
2400 return QgsPointXY();
2401 }
2402 if ( const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
2403 {
2404 return QgsPointXY( pt->x(), pt->y() );
2405 }
2406 else
2407 {
2408 return QgsPointXY();
2409 }
2410}
2411
2413{
2414 QgsPolylineXY polyLine;
2415 if ( !d->geometry )
2416 {
2417 return polyLine;
2418 }
2419
2420 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve || QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CircularString );
2421 std::unique_ptr< QgsLineString > segmentizedLine;
2422 QgsLineString *line = nullptr;
2423 if ( doSegmentation )
2424 {
2425 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
2426 if ( !curve )
2427 {
2428 return polyLine;
2429 }
2430 segmentizedLine.reset( curve->curveToLine() );
2431 line = segmentizedLine.get();
2432 }
2433 else
2434 {
2435 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
2436 if ( !line )
2437 {
2438 return polyLine;
2439 }
2440 }
2441
2442 int nVertices = line->numPoints();
2443 polyLine.resize( nVertices );
2444 QgsPointXY *data = polyLine.data();
2445 const double *xData = line->xData();
2446 const double *yData = line->yData();
2447 for ( int i = 0; i < nVertices; ++i )
2448 {
2449 data->setX( *xData++ );
2450 data->setY( *yData++ );
2451 data++;
2452 }
2453
2454 return polyLine;
2455}
2456
2458{
2459 if ( !d->geometry )
2460 return QgsPolygonXY();
2461
2462 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
2463
2464 QgsPolygon *p = nullptr;
2465 std::unique_ptr< QgsPolygon > segmentized;
2466 if ( doSegmentation )
2467 {
2468 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
2469 if ( !curvePoly )
2470 {
2471 return QgsPolygonXY();
2472 }
2473 segmentized.reset( curvePoly->toPolygon() );
2474 p = segmentized.get();
2475 }
2476 else
2477 {
2478 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
2479 }
2480
2481 if ( !p )
2482 {
2483 return QgsPolygonXY();
2484 }
2485
2486 QgsPolygonXY polygon;
2487 convertPolygon( *p, polygon );
2488
2489 return polygon;
2490}
2491
2493{
2494 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
2495 {
2496 return QgsMultiPointXY();
2497 }
2498
2499 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
2500 if ( !mp )
2501 {
2502 return QgsMultiPointXY();
2503 }
2504
2505 int nPoints = mp->numGeometries();
2506 QgsMultiPointXY multiPoint( nPoints );
2507 for ( int i = 0; i < nPoints; ++i )
2508 {
2509 const QgsPoint *pt = mp->pointN( i );
2510 multiPoint[i].setX( pt->x() );
2511 multiPoint[i].setY( pt->y() );
2512 }
2513 return multiPoint;
2514}
2515
2517{
2518 if ( !d->geometry )
2519 {
2520 return QgsMultiPolylineXY();
2521 }
2522
2523 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2524 if ( !geomCollection )
2525 {
2526 return QgsMultiPolylineXY();
2527 }
2528
2529 int nLines = geomCollection->numGeometries();
2530 if ( nLines < 1 )
2531 {
2532 return QgsMultiPolylineXY();
2533 }
2534
2536 mpl.reserve( nLines );
2537 for ( int i = 0; i < nLines; ++i )
2538 {
2539 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2540 std::unique_ptr< QgsLineString > segmentized;
2541 if ( !line )
2542 {
2543 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2544 if ( !curve )
2545 {
2546 continue;
2547 }
2548 segmentized.reset( curve->curveToLine() );
2549 line = segmentized.get();
2550 }
2551
2552 QgsPolylineXY polyLine;
2553 int nVertices = line->numPoints();
2554 polyLine.resize( nVertices );
2555 QgsPointXY *data = polyLine.data();
2556 const double *xData = line->xData();
2557 const double *yData = line->yData();
2558 for ( int i = 0; i < nVertices; ++i )
2559 {
2560 data->setX( *xData++ );
2561 data->setY( *yData++ );
2562 data++;
2563 }
2564 mpl.append( polyLine );
2565 }
2566 return mpl;
2567}
2568
2570{
2571 if ( !d->geometry )
2572 {
2573 return QgsMultiPolygonXY();
2574 }
2575
2576 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2577 if ( !geomCollection )
2578 {
2579 return QgsMultiPolygonXY();
2580 }
2581
2582 const int nPolygons = geomCollection->numGeometries();
2583 if ( nPolygons < 1 )
2584 {
2585 return QgsMultiPolygonXY();
2586 }
2587
2589 mp.reserve( nPolygons );
2590 for ( int i = 0; i < nPolygons; ++i )
2591 {
2592 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2593 if ( !polygon )
2594 {
2595 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2596 if ( cPolygon )
2597 {
2598 polygon = cPolygon->toPolygon();
2599 }
2600 else
2601 {
2602 continue;
2603 }
2604 }
2605
2606 QgsPolygonXY poly;
2607 convertPolygon( *polygon, poly );
2608 mp.push_back( poly );
2609 }
2610 return mp;
2611}
2612
2613double QgsGeometry::area() const
2614{
2615 if ( !d->geometry )
2616 {
2617 return -1.0;
2618 }
2619
2620 return d->geometry->area();
2621}
2622
2624{
2625 if ( !d->geometry )
2626 {
2627 throw QgsInvalidArgumentException( "Cannot compute 3D area: geometry is null." );
2628 }
2629
2630 return d->geometry->area3D();
2631}
2632
2634{
2635 if ( !d->geometry )
2636 {
2637 return -1.0;
2638 }
2639
2640 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2641 {
2643 return 0.0;
2644
2646 return d->geometry->length();
2647
2649 return d->geometry->perimeter();
2650
2653 return d->geometry->length();
2654 }
2655 return -1;
2656}
2657
2658double QgsGeometry::distance( const QgsGeometry &geom ) const
2659{
2660 if ( !d->geometry || !geom.d->geometry )
2661 {
2662 return -1.0;
2663 }
2664
2665 // avoid calling geos for trivial point-to-point distance calculations
2666 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( geom.wkbType() ) == Qgis::WkbType::Point )
2667 {
2668 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2669 }
2670
2671 QgsGeos g( d->geometry.get() );
2672 mLastError.clear();
2673 return g.distance( geom.d->geometry.get(), &mLastError );
2674}
2675
2677{
2678 if ( !d->geometry || !geom.d->geometry )
2679 {
2680 return -1.0;
2681 }
2682
2683 QgsGeos g( d->geometry.get() );
2684 mLastError.clear();
2685 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2686}
2687
2688double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2689{
2690 if ( !d->geometry || !geom.d->geometry )
2691 {
2692 return -1.0;
2693 }
2694
2695 QgsGeos g( d->geometry.get() );
2696 mLastError.clear();
2697 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2698}
2699
2700
2702{
2703 if ( !d->geometry || !geom.d->geometry )
2704 {
2705 return -1.0;
2706 }
2707
2708 QgsGeos g( d->geometry.get() );
2709 mLastError.clear();
2710 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
2711}
2712
2713double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2714{
2715 if ( !d->geometry || !geom.d->geometry )
2716 {
2717 return -1.0;
2718 }
2719
2720 QgsGeos g( d->geometry.get() );
2721 mLastError.clear();
2722 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2723}
2724
2726{
2727 if ( !d->geometry || d->geometry.get()->isEmpty() )
2729 return d->geometry->vertices_begin();
2730}
2731
2733{
2734 if ( !d->geometry || d->geometry.get()->isEmpty() )
2736 return d->geometry->vertices_end();
2737}
2738
2740{
2741 if ( !d->geometry || d->geometry.get()->isEmpty() )
2742 return QgsVertexIterator();
2743 return QgsVertexIterator( d->geometry.get() );
2744}
2745
2747{
2748 if ( !d->geometry )
2750
2751 detach();
2752 return d->geometry->parts_begin();
2753}
2754
2756{
2757 if ( !d->geometry )
2759 return d->geometry->parts_end();
2760}
2761
2763{
2764 if ( !d->geometry )
2766 return d->geometry->const_parts_begin();
2767}
2768
2770{
2771 if ( !d->geometry )
2773 return d->geometry->const_parts_end();
2774}
2775
2777{
2778 if ( !d->geometry )
2779 return QgsGeometryPartIterator();
2780
2781 detach();
2782 return QgsGeometryPartIterator( d->geometry.get() );
2783}
2784
2786{
2787 if ( !d->geometry )
2789
2790 return QgsGeometryConstPartIterator( d->geometry.get() );
2791}
2792
2793QgsGeometry QgsGeometry::buffer( double distance, int segments, QgsFeedback *feedback ) const
2794{
2795 if ( !d->geometry )
2796 {
2797 return QgsGeometry();
2798 }
2799
2800 QgsGeos g( d->geometry.get() );
2801 mLastError.clear();
2802 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError, feedback ) );
2803 if ( !geom )
2804 {
2805 QgsGeometry result;
2806 result.mLastError = mLastError;
2807 return result;
2808 }
2809 return QgsGeometry( std::move( geom ) );
2810}
2811
2812QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, QgsFeedback *feedback ) const
2813{
2814 if ( !d->geometry )
2815 {
2816 return QgsGeometry();
2817 }
2818
2819 QgsGeos g( d->geometry.get() );
2820 mLastError.clear();
2821 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError, feedback );
2822 if ( !geom )
2823 {
2824 QgsGeometry result;
2825 result.mLastError = mLastError;
2826 return result;
2827 }
2828 return QgsGeometry( geom );
2829}
2830
2831QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2832{
2833 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2834 {
2835 return QgsGeometry();
2836 }
2837
2838 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2839 {
2840 const QVector<QgsGeometry> parts = asGeometryCollection();
2841 QVector<QgsGeometry> results;
2842 results.reserve( parts.count() );
2843 for ( const QgsGeometry &part : parts )
2844 {
2845 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2846 if ( !result.isNull() )
2847 results << result;
2848 }
2849 if ( results.isEmpty() )
2850 return QgsGeometry();
2851
2852 QgsGeometry first = results.takeAt( 0 );
2853 for ( const QgsGeometry &result : std::as_const( results ) )
2854 {
2855 first.addPart( result );
2856 }
2857 return first;
2858 }
2859 else
2860 {
2861 QgsGeos geos( d->geometry.get() );
2862 mLastError.clear();
2863
2864 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2865 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2866
2867 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2868 if ( !offsetGeom )
2869 {
2870 QgsGeometry result;
2871 result.mLastError = mLastError;
2872 return result;
2873 }
2874
2875 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2876 {
2877 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2878 if ( newOrientation != prevOrientation )
2879 {
2880 // GEOS has flipped line orientation, flip it back
2881 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2882 offsetGeom = std::move( flipped );
2883 }
2884 }
2885 return QgsGeometry( std::move( offsetGeom ) );
2886 }
2887}
2888
2889QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2890{
2891 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2892 {
2893 return QgsGeometry();
2894 }
2895
2896 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2897 {
2898 const QVector<QgsGeometry> parts = asGeometryCollection();
2899 QVector<QgsGeometry> results;
2900 results.reserve( parts.count() );
2901 for ( const QgsGeometry &part : parts )
2902 {
2903 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2904 if ( !result.isNull() )
2905 results << result;
2906 }
2907 if ( results.isEmpty() )
2908 return QgsGeometry();
2909
2910 QgsGeometry first = results.takeAt( 0 );
2911 for ( const QgsGeometry &result : std::as_const( results ) )
2912 {
2913 first.addPart( result );
2914 }
2915 return first;
2916 }
2917 else
2918 {
2919 QgsGeos geos( d->geometry.get() );
2920 mLastError.clear();
2921 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit, &mLastError );
2922 if ( !bufferGeom )
2923 {
2924 QgsGeometry result;
2925 result.mLastError = mLastError;
2926 return result;
2927 }
2928 return QgsGeometry( std::move( bufferGeom ) );
2929 }
2930}
2931
2932QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2933{
2934 QgsInternalGeometryEngine engine( *this );
2935
2936 return engine.taperedBuffer( startWidth, endWidth, segments );
2937}
2938
2940{
2941 QgsInternalGeometryEngine engine( *this );
2942
2943 return engine.variableWidthBufferByM( segments );
2944}
2945
2946QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance, double startDeflection, double endDeflection ) const
2947{
2948 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2949 {
2950 return QgsGeometry();
2951 }
2952
2953 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2954 {
2955 const QVector<QgsGeometry> parts = asGeometryCollection();
2956 QVector<QgsGeometry> results;
2957 results.reserve( parts.count() );
2958 for ( const QgsGeometry &part : parts )
2959 {
2960 QgsGeometry result = part.extendLine( startDistance, endDistance, startDeflection, endDeflection );
2961 if ( !result.isNull() )
2962 results << result;
2963 }
2964 if ( results.isEmpty() )
2965 return QgsGeometry();
2966
2967 QgsGeometry first = results.takeAt( 0 );
2968 for ( const QgsGeometry &result : std::as_const( results ) )
2969 {
2970 first.addPart( result );
2971 }
2972 return first;
2973 }
2974 else
2975 {
2976 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2977 if ( !line )
2978 return QgsGeometry();
2979
2980 std::unique_ptr< QgsLineString > newLine( line->clone() );
2981 newLine->extend( startDistance, endDistance, startDeflection, endDeflection );
2982 return QgsGeometry( std::move( newLine ) );
2983 }
2984}
2985
2986QgsGeometry QgsGeometry::simplify( double tolerance, QgsFeedback *feedback ) const
2987{
2988 if ( !d->geometry )
2989 {
2990 return QgsGeometry();
2991 }
2992
2993 QgsGeos geos( d->geometry.get() );
2994 mLastError.clear();
2995 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError, feedback ) );
2996 if ( !simplifiedGeom )
2997 {
2998 QgsGeometry result;
2999 result.mLastError = mLastError;
3000 return result;
3001 }
3002 return QgsGeometry( std::move( simplifiedGeom ) );
3003}
3004
3005QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
3006{
3007 QgsInternalGeometryEngine engine( *this );
3008
3009 return engine.densifyByCount( extraNodesPerSegment );
3010}
3011
3013{
3014 QgsInternalGeometryEngine engine( *this );
3015
3016 return engine.densifyByDistance( distance );
3017}
3018
3019QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
3020{
3021 QgsInternalGeometryEngine engine( *this );
3022
3023 return engine.convertToCurves( distanceTolerance, angleTolerance );
3024}
3025
3027{
3028 if ( !d->geometry )
3029 {
3030 return QgsGeometry();
3031 }
3032
3033 // avoid calling geos for trivial point centroids
3034 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
3035 {
3036 QgsGeometry c = *this;
3037 c.get()->dropZValue();
3038 c.get()->dropMValue();
3039 return c;
3040 }
3041
3042 QgsGeos geos( d->geometry.get() );
3043
3044 mLastError.clear();
3045 QgsGeometry result( geos.centroid( &mLastError ) );
3046 result.mLastError = mLastError;
3047 return result;
3048}
3049
3051{
3052 if ( !d->geometry )
3053 {
3054 return QgsGeometry();
3055 }
3056
3057 QgsGeos geos( d->geometry.get() );
3058
3059 mLastError.clear();
3060 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
3061 result.mLastError = mLastError;
3062 return result;
3063}
3064
3065QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
3066{
3067 QgsInternalGeometryEngine engine( *this );
3068
3069 return engine.poleOfInaccessibility( precision, distanceToBoundary );
3070}
3071
3072QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
3073{
3074 if ( !d->geometry )
3075 {
3076 return QgsGeometry();
3077 }
3078
3079 QgsGeos geos( d->geometry.get() );
3080
3081 mLastError.clear();
3082 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
3083 result.mLastError = mLastError;
3084 return result;
3085}
3086
3088{
3089 if ( !d->geometry )
3090 {
3091 return QgsGeometry();
3092 }
3093
3094 QgsGeos geos( d->geometry.get() );
3095
3096 mLastError.clear();
3097 QgsGeometry result( geos.minimumWidth( &mLastError ) );
3098 result.mLastError = mLastError;
3099 return result;
3100}
3101
3103{
3104 if ( !d->geometry )
3105 {
3106 return std::numeric_limits< double >::quiet_NaN();
3107 }
3108
3109 QgsGeos geos( d->geometry.get() );
3110
3111 mLastError.clear();
3112 return geos.minimumClearance( &mLastError );
3113}
3114
3116{
3117 if ( !d->geometry )
3118 {
3119 return QgsGeometry();
3120 }
3121
3122 QgsGeos geos( d->geometry.get() );
3123
3124 mLastError.clear();
3125 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
3126 result.mLastError = mLastError;
3127 return result;
3128}
3129
3131{
3132 if ( !d->geometry )
3133 {
3134 return QgsGeometry();
3135 }
3136 QgsGeos geos( d->geometry.get() );
3137 mLastError.clear();
3138 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
3139 if ( !cHull )
3140 {
3141 QgsGeometry geom;
3142 geom.mLastError = mLastError;
3143 return geom;
3144 }
3145 return QgsGeometry( std::move( cHull ) );
3146}
3147
3148QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles, QgsFeedback *feedback ) const
3149{
3150 if ( !d->geometry )
3151 {
3152 return QgsGeometry();
3153 }
3154 QgsGeos geos( d->geometry.get() );
3155 mLastError.clear();
3156 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError, feedback ) );
3157 if ( !concaveHull )
3158 {
3159 QgsGeometry geom;
3160 geom.mLastError = mLastError;
3161 return geom;
3162 }
3163 return QgsGeometry( std::move( concaveHull ) );
3164}
3165
3166QgsGeometry QgsGeometry::concaveHullOfPolygons( double lengthRatio, bool allowHoles, bool isTight, QgsFeedback *feedback ) const
3167{
3168 if ( !d->geometry )
3169 {
3170 return QgsGeometry();
3171 }
3172
3174 {
3175 QgsGeometry geom;
3176 geom.mLastError = u"Only Polygon or MultiPolygon geometries are supported"_s;
3177 return geom;
3178 }
3179
3180 QgsGeos geos( d->geometry.get() );
3181 mLastError.clear();
3182 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHullOfPolygons( lengthRatio, allowHoles, isTight, &mLastError, feedback ) );
3183 if ( !concaveHull )
3184 {
3185 QgsGeometry geom;
3186 geom.mLastError = mLastError;
3187 return geom;
3188 }
3189 return QgsGeometry( std::move( concaveHull ) );
3190}
3191
3192QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
3193{
3194 if ( !d->geometry )
3195 {
3196 return QgsGeometry();
3197 }
3198
3199 QgsGeos geos( d->geometry.get() );
3200 mLastError.clear();
3201 QgsGeometry result = QgsGeometry( geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError ) );
3202 result.mLastError = mLastError;
3203 return result;
3204}
3205
3206QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
3207{
3208 if ( !d->geometry )
3209 {
3210 return QgsGeometry();
3211 }
3212
3213 QgsGeos geos( d->geometry.get() );
3214 mLastError.clear();
3215 QgsGeometry result = QgsGeometry( geos.delaunayTriangulation( tolerance, edgesOnly ) );
3216 result.mLastError = mLastError;
3217 return result;
3218}
3219
3221{
3222 if ( !d->geometry )
3223 {
3224 return QgsGeometry();
3225 }
3226
3227 QgsGeos geos( d->geometry.get() );
3228 mLastError.clear();
3229 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
3230 result.mLastError = mLastError;
3231 return result;
3232}
3233
3235{
3236 if ( !d->geometry )
3237 {
3238 return QgsGeometry();
3239 }
3240
3241 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::GeometryCollection
3242 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPolygon
3243 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::Polygon )
3244 return QgsGeometry();
3245
3246 QgsGeos geos( d->geometry.get() );
3247 mLastError.clear();
3248 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
3249 result.mLastError = mLastError;
3250 return result;
3251}
3252
3254{
3255 if ( !d->geometry )
3256 {
3258 }
3259
3260 QgsGeos geos( d->geometry.get() );
3261 mLastError.clear();
3262 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
3263
3264 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
3265
3266 if ( invalidEdges && invalidEdgesGeom )
3267 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
3268
3269 return result;
3270}
3271
3272QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
3273{
3274 if ( !d->geometry )
3275 {
3276 return QgsGeometry();
3277 }
3278
3279 QgsGeos geos( d->geometry.get() );
3280 mLastError.clear();
3281 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
3282 result.mLastError = mLastError;
3283 return result;
3284}
3285
3287{
3288 if ( !d->geometry )
3289 {
3290 return QgsGeometry();
3291 }
3292
3293 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::GeometryCollection
3294 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPolygon
3295 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::Polygon )
3296 return QgsGeometry();
3297
3298 QgsGeos geos( d->geometry.get() );
3299 mLastError.clear();
3300 const QgsGeometry result( geos.cleanCoverage( parameters, &mLastError, feedback ) );
3301 result.mLastError = mLastError;
3302 return result;
3303}
3304
3306{
3307 if ( !d->geometry )
3308 {
3309 return QgsGeometry();
3310 }
3311
3312 QgsGeos geos( d->geometry.get() );
3313 mLastError.clear();
3314 QgsGeometry result( geos.node( &mLastError ) );
3315 result.mLastError = mLastError;
3316 return result;
3317}
3318
3320{
3321 if ( !d->geometry )
3322 {
3323 return QgsGeometry();
3324 }
3325
3326 QgsGeos geos( d->geometry.get() );
3327 mLastError.clear();
3328 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
3329 result.mLastError = mLastError;
3330 return result;
3331}
3332
3333QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3334{
3335 if ( !d->geometry )
3336 {
3337 return QgsGeometry();
3338 }
3339
3340 const QgsAbstractGeometry *geom = d->geometry.get();
3341 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
3342 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
3343 {
3344 segmentizedCopy.reset( d->geometry->segmentize() );
3345 geom = segmentizedCopy.get();
3346 }
3347
3348 QgsGeos geos( geom );
3349 mLastError.clear();
3350 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters, feedback ) );
3351 if ( !result )
3352 {
3353 QgsGeometry geom;
3354 geom.mLastError = mLastError;
3355 return geom;
3356 }
3357 return QgsGeometry( std::move( result ) );
3358}
3359
3361{
3362 if ( !d->geometry )
3363 {
3364 return QgsGeometry();
3365 }
3366
3367 QgsGeometry line = *this;
3369 return QgsGeometry();
3370 else if ( type() == Qgis::GeometryType::Polygon )
3371 {
3372 line = QgsGeometry( d->geometry->boundary() );
3373 }
3374
3375 const QgsCurve *curve = nullptr;
3377 {
3378 // if multi part, iterate through parts to find target part
3379 for ( int part = 0; part < collection->numGeometries(); ++part )
3380 {
3381 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
3382 if ( !candidate )
3383 continue;
3384 const double candidateLength = candidate->length();
3385 if ( candidateLength >= distance )
3386 {
3387 curve = candidate;
3388 break;
3389 }
3390
3391 distance -= candidateLength;
3392 }
3393 }
3394 else
3395 {
3397 }
3398 if ( !curve )
3399 return QgsGeometry();
3400
3401 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
3402 if ( !result )
3403 {
3404 return QgsGeometry();
3405 }
3406 return QgsGeometry( std::move( result ) );
3407}
3408
3409double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
3410{
3411 if ( type() != Qgis::GeometryType::Line )
3412 return -1;
3413
3415 return -1;
3416
3417 QgsGeometry segmentized = *this;
3419 {
3420 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
3421 }
3422
3423 QgsGeos geos( d->geometry.get() );
3424 mLastError.clear();
3425 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
3426}
3427
3429{
3430 if ( !d->geometry || d->geometry->isEmpty() )
3431 return 0.0;
3432
3433 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
3435 return 0.0;
3436
3437 // always operate on segmentized geometries
3438 QgsGeometry segmentized = *this;
3439 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
3440 {
3441 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
3442 }
3443
3444 QgsVertexId previous;
3445 QgsVertexId next;
3446 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
3447 return 0.0;
3448
3449 if ( previous == next )
3450 {
3451 // distance coincided exactly with a vertex
3452 QgsVertexId v2 = previous;
3453 QgsVertexId v1;
3454 QgsVertexId v3;
3455 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
3456 if ( v1.isValid() && v3.isValid() )
3457 {
3458 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3459 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3460 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
3461 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3462 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
3463 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
3464 }
3465 else if ( v3.isValid() )
3466 {
3467 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
3468 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
3469 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3470 }
3471 else
3472 {
3473 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3474 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3475 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3476 }
3477 }
3478 else
3479 {
3480 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
3481 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
3482 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3483 }
3484}
3485
3486QgsGeometry QgsGeometry::intersection( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3487{
3488 if ( !d->geometry || geometry.isNull() )
3489 {
3490 return QgsGeometry();
3491 }
3492
3493 QgsGeos geos( d->geometry.get() );
3494
3495 mLastError.clear();
3496 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3497
3498 if ( !resultGeom )
3499 {
3500 QgsGeometry geom;
3501 geom.mLastError = mLastError;
3502 return geom;
3503 }
3504
3505 return QgsGeometry( std::move( resultGeom ) );
3506}
3507
3508QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3509{
3510 if ( !d->geometry || geometry.isNull() )
3511 {
3512 return QgsGeometry();
3513 }
3514
3515 QgsGeos geos( d->geometry.get() );
3516 mLastError.clear();
3517 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3518 if ( !resultGeom )
3519 {
3520 QgsGeometry geom;
3521 geom.mLastError = mLastError;
3522 return geom;
3523 }
3524 return QgsGeometry( std::move( resultGeom ) );
3525}
3526
3528{
3529 if ( !d->geometry )
3530 {
3531 return QgsGeometry();
3532 }
3533
3534 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::LineString )
3535 {
3536 // special case - a single linestring was passed
3537 return QgsGeometry( *this );
3538 }
3539
3540 QgsGeos geos( d->geometry.get() );
3541 mLastError.clear();
3542 QgsGeometry result( geos.mergeLines( &mLastError, parameters ) );
3543 result.mLastError = mLastError;
3544 return result;
3545}
3546
3547QgsGeometry QgsGeometry::difference( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3548{
3549 if ( !d->geometry || geometry.isNull() )
3550 {
3551 return QgsGeometry();
3552 }
3553
3554 QgsGeos geos( d->geometry.get() );
3555
3556 mLastError.clear();
3557 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3558 if ( !resultGeom )
3559 {
3560 QgsGeometry geom;
3561 geom.mLastError = mLastError;
3562 return geom;
3563 }
3564 return QgsGeometry( std::move( resultGeom ) );
3565}
3566
3567QgsGeometry QgsGeometry::symDifference( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3568{
3569 if ( !d->geometry || geometry.isNull() )
3570 {
3571 return QgsGeometry();
3572 }
3573
3574 QgsGeos geos( d->geometry.get() );
3575
3576 mLastError.clear();
3577 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3578 if ( !resultGeom )
3579 {
3580 QgsGeometry geom;
3581 geom.mLastError = mLastError;
3582 return geom;
3583 }
3584 return QgsGeometry( std::move( resultGeom ) );
3585}
3586
3588{
3589 QgsInternalGeometryEngine engine( *this );
3590
3591 return engine.extrude( x, y );
3592}
3593
3595
3596QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3597{
3599 return QVector< QgsPointXY >();
3600
3601 QgsInternalGeometryEngine engine( *this );
3602 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3603 mLastError = engine.lastError();
3604 return res;
3605}
3606
3607QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3608{
3610 return QVector< QgsPointXY >();
3611
3612 QgsInternalGeometryEngine engine( *this );
3613 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3614 mLastError = engine.lastError();
3615 return res;
3616}
3618
3620{
3621 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3622}
3623
3625{
3626 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3627}
3628
3629QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3630{
3631 QVector<QgsGeometry> geometryList;
3632 if ( !d->geometry )
3633 {
3634 return geometryList;
3635 }
3636
3638 if ( gc )
3639 {
3640 int numGeom = gc->numGeometries();
3641 geometryList.reserve( numGeom );
3642 for ( int i = 0; i < numGeom; ++i )
3643 {
3644 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3645 }
3646 }
3647 else //a singlepart geometry
3648 {
3649 geometryList.append( *this );
3650 }
3651
3652 return geometryList;
3653}
3654
3656{
3657 QgsPointXY point = asPoint();
3658 return point.toQPointF();
3659}
3660
3662{
3663 const QgsAbstractGeometry *part = constGet();
3664
3665 // if a geometry collection, get first part only
3667 {
3668 if ( collection->numGeometries() > 0 )
3669 part = collection->geometryN( 0 );
3670 else
3671 return QPolygonF();
3672 }
3673
3674 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3675 return curve->asQPolygonF();
3676 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3677 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3678 return QPolygonF();
3679}
3680
3681bool QgsGeometry::deleteRing( int ringNum, int partNum )
3682{
3683 if ( !d->geometry )
3684 {
3685 return false;
3686 }
3687
3688 detach();
3689 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3690 return ok;
3691}
3692
3693bool QgsGeometry::deletePart( int partNum )
3694{
3695 if ( !d->geometry )
3696 {
3697 return false;
3698 }
3699
3700 if ( !isMultipart() && partNum < 1 )
3701 {
3702 set( nullptr );
3703 return true;
3704 }
3705
3706 detach();
3707 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3708 return ok;
3709}
3710
3711Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3712{
3713 if ( !d->geometry )
3714 {
3716 }
3717
3718 Qgis::WkbType geomTypeBeforeModification = wkbType();
3719
3720 bool haveInvalidGeometry = false;
3721 bool geomModified = false;
3722
3723 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3724 if ( diffGeom )
3725 {
3726 reset( std::move( diffGeom ) );
3727 geomModified = true;
3728 }
3729
3730 if ( geomTypeBeforeModification != wkbType() )
3732 if ( haveInvalidGeometry )
3734 if ( !geomModified )
3736
3738}
3739
3771
3772QgsGeometry QgsGeometry::makeValid( Qgis::MakeValidMethod method, bool keepCollapsed, QgsFeedback *feedback ) const
3773{
3774 if ( !d->geometry )
3775 return QgsGeometry();
3776
3777 mLastError.clear();
3778 QgsGeos geos( d->geometry.get() );
3779 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError, feedback ) );
3780
3781 QgsGeometry result = QgsGeometry( std::move( g ) );
3782 result.mLastError = mLastError;
3783 return result;
3784}
3785
3790
3792{
3793 if ( !d->geometry )
3794 {
3796 }
3797
3798 if ( isMultipart() )
3799 {
3800 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3801 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3803 {
3804 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3805 }
3806 }
3807 else
3808 {
3809 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3810 {
3811 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3812 }
3813 }
3814
3816}
3817
3819{
3820 if ( !d->geometry )
3821 return QgsGeometry();
3822
3823 if ( isMultipart() )
3824 {
3825 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3826 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3827 newCollection->reserve( collection->numGeometries() );
3828 for ( int i = 0; i < collection->numGeometries(); ++i )
3829 {
3830 const QgsAbstractGeometry *g = collection->geometryN( i );
3832 {
3833 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3834 corrected->forceClockwise();
3835 newCollection->addGeometry( corrected.release() );
3836 }
3837 else
3838 {
3839 newCollection->addGeometry( g->clone() );
3840 }
3841 }
3842 return QgsGeometry( std::move( newCollection ) );
3843 }
3844 else
3845 {
3846 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3847 {
3848 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3849 corrected->forceClockwise();
3850 return QgsGeometry( std::move( corrected ) );
3851 }
3852 else
3853 {
3854 // not a curve polygon, so return unchanged
3855 return *this;
3856 }
3857 }
3858}
3859
3861{
3862 if ( !d->geometry )
3863 return QgsGeometry();
3864
3865 if ( isMultipart() )
3866 {
3867 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3868 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3869 newCollection->reserve( collection->numGeometries() );
3870 for ( int i = 0; i < collection->numGeometries(); ++i )
3871 {
3872 const QgsAbstractGeometry *g = collection->geometryN( i );
3874 {
3875 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3876 corrected->forceCounterClockwise();
3877 newCollection->addGeometry( corrected.release() );
3878 }
3879 else
3880 {
3881 newCollection->addGeometry( g->clone() );
3882 }
3883 }
3884 return QgsGeometry( std::move( newCollection ) );
3885 }
3886 else
3887 {
3888 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3889 {
3890 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3891 corrected->forceCounterClockwise();
3892 return QgsGeometry( std::move( corrected ) );
3893 }
3894 else
3895 {
3896 // not a curve polygon, so return unchanged
3897 return *this;
3898 }
3899 }
3900}
3901
3902
3903void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3904{
3905 errors.clear();
3906 if ( !d->geometry )
3907 return;
3908
3909 // avoid expensive calcs for trivial point geometries
3910 if ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) == Qgis::GeometryType::Point )
3911 {
3912 return;
3913 }
3914
3915 switch ( method )
3916 {
3918 QgsGeometryValidator::validateGeometry( *this, errors, method );
3919 return;
3920
3922 {
3923 QgsGeos geos( d->geometry.get(), 0, Qgis::GeosCreationFlags() );
3924 QString error;
3925 QgsGeometry errorLoc;
3926 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3927 {
3928 if ( errorLoc.isNull() )
3929 {
3930 errors.append( QgsGeometry::Error( error ) );
3931 }
3932 else
3933 {
3934 const QgsPointXY point = errorLoc.asPoint();
3935 errors.append( QgsGeometry::Error( error, point ) );
3936 }
3937 return;
3938 }
3939 break;
3940 }
3942 {
3943#ifdef WITH_SFCGAL
3944 QString errorMsg;
3945 QgsGeometry errorLoc;
3946 const QgsSfcgalGeometry sfcgalGeom( d->geometry.get() );
3947 if ( !QgsSfcgalEngine::isValid( sfcgalGeom.sfcgalGeometry().get(), nullptr, &errorMsg, &errorLoc ) )
3948 {
3949 if ( errorLoc.isNull() )
3950 {
3951 errors.append( QgsGeometry::Error( errorMsg ) );
3952 }
3953 else
3954 {
3955 const QgsPointXY point = errorLoc.asPoint();
3956 errors.append( QgsGeometry::Error( errorMsg, point ) );
3957 }
3958 return;
3959 }
3960#else
3961 throw QgsNotSupportedException( u"This operation requires a QGIS installation with SFCGAL support enabled. Please use a version of QGIS that includes SFCGAL."_s );
3962#endif
3963 }
3964 }
3965}
3966
3968{
3969 if ( !d->geometry )
3970 {
3971 return;
3972 }
3973
3974 detach();
3975 d->geometry->normalize();
3976}
3977
3979{
3980 if ( !d->geometry )
3981 {
3982 return false;
3983 }
3984
3985 return d->geometry->isValid( mLastError, flags );
3986}
3987
3989{
3990 if ( !d->geometry )
3991 return false;
3992
3993 QgsGeos geos( d->geometry.get() );
3994 mLastError.clear();
3995 return geos.isSimple( &mLastError );
3996}
3997
3998bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3999{
4000 if ( !d->geometry )
4001 return false;
4002
4003 QgsInternalGeometryEngine engine( *this );
4004 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
4005}
4006
4008{
4010}
4011
4013{
4014 // === WARNING ===
4015 // if tolerance/epsilon value is changed in `geos.isFuzzyEqual` or in implementation of `QgsAbstractGeometry::operator==`, documentation must be updaded accordingly and also changed in expression helper files (resources/function_help/json)
4016
4017 if ( !d->geometry || g.isNull() )
4018 {
4019 return false;
4020 }
4021
4022 // fast check - are they shared copies of the same underlying geometry?
4023 if ( d == g.d )
4024 return true;
4025
4026 // fast check - distinct geometry types?
4027 if ( type() != g.type() )
4028 return false;
4029
4030 mLastError.clear();
4031 switch ( backend )
4032 {
4034 {
4035 // avoid calling geos for trivial point case
4036 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
4037 return *d->geometry == *g.d->geometry;
4038
4039 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
4040 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
4041 return false;
4042
4043 QgsGeos geos( d->geometry.get() );
4044 // fuzzy check call, with near zero epsilon, will behave as an exact comparison
4045 return geos.isFuzzyEqual( g.d->geometry.get(), 1e-8, &mLastError );
4046 }
4047
4049 {
4050 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
4051 if ( ( !d->geometry->is3D() && d->geometry->boundingBox() != g.d->geometry->boundingBox() ) || ( d->geometry->is3D() && d->geometry->boundingBox3D() != g.d->geometry->boundingBox3D() ) )
4052 return false;
4053
4054 // slower check - actually test the geometries
4055 return *d->geometry == *g.d->geometry;
4056 }
4057 }
4059}
4060
4062{
4063 if ( !d->geometry || !g.d->geometry )
4064 {
4065 return false;
4066 }
4067
4068 // fast check - are they shared copies of the same underlying geometry?
4069 if ( d == g.d )
4070 return true;
4071
4072 // fast check - distinct geometry types?
4073 if ( type() != g.type() )
4074 return false;
4075
4076 mLastError.clear();
4077 switch ( backend )
4078 {
4080 {
4081 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
4082 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
4083 return false;
4084
4085 QgsGeos geos( d->geometry.get() );
4086 return geos.isEqual( g.d->geometry.get(), &mLastError );
4087 }
4088
4090 throw QgsNotSupportedException( u"Geometry backend '%1' is not supported by this function."_s.arg( qgsEnumValueToKey( backend ) ) );
4091 }
4093}
4094
4095bool QgsGeometry::isFuzzyEqual( const QgsGeometry &g, double epsilon, Qgis::GeometryBackend backend ) const
4096{
4097 if ( !d->geometry || g.isNull() )
4098 {
4099 return false;
4100 }
4101
4102 // fast check - are they shared copies of the same underlying geometry?
4103 if ( d == g.d )
4104 return true;
4105
4106 // fast check - distinct geometry types?
4107 if ( type() != g.type() )
4108 return false;
4109
4110 mLastError.clear();
4111 switch ( backend )
4112 {
4114 {
4115 QgsGeos geos( d->geometry.get() );
4116 return geos.isFuzzyEqual( g.d->geometry.get(), epsilon, &mLastError );
4117 }
4118
4120 {
4121 // slower check - actually test the geometries
4122 return d->geometry->fuzzyEqual( *g.d->geometry, epsilon );
4123 }
4124 }
4126}
4127
4128QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters, QgsFeedback *feedback )
4129{
4130 QgsGeos geos( nullptr );
4131
4132 QString error;
4133 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters, feedback ) );
4134 QgsGeometry result( std::move( geom ) );
4135 result.mLastError = error;
4136 return result;
4137}
4138
4139QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
4140{
4141 QVector<const QgsAbstractGeometry *> geomV2List;
4142 for ( const QgsGeometry &g : geometryList )
4143 {
4144 if ( !( g.isNull() ) )
4145 {
4146 geomV2List.append( g.constGet() );
4147 }
4148 }
4149
4150 QString error;
4151 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
4152 result.mLastError = error;
4153 return result;
4154}
4155
4157{
4158 if ( !d->geometry || !requiresConversionToStraightSegments() )
4159 {
4160 return;
4161 }
4162
4163 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
4164 reset( std::move( straightGeom ) );
4165}
4166
4168{
4169 if ( !d->geometry )
4170 {
4171 return false;
4172 }
4173
4174 return d->geometry->hasCurvedSegments();
4175}
4176
4178{
4179 if ( !d->geometry )
4180 {
4182 }
4183
4184 detach();
4185 d->geometry->transform( ct, direction, transformZ );
4187}
4188
4189Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
4190{
4191 if ( !d->geometry )
4192 {
4194 }
4195
4196 detach();
4197 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
4199}
4200
4202{
4203 if ( d->geometry )
4204 {
4205 detach();
4206 d->geometry->transform( mtp.transform() );
4207 }
4208}
4209
4211{
4212 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
4213 {
4214 return QgsGeometry();
4215 }
4216
4217 QgsGeos geos( d->geometry.get() );
4218 mLastError.clear();
4219 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError, feedback );
4220 if ( !resultGeom )
4221 {
4222 QgsGeometry result;
4223 result.mLastError = mLastError;
4224 return result;
4225 }
4226 return QgsGeometry( std::move( resultGeom ) );
4227}
4228
4229void QgsGeometry::draw( QPainter &p ) const
4230{
4231 if ( d->geometry )
4232 {
4233 d->geometry->draw( p );
4234 }
4235}
4236
4237static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
4238{
4239 if ( vertexIndex < 0 )
4240 return false; // clearly something wrong
4241
4243 {
4244 partIndex = 0;
4245 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
4246 {
4247 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
4248
4249 // count total number of vertices in the part
4250 int numPoints = 0;
4251 for ( int k = 0; k < part->ringCount(); ++k )
4252 numPoints += part->vertexCount( 0, k );
4253
4254 if ( vertexIndex < numPoints )
4255 {
4256 int nothing;
4257 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
4258 }
4259 vertexIndex -= numPoints;
4260 partIndex++;
4261 }
4262 }
4263 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
4264 {
4265 // PolyhedralSurface: patches are the parts
4266 partIndex = 0;
4267 for ( int i = 0; i < polySurface->numPatches(); ++i )
4268 {
4269 const QgsPolygon *patch = polySurface->patchN( i );
4270 // count total number of vertices in the patch
4271 int numPoints = 0;
4272 for ( int k = 0; k < patch->ringCount(); ++k )
4273 numPoints += patch->vertexCount( 0, k );
4274
4275 if ( vertexIndex < numPoints )
4276 {
4277 int nothing;
4278 return vertexIndexInfo( patch, vertexIndex, nothing, ringIndex, vertex );
4279 }
4280 vertexIndex -= numPoints;
4281 partIndex++;
4282 }
4283 }
4284 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4285 {
4286 const QgsCurve *ring = curvePolygon->exteriorRing();
4287 if ( vertexIndex < ring->numPoints() )
4288 {
4289 partIndex = 0;
4290 ringIndex = 0;
4291 vertex = vertexIndex;
4292 return true;
4293 }
4294 vertexIndex -= ring->numPoints();
4295 ringIndex = 1;
4296 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
4297 {
4298 const QgsCurve *ring = curvePolygon->interiorRing( i );
4299 if ( vertexIndex < ring->numPoints() )
4300 {
4301 partIndex = 0;
4302 vertex = vertexIndex;
4303 return true;
4304 }
4305 vertexIndex -= ring->numPoints();
4306 ringIndex += 1;
4307 }
4308 }
4309 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4310 {
4311 if ( vertexIndex < curve->numPoints() )
4312 {
4313 partIndex = 0;
4314 ringIndex = 0;
4315 vertex = vertexIndex;
4316 return true;
4317 }
4318 }
4319 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
4320 {
4321 if ( vertexIndex == 0 )
4322 {
4323 partIndex = 0;
4324 ringIndex = 0;
4325 vertex = 0;
4326 return true;
4327 }
4328 }
4329
4330 return false;
4331}
4332
4334{
4335 if ( !d->geometry )
4336 {
4337 return false;
4338 }
4339
4340 id.type = Qgis::VertexType::Segment;
4341
4342 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
4343 if ( !res )
4344 return false;
4345
4346 // now let's find out if it is a straight or circular segment
4347 const QgsAbstractGeometry *g = d->geometry.get();
4349 {
4350 g = geomCollection->geometryN( id.part );
4351 }
4352 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
4353 {
4354 g = polySurface->patchN( id.part );
4355 }
4356
4357 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4358 {
4359 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
4360 }
4361
4362 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4363 {
4364 QgsPoint p;
4365 res = curve->pointAt( id.vertex, p, id.type );
4366 if ( !res )
4367 return false;
4368 }
4369
4370 return true;
4371}
4372
4374{
4375 if ( !d->geometry )
4376 {
4377 return -1;
4378 }
4379 return d->geometry->vertexNumberFromVertexId( id );
4380}
4381
4383{
4384 return mLastError;
4385}
4386
4387void QgsGeometry::filterVertices( const std::function<bool( const QgsPoint & )> &filter )
4388{
4389 if ( !d->geometry )
4390 return;
4391
4392 detach();
4393
4394 d->geometry->filterVertices( filter );
4395}
4396
4397void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
4398{
4399 if ( !d->geometry )
4400 return;
4401
4402 detach();
4403
4404 d->geometry->transformVertices( transform );
4405}
4406
4407void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
4408{
4409 output.clear();
4410 for ( const QgsPointXY &p : input )
4411 {
4412 output.append( QgsPoint( p ) );
4413 }
4414}
4415
4416void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
4417{
4418 output.clear();
4419 for ( const QgsPoint &p : input )
4420 {
4421 output.append( QgsPointXY( p.x(), p.y() ) );
4422 }
4423}
4424
4425void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
4426{
4427 output.clear();
4428
4429 auto convertRing = []( const QgsCurve *ring ) -> QgsPolylineXY {
4430 QgsPolylineXY res;
4432 std::unique_ptr< QgsLineString > segmentizedLine;
4433 const QgsLineString *line = nullptr;
4434 if ( doSegmentation )
4435 {
4436 segmentizedLine.reset( ring->curveToLine() );
4437 line = segmentizedLine.get();
4438 }
4439 else
4440 {
4442 if ( !line )
4443 {
4444 return res;
4445 }
4446 }
4447
4448 int nVertices = line->numPoints();
4449 res.resize( nVertices );
4450 QgsPointXY *data = res.data();
4451 const double *xData = line->xData();
4452 const double *yData = line->yData();
4453 for ( int i = 0; i < nVertices; ++i )
4454 {
4455 data->setX( *xData++ );
4456 data->setY( *yData++ );
4457 data++;
4458 }
4459 return res;
4460 };
4461
4462 if ( const QgsCurve *exterior = input.exteriorRing() )
4463 {
4464 output.push_back( convertRing( exterior ) );
4465 }
4466
4467 const int interiorRingCount = input.numInteriorRings();
4468 output.reserve( output.size() + interiorRingCount );
4469 for ( int n = 0; n < interiorRingCount; ++n )
4470 {
4471 output.push_back( convertRing( input.interiorRing( n ) ) );
4472 }
4473}
4474
4476{
4477 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
4478}
4479
4480QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
4481{
4482 std::unique_ptr< QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
4483
4484 if ( polygon.isClosed() )
4485 {
4486 auto poly = std::make_unique< QgsPolygon >();
4487 poly->setExteriorRing( ring.release() );
4488 return QgsGeometry( std::move( poly ) );
4489 }
4490 else
4491 {
4492 return QgsGeometry( std::move( ring ) );
4493 }
4494}
4495
4497{
4499 QgsPolygonXY result;
4500 result << createPolylineFromQPolygonF( polygon );
4501 return result;
4503}
4504
4506{
4507 QgsPolylineXY result;
4508 result.reserve( polygon.count() );
4509 for ( const QPointF &p : polygon )
4510 {
4511 result.append( QgsPointXY( p ) );
4512 }
4513 return result;
4514}
4515
4516bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
4517{
4518 if ( p1.count() != p2.count() )
4519 return false;
4520
4521 for ( int i = 0; i < p1.count(); ++i )
4522 {
4523 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
4524 return false;
4525 }
4526 return true;
4527}
4528
4529bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
4530{
4531 if ( p1.count() != p2.count() )
4532 return false;
4533
4534 for ( int i = 0; i < p1.count(); ++i )
4535 {
4536 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4537 return false;
4538 }
4539 return true;
4540}
4541
4542
4543bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
4544{
4545 if ( p1.count() != p2.count() )
4546 return false;
4547
4548 for ( int i = 0; i < p1.count(); ++i )
4549 {
4550 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4551 return false;
4552 }
4553 return true;
4554}
4555
4556QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4557{
4558 if ( !d->geometry || d->geometry->isEmpty() )
4559 return QgsGeometry();
4560
4561 QgsGeometry geom = *this;
4563 geom = QgsGeometry( d->geometry->segmentize() );
4564
4565 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
4566 {
4569 //can't smooth a point based geometry
4570 return geom;
4571
4573 {
4575 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
4576 }
4577
4579 {
4581
4582 auto resultMultiline = std::make_unique< QgsMultiLineString>();
4583 resultMultiline->reserve( inputMultiLine->numGeometries() );
4584 for ( int i = 0; i < inputMultiLine->numGeometries(); ++i )
4585 {
4586 resultMultiline->addGeometry( smoothLine( *( inputMultiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4587 }
4588 return QgsGeometry( std::move( resultMultiline ) );
4589 }
4590
4592 {
4594 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
4595 }
4596
4598 {
4600
4601 auto resultMultiPoly = std::make_unique< QgsMultiPolygon >();
4602 resultMultiPoly->reserve( inputMultiPoly->numGeometries() );
4603 for ( int i = 0; i < inputMultiPoly->numGeometries(); ++i )
4604 {
4605 resultMultiPoly->addGeometry( smoothPolygon( *( inputMultiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4606 }
4607 return QgsGeometry( std::move( resultMultiPoly ) );
4608 }
4609
4611 default:
4612 return QgsGeometry( *this );
4613 }
4614}
4615
4616std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing )
4617{
4618 auto result = std::make_unique< QgsLineString >( line );
4619 QgsPointSequence outputLine;
4620 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
4621 {
4622 outputLine.resize( 0 );
4623 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
4624 bool skipFirst = false;
4625 bool skipLast = false;
4626 if ( isRing )
4627 {
4628 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
4629 QgsPoint p2 = result->pointN( 0 );
4630 QgsPoint p3 = result->pointN( 1 );
4631 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4632 angle = std::fabs( M_PI - angle );
4633 skipFirst = angle > maxAngleRads;
4634 }
4635 for ( int i = 0; i < result->numPoints() - 1; i++ )
4636 {
4637 QgsPoint p1 = result->pointN( i );
4638 QgsPoint p2 = result->pointN( i + 1 );
4639
4640 double angle = M_PI;
4641 if ( i == 0 && isRing )
4642 {
4643 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
4644 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4645 }
4646 else if ( i < result->numPoints() - 2 )
4647 {
4648 QgsPoint p3 = result->pointN( i + 2 );
4649 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4650 }
4651 else if ( i == result->numPoints() - 2 && isRing )
4652 {
4653 QgsPoint p3 = result->pointN( 1 );
4654 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4655 }
4656
4657 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
4658
4659 // don't apply distance threshold to first or last segment
4660 if ( i == 0 || i >= result->numPoints() - 2 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
4661 {
4662 if ( !isRing )
4663 {
4664 if ( !skipFirst )
4665 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
4666 if ( !skipLast )
4667 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
4668 else
4669 outputLine << p2;
4670 }
4671 else
4672 {
4673 // ring
4674 if ( !skipFirst )
4675 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
4676 else if ( i == 0 )
4677 outputLine << p1;
4678 if ( !skipLast )
4679 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
4680 else
4681 outputLine << p2;
4682 }
4683 }
4684 skipFirst = skipLast;
4685 }
4686
4687 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4688 outputLine << outputLine.at( 0 );
4689
4690 result->setPoints( outputLine );
4691 }
4692 return result;
4693}
4694
4695std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4696{
4697 double maxAngleRads = maxAngle * M_PI / 180.0;
4698 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4699 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4700}
4701
4702std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4703{
4704 double maxAngleRads = maxAngle * M_PI / 180.0;
4705 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4706 auto resultPoly = std::make_unique< QgsPolygon >();
4707
4708 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4709
4710 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4711 {
4712 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4713 }
4714 return resultPoly;
4715}
4716
4717QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4718{
4719 switch ( type() )
4720 {
4722 {
4723 bool srcIsMultipart = isMultipart();
4724
4725 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4726 {
4727 // return a copy of the same geom
4728 return QgsGeometry( *this );
4729 }
4730 if ( destMultipart )
4731 {
4732 // layer is multipart => make a multipoint with a single point
4733 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4734 }
4735 else
4736 {
4737 // destination is singlepart => make a single part if possible
4738 QgsMultiPointXY multiPoint = asMultiPoint();
4739 if ( multiPoint.count() == 1 )
4740 {
4741 return fromPointXY( multiPoint[0] );
4742 }
4743 }
4744 return QgsGeometry();
4745 }
4746
4748 {
4749 // only possible if destination is multipart
4750 if ( !destMultipart )
4751 return QgsGeometry();
4752
4753 // input geometry is multipart
4754 if ( isMultipart() )
4755 {
4756 const QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4757 QgsMultiPointXY multiPoint;
4758 for ( const QgsPolylineXY &l : inputMultiLine )
4759 for ( const QgsPointXY &p : l )
4760 multiPoint << p;
4761 return fromMultiPointXY( multiPoint );
4762 }
4763 // input geometry is not multipart: copy directly the line into a multipoint
4764 else
4765 {
4766 QgsPolylineXY line = asPolyline();
4767 if ( !line.isEmpty() )
4768 return fromMultiPointXY( line );
4769 }
4770 return QgsGeometry();
4771 }
4772
4774 {
4775 // can only transform if destination is multipoint
4776 if ( !destMultipart )
4777 return QgsGeometry();
4778
4779 // input geometry is multipart: make a multipoint from multipolygon
4780 if ( isMultipart() )
4781 {
4782 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4783 QgsMultiPointXY multiPoint;
4784 for ( const QgsPolygonXY &poly : multiPolygon )
4785 for ( const QgsPolylineXY &line : poly )
4786 for ( const QgsPointXY &pt : line )
4787 multiPoint << pt;
4788 return fromMultiPointXY( multiPoint );
4789 }
4790 // input geometry is not multipart: make a multipoint from polygon
4791 else
4792 {
4793 const QgsPolygonXY polygon = asPolygon();
4794 QgsMultiPointXY multiPoint;
4795 for ( const QgsPolylineXY &line : polygon )
4796 for ( const QgsPointXY &pt : line )
4797 multiPoint << pt;
4798 return fromMultiPointXY( multiPoint );
4799 }
4800 }
4801
4802 default:
4803 return QgsGeometry();
4804 }
4805}
4806
4807QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4808{
4809 switch ( type() )
4810 {
4812 {
4813 if ( !isMultipart() )
4814 return QgsGeometry();
4815
4816 QgsMultiPointXY multiPoint = asMultiPoint();
4817 if ( multiPoint.count() < 2 )
4818 return QgsGeometry();
4819
4820 if ( destMultipart )
4821 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4822 else
4823 return fromPolylineXY( multiPoint );
4824 }
4825
4827 {
4828 bool srcIsMultipart = isMultipart();
4829
4830 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4831 {
4832 // return a copy of the same geom
4833 return QgsGeometry( *this );
4834 }
4835 if ( destMultipart )
4836 {
4837 // destination is multipart => makes a multipoint with a single line
4838 QgsPolylineXY line = asPolyline();
4839 if ( !line.isEmpty() )
4840 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4841 }
4842 else
4843 {
4844 // destination is singlepart => make a single part if possible
4845 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4846 if ( inputMultiLine.count() == 1 )
4847 return fromPolylineXY( inputMultiLine[0] );
4848 }
4849 return QgsGeometry();
4850 }
4851
4853 {
4854 // input geometry is multipolygon
4855 if ( isMultipart() )
4856 {
4857 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4858 QgsMultiPolylineXY inputMultiLine;
4859 for ( const QgsPolygonXY &poly : multiPolygon )
4860 for ( const QgsPolylineXY &line : poly )
4861 inputMultiLine << line;
4862
4863 if ( destMultipart )
4864 {
4865 // destination is multipart
4866 return fromMultiPolylineXY( inputMultiLine );
4867 }
4868 else if ( inputMultiLine.count() == 1 )
4869 {
4870 // destination is singlepart => make a single part if possible
4871 return fromPolylineXY( inputMultiLine[0] );
4872 }
4873 }
4874 // input geometry is single polygon
4875 else
4876 {
4877 QgsPolygonXY polygon = asPolygon();
4878 // if polygon has rings
4879 if ( polygon.count() > 1 )
4880 {
4881 // cannot fit a polygon with rings in a single line layer
4882 // TODO: would it be better to remove rings?
4883 if ( destMultipart )
4884 {
4885 const QgsPolygonXY polygon = asPolygon();
4886 QgsMultiPolylineXY inputMultiLine;
4887 inputMultiLine.reserve( polygon.count() );
4888 for ( const QgsPolylineXY &line : polygon )
4889 inputMultiLine << line;
4890 return fromMultiPolylineXY( inputMultiLine );
4891 }
4892 }
4893 // no rings
4894 else if ( polygon.count() == 1 )
4895 {
4896 if ( destMultipart )
4897 {
4898 return fromMultiPolylineXY( polygon );
4899 }
4900 else
4901 {
4902 return fromPolylineXY( polygon[0] );
4903 }
4904 }
4905 }
4906 return QgsGeometry();
4907 }
4908
4909 default:
4910 return QgsGeometry();
4911 }
4912}
4913
4914QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4915{
4916 switch ( type() )
4917 {
4919 {
4920 if ( !isMultipart() )
4921 return QgsGeometry();
4922
4923 QgsMultiPointXY multiPoint = asMultiPoint();
4924 if ( multiPoint.count() < 3 )
4925 return QgsGeometry();
4926
4927 if ( multiPoint.last() != multiPoint.first() )
4928 multiPoint << multiPoint.first();
4929
4930 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4931 if ( destMultipart )
4932 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4933 else
4934 return fromPolygonXY( polygon );
4935 }
4936
4938 {
4939 // input geometry is multiline
4940 if ( isMultipart() )
4941 {
4942 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4943 QgsMultiPolygonXY multiPolygon;
4944 for ( QgsMultiPolylineXY::iterator multiLineIt = inputMultiLine.begin(); multiLineIt != inputMultiLine.end(); ++multiLineIt )
4945 {
4946 // do not create polygon for a 1 segment line
4947 if ( ( *multiLineIt ).count() < 3 )
4948 return QgsGeometry();
4949 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4950 return QgsGeometry();
4951
4952 // add closing node
4953 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4954 *multiLineIt << ( *multiLineIt ).first();
4955 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4956 }
4957 // check that polygons were inserted
4958 if ( !multiPolygon.isEmpty() )
4959 {
4960 if ( destMultipart )
4961 {
4962 return fromMultiPolygonXY( multiPolygon );
4963 }
4964 else if ( multiPolygon.count() == 1 )
4965 {
4966 // destination is singlepart => make a single part if possible
4967 return fromPolygonXY( multiPolygon[0] );
4968 }
4969 }
4970 }
4971 // input geometry is single line
4972 else
4973 {
4974 QgsPolylineXY line = asPolyline();
4975
4976 // do not create polygon for a 1 segment line
4977 if ( line.count() < 3 )
4978 return QgsGeometry();
4979 if ( line.count() == 3 && line.first() == line.last() )
4980 return QgsGeometry();
4981
4982 // add closing node
4983 if ( line.first() != line.last() )
4984 line << line.first();
4985
4986 // destination is multipart
4987 if ( destMultipart )
4988 {
4989 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4990 }
4991 else
4992 {
4993 return fromPolygonXY( QgsPolygonXY() << line );
4994 }
4995 }
4996 return QgsGeometry();
4997 }
4998
5000 {
5001 bool srcIsMultipart = isMultipart();
5002
5003 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
5004 {
5005 // return a copy of the same geom
5006 return QgsGeometry( *this );
5007 }
5008 if ( destMultipart )
5009 {
5010 // destination is multipart => makes a multipoint with a single polygon
5011 QgsPolygonXY polygon = asPolygon();
5012 if ( !polygon.isEmpty() )
5013 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
5014 }
5015 else
5016 {
5017 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
5018 if ( multiPolygon.count() == 1 )
5019 {
5020 // destination is singlepart => make a single part if possible
5021 return fromPolygonXY( multiPolygon[0] );
5022 }
5023 }
5024 return QgsGeometry();
5025 }
5026
5027 default:
5028 return QgsGeometry();
5029 }
5030}
5031
5033{
5034 return new QgsGeos( geometry, precision, flags );
5035}
5036
5037QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
5038{
5039 out << geometry.asWkb();
5040 return out;
5041}
5042
5043QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
5044{
5045 QByteArray byteArray;
5046 in >> byteArray;
5047 if ( byteArray.isEmpty() )
5048 {
5049 geometry.set( nullptr );
5050 return in;
5051 }
5052
5053 geometry.fromWkb( byteArray );
5054 return in;
5055}
5056
5057
5059{
5060 return mMessage;
5061}
5062
5064{
5065 return mLocation;
5066}
5067
5069{
5070 return mHasLocation;
5071}
5072
5073QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int vertexIndex, double distance1, double distance2, int segments ) const
5074{
5075 QgsDebugMsgLevel( u"%1 starts: %2"_s.arg( qgsEnumValueToKey( op ) ).arg( asWkt( 2 ) ), 3 );
5076 if ( isNull() )
5077 {
5078 mLastError = u"Operation '%1' needs non-null geometry."_s.arg( qgsEnumValueToKey( op ) );
5079 return QgsGeometry();
5080 }
5081
5082 QgsCurve *curve = nullptr;
5083
5084 int modifiedPart = -1;
5085 int modifiedRing = -1;
5086 QgsVertexId vertexId;
5087 if ( !vertexIdFromVertexNr( vertexIndex, vertexId ) )
5088 {
5089 mLastError = u"Invalid vertex index"_s;
5090 return QgsGeometry();
5091 }
5092 int resolvedVertexIndex = vertexId.vertex;
5093 QgsMultiCurve *inputMultiCurve = nullptr;
5094 QgsMultiSurface *inputMultiSurface = nullptr;
5096
5097 if ( geomType == Qgis::GeometryType::Line )
5098 {
5099 if ( isMultipart() )
5100 {
5101 modifiedPart = vertexId.part;
5102
5103 inputMultiCurve = qgsgeometry_cast<QgsMultiCurve *>( d->geometry.get() );
5104 curve = dynamic_cast<QgsCurve *>( inputMultiCurve->curveN( modifiedPart ) );
5105 }
5106 else
5107 {
5108 curve = dynamic_cast<QgsCurve *>( d->geometry.get() );
5109 }
5110 }
5111 else if ( geomType == Qgis::GeometryType::Polygon )
5112 {
5113 QgsCurvePolygon *poly = nullptr;
5114 if ( isMultipart() )
5115 {
5116 modifiedPart = vertexId.part;
5117 // get part, get ring
5118 inputMultiSurface = qgsgeometry_cast<QgsMultiSurface *>( d->geometry.get() );
5119 poly = dynamic_cast<QgsCurvePolygon *>( inputMultiSurface->surfaceN( modifiedPart ) );
5120 }
5121 else
5122 {
5123 poly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
5124 }
5125 if ( !poly )
5126 {
5127 mLastError = u"Could not get polygon geometry."_s;
5128 return QgsGeometry();
5129 }
5130
5131 // if has rings
5132 modifiedRing = vertexId.ring;
5133 if ( modifiedRing == 0 )
5134 curve = qgsgeometry_cast<QgsCurve *>( poly->exteriorRing() );
5135 else
5136 curve = qgsgeometry_cast<QgsCurve *>( poly->interiorRing( modifiedRing - 1 ) );
5137 }
5138 else
5139 curve = nullptr;
5140
5141 if ( !curve )
5142 {
5143 mLastError = u"Operation '%1' needs curve geometry."_s.arg( qgsEnumValueToKey( op ) );
5144 return QgsGeometry();
5145 }
5146
5147 std::unique_ptr<QgsAbstractGeometry> result;
5148 try
5149 {
5151 result = QgsGeometryUtils::chamferVertex( curve, resolvedVertexIndex, distance1, distance2 );
5152 else
5153 result = QgsGeometryUtils::filletVertex( curve, resolvedVertexIndex, distance1, segments );
5154 }
5155 catch ( QgsInvalidArgumentException &e )
5156 {
5157 mLastError = u"%1 Requested vertex: %2 was resolved as: [part: %3, ring: %4, vertex: %5]"_s //
5158 .arg( e.what() )
5159 .arg( vertexIndex )
5160 .arg( modifiedPart )
5161 .arg( modifiedRing )
5162 .arg( resolvedVertexIndex );
5163 return QgsGeometry();
5164 }
5165
5166 if ( !result )
5167 {
5168 mLastError = u"Operation '%1' generates a null geometry."_s.arg( qgsEnumValueToKey( op ) );
5169 return QgsGeometry();
5170 }
5171
5172 if ( result->isEmpty() )
5173 return QgsGeometry( std::move( result ) );
5174
5175 // insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon
5176 auto updatePolygon = []( const QgsCurvePolygon *inputPoly, QgsAbstractGeometry *result, int modifiedRing ) -> std::unique_ptr<QgsCurvePolygon> {
5177 auto newPoly = std::make_unique<QgsCurvePolygon>();
5178 if ( qgsgeometry_cast<const QgsPolygon *>( inputPoly ) )
5179 newPoly = std::make_unique<QgsPolygon>();
5180
5181 for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex )
5182 {
5183 if ( ringIndex == modifiedRing )
5184 {
5185 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
5186 {
5187 if ( ringIndex == 0 && resPartIte == result->parts_begin() )
5188 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
5189 else
5190 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
5191 }
5192 }
5193 else
5194 {
5195 if ( ringIndex == 0 )
5196 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->exteriorRing()->clone() ) );
5197 else
5198 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->interiorRing( ringIndex - 1 )->clone() ) );
5199 }
5200 }
5201 return newPoly;
5202 };
5203
5204 std::unique_ptr<QgsAbstractGeometry> finalGeom;
5205 if ( geomType == Qgis::GeometryType::Line )
5206 {
5207 if ( modifiedPart >= 0 )
5208 {
5209 auto newMultiCurve = std::make_unique<QgsMultiCurve>();
5210 if ( qgsgeometry_cast<const QgsMultiLineString *>( inputMultiCurve ) )
5211 newMultiCurve = std::make_unique<QgsMultiLineString>();
5212
5213 int partIndex = 0;
5214 for ( QgsMultiCurve::part_iterator partIte = inputMultiCurve->parts_begin(); partIte != inputMultiCurve->parts_end(); ++partIte )
5215 {
5216 if ( partIndex == modifiedPart )
5217 {
5218 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
5219 {
5220 newMultiCurve->addGeometry( ( *resPartIte )->clone() );
5221 }
5222 }
5223 else
5224 {
5225 newMultiCurve->addGeometry( ( *partIte )->clone() );
5226 }
5227 partIndex++;
5228 }
5229 finalGeom = std::move( newMultiCurve );
5230 }
5231 else
5232 {
5233 // resultGeom is already the correct result!
5234 finalGeom = std::move( result );
5235 }
5236 }
5237 else
5238 {
5239 // geomType == Qgis::GeometryType::Polygon
5240 if ( modifiedPart >= 0 )
5241 {
5242 auto newMultiSurface = std::make_unique<QgsMultiSurface>();
5243 if ( qgsgeometry_cast<const QgsMultiPolygon *>( inputMultiSurface ) )
5244 newMultiSurface = std::make_unique<QgsMultiPolygon>();
5245
5246 int partIndex = 0;
5247 for ( QgsAbstractGeometry::part_iterator partIte = inputMultiSurface->parts_begin(); partIte != inputMultiSurface->parts_end(); ++partIte )
5248 {
5249 if ( partIndex == modifiedPart )
5250 {
5251 std::unique_ptr<QgsCurvePolygon> newCurvePoly = updatePolygon( qgsgeometry_cast<const QgsCurvePolygon *>( *partIte ), result.get(), modifiedRing );
5252 newMultiSurface->addGeometry( newCurvePoly.release() );
5253 }
5254 else
5255 {
5256 newMultiSurface->addGeometry( ( *partIte )->clone() );
5257 }
5258 partIndex++;
5259 }
5260 finalGeom = std::move( newMultiSurface );
5261 }
5262 else
5263 {
5264 std::unique_ptr<QgsCurvePolygon> newCurvePoly = updatePolygon( qgsgeometry_cast<const QgsCurvePolygon *>( d->geometry.get() ), result.get(), modifiedRing );
5265 finalGeom = std::move( newCurvePoly );
5266 }
5267 }
5268
5269 QgsGeometry finalResult( std::move( finalGeom ) );
5270
5271 QgsDebugMsgLevel( u"Final result Wkt: %1"_s.arg( finalResult.asWkt( 2 ) ), 3 );
5272
5273 return finalResult;
5274}
5275
5276
5277QgsGeometry QgsGeometry::chamfer( int vertexIndex, double distance1, double distance2 ) const
5278{
5279 return doChamferFillet( ChamferFilletOperationType::Chamfer, vertexIndex, distance1, distance2, 0 );
5280}
5281
5282QgsGeometry QgsGeometry::fillet( int vertexIndex, double radius, int segments ) const
5283{
5284 return doChamferFillet( ChamferFilletOperationType::Fillet, vertexIndex, radius, 0.0, segments );
5285}
5286
5287QgsGeometry QgsGeometry::chamfer( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2 )
5288{
5289 std::unique_ptr<QgsLineString> result( QgsGeometryUtils::createChamferGeometry( segment1Start, segment1End, segment2Start, segment2End, distance1, distance2 ) );
5290
5291 if ( !result )
5292 {
5293 return QgsGeometry();
5294 }
5295
5296 return QgsGeometry( std::move( result ) );
5297}
5298
5299QgsGeometry QgsGeometry::fillet( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments )
5300{
5301 std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::createFilletGeometry( segment1Start, segment1End, segment2Start, segment2End, radius, segments ) );
5302
5303 if ( !result )
5304 {
5305 return QgsGeometry();
5306 }
5307
5308 return QgsGeometry( std::move( result ) );
5309}
5310
5311QVector< QgsGeometry > QgsGeometry::collectSplitFeatures( const QVector< QgsGeometry> newGeoms ) const
5312{
5313 QVector< QgsGeometry> collectedNewGeoms;
5314 QVector< QgsGeometry> unchangedParts;
5315
5316 for ( QgsGeometry newGeom : newGeoms )
5317 {
5318 bool inOriginalCollection = false;
5319 QVector< QgsGeometry > originalGeoms = this->asGeometryCollection();
5320 for ( QgsGeometry &originalPart : originalGeoms )
5321 {
5322 if ( newGeom.isExactlyEqual( originalPart ) )
5323 {
5324 inOriginalCollection = true;
5325 break;
5326 }
5327 }
5328 if ( inOriginalCollection )
5329 {
5330 unchangedParts.append( newGeom );
5331 }
5332 else
5333 {
5334 // Parts that were split are added to a single multi-part geometry.
5335 newGeom.convertToMultiType();
5336 collectedNewGeoms.append( newGeom );
5337 }
5338 }
5339
5340 // All unchanged parts are added to the same multi-geometry.
5341 if ( !unchangedParts.empty() )
5342 {
5343 collectedNewGeoms.prepend( QgsGeometry::collectGeometry( unchangedParts ) );
5344 }
5345 return collectedNewGeoms;
5346}
5347
5348QVector< QgsGeometry > QgsGeometry::collectSplitParts( const QVector< QgsGeometry> newGeoms ) const
5349{
5350 QVector< QgsGeometry> collectedNewGeoms;
5351 for ( const QgsGeometry &newGeom : newGeoms )
5352 {
5353 collectedNewGeoms.append( QgsGeometry::collectGeometry( QVector< QgsGeometry>() << newGeom ) );
5354 }
5355 return collectedNewGeoms;
5356}
5357
5358QVector< QgsGeometry > QgsGeometry::collectSplitGeometries( const QVector< QgsGeometry> newGeomsTemp, const bool splitFeature )
5359{
5360 QVector< QgsGeometry> newGeoms;
5361 if ( !newGeomsTemp.isEmpty() )
5362 {
5364 {
5365 newGeoms = splitFeature ? collectSplitFeatures( newGeomsTemp ) : collectSplitParts( newGeomsTemp );
5366 }
5367 else
5368 {
5369 newGeoms = newGeomsTemp;
5370 }
5371
5372 if ( splitFeature && !newGeoms.isEmpty() )
5373 {
5374 *this = newGeoms.takeAt( 0 );
5375 }
5376 }
5377 return newGeoms;
5378}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2319
@ GEOS
Use GEOS implementation.
Definition qgis.h:2321
@ QGIS
Use internal implementation.
Definition qgis.h:2320
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2242
BufferSide
Side of line to buffer.
Definition qgis.h:2268
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3529
AngularDirection
Angular directions.
Definition qgis.h:3670
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3673
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2212
@ AddPartSelectedGeometryNotFound
The selected geometry cannot be found.
Definition qgis.h:2222
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Definition qgis.h:2216
@ Success
Operation succeeded.
Definition qgis.h:2213
@ SelectionIsEmpty
No features were selected.
Definition qgis.h:2217
@ GeometryTypeHasChanged
Operation has changed geometry type.
Definition qgis.h:2231
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
Definition qgis.h:2228
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint).
Definition qgis.h:2227
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition qgis.h:2223
@ AddRingNotClosed
The input ring is not closed.
Definition qgis.h:2225
@ SelectionIsGreaterThanOne
More than one features were selected.
Definition qgis.h:2218
@ SplitCannotSplitPoint
Cannot split points.
Definition qgis.h:2230
@ GeometryEngineError
Geometry engine misses a method implemented or an error occurred in the geometry engine.
Definition qgis.h:2219
@ NothingHappened
Nothing happened, without any error.
Definition qgis.h:2214
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition qgis.h:2215
@ LayerNotEditable
Cannot edit layer.
Definition qgis.h:2220
@ AddRingNotValid
The input ring is not valid.
Definition qgis.h:2226
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2246
@ Segment
The actual start or end point of a segment.
Definition qgis.h:3304
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:2255
@ QgisInternal
Use internal QgsGeometryValidator method.
Definition qgis.h:2256
@ Sfcgal
Use SFCGAL validation methods. Only available for QGIS builds with SFCGAL support enabled.
Definition qgis.h:2258
@ Geos
Use GEOS validation methods.
Definition qgis.h:2257
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
Definition qgis.h:2342
GeoJsonProfile
GeoJson export Profile according to OGC Features and Geometries JSON - Part 1: Core https://docs....
Definition qgis.h:5134
@ Rfc7946
GeoJson profile compliant with RFC7946 standard "http://www.opengis.net/def/profile/OGC/0/rfc7946".
Definition qgis.h:5136
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:399
@ Point
Points.
Definition qgis.h:400
@ Line
Lines.
Definition qgis.h:401
@ Polygon
Polygons.
Definition qgis.h:402
@ Unknown
Unknown types.
Definition qgis.h:403
@ Null
No geometry.
Definition qgis.h:404
JoinStyle
Join styles for buffers.
Definition qgis.h:2293
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2280
CoverageValidityResult
Coverage validity results.
Definition qgis.h:2351
@ Error
An exception occurred while determining validity.
Definition qgis.h:2354
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3514
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2364
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:314
@ CompoundCurve
CompoundCurve.
Definition qgis.h:325
@ Point
Point.
Definition qgis.h:316
@ LineString
LineString.
Definition qgis.h:317
@ TIN
TIN.
Definition qgis.h:330
@ MultiPoint
MultiPoint.
Definition qgis.h:320
@ Polygon
Polygon.
Definition qgis.h:318
@ MultiPolygon
MultiPolygon.
Definition qgis.h:322
@ Triangle
Triangle.
Definition qgis.h:319
@ NoGeometry
No geometry.
Definition qgis.h:332
@ MultiLineString
MultiLineString.
Definition qgis.h:321
@ Unknown
Unknown.
Definition qgis.h:315
@ CircularString
CircularString.
Definition qgis.h:324
@ GeometryCollection
GeometryCollection.
Definition qgis.h:323
@ MultiCurve
MultiCurve.
Definition qgis.h:327
@ CurvePolygon
CurvePolygon.
Definition qgis.h:326
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:329
@ MultiSurface
MultiSurface.
Definition qgis.h:328
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2882
The part_iterator class provides an STL-style iterator for const references to geometry parts.
The part_iterator class provides an STL-style iterator for geometry parts.
The vertex_iterator class provides an 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.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual int vertexNumberFromVertexId(QgsVertexId id) const =0
Returns the vertex number corresponding to a vertex id.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual QgsAbstractGeometry * toCurveType() const =0
Returns the geometry converted to the more generic curve type.
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.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool deleteVertex(QgsVertexId position)
Deletes a vertex within the geometry.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual bool isEmpty() const
Returns true if the geometry is empty.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
virtual int dimension() const =0
Returns the inherent dimension of the geometry.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:240
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:205
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:268
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:212
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:388
bool is2d() const
Returns true if the box can be considered a 2-dimensional box, i.e.
Definition qgsbox3d.cpp:137
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:233
Circle geometry type.
Definition qgscircle.h:46
static QgsCircle from2Points(const QgsPoint &pt1, const QgsPoint &pt2)
Constructs a circle by 2 points on the circle.
Definition qgscircle.cpp:39
double radius() const
Returns the radius of the circle.
Definition qgscircle.h:303
std::unique_ptr< QgsCircularString > toCircularString(bool oriented=false) const
Returns a circular string from the circle.
bool contains(const QgsPoint &point, double epsilon=1E-8) const
Returns true if the circle contains the point.
static QgsCircle minimalCircleFrom3Points(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon=1E-8)
Constructs the smallest circle from 3 points.
Circular string geometry type.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
Compound curve geometry type.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
A const WKB pointer.
Definition qgswkbptr.h:211
Handles coordinate transforms between two coordinate systems.
Encapsulates parameters for a coverage cleaning operation.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
virtual QgsPolygon * toPolygon(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a new polygon geometry corresponding to a segmentized approximation of the curve.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership).
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
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 void points(QgsPointSequence &pt) const =0
Returns a list of points within the curve.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
QgsPoint center() const
Returns the center point.
Definition qgsellipse.h:122
QString what() const
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
virtual bool insertGeometry(QgsAbstractGeometry *g, int index)
Inserts a geometry before a specified index and takes ownership.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Java-style iterator for const traversal of parts of a geometry.
static Qgis::GeometryOperationResult addRing(QgsAbstractGeometry *geometry, std::unique_ptr< QgsCurve > ring)
Add an interior ring to a geometry.
static std::unique_ptr< QgsAbstractGeometry > avoidIntersections(const QgsAbstractGeometry &geom, const QList< QgsVectorLayer * > &avoidIntersectionsLayers, bool &haveInvalidGeometry, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Alters a geometry so that it avoids intersections with features from all open vector layers.
static bool deletePart(QgsAbstractGeometry *geom, int partNum)
Deletes a part from a geometry.
static bool deleteRing(QgsAbstractGeometry *geom, int ringNum, int partNum=0)
Deletes a ring from a geometry.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
A geometry engine is a low-level representation of a QgsAbstractGeometry object, optimised for use wi...
EngineOperationResult
Success or failure of a geometry operation.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
@ InvalidInput
The input is not valid.
@ NodedGeometryError
Error occurred while creating a noded geometry.
@ EngineError
Error occurred in the geometry engine.
@ SplitCannotSplitPoint
Points cannot be split.
@ Success
Operation succeeded.
@ MethodNotImplemented
Method not implemented in geometry engine.
static std::unique_ptr< QgsMultiPolygon > fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Construct geometry from a multipolygon.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(Qgis::WkbType type)
Returns a new geometry collection matching a specified WKB type.
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< QgsMultiLineString > fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Construct geometry from a multipolyline.
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.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(Qgis::WkbType t)
Returns empty geometry from wkb type.
Encapsulates parameters under which a geometry operation is performed.
Java-style iterator for traversal of parts of a geometry.
static double angleBetweenThreePoints(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the angle between the lines AB and BC, where AB and BC described by points a,...
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
static std::unique_ptr< QgsLineString > createChamferGeometry(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2)
Creates a complete chamfer geometry connecting two segments.
static QgsPointXY interpolatePointOnLine(double x1, double y1, double x2, double y2, double fraction)
Interpolates the position of a point a fraction of the way along the line from (x1,...
static std::unique_ptr< QgsAbstractGeometry > createFilletGeometry(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments)
Creates a complete fillet geometry connecting two segments.
static QgsPoint interpolatePointOnSegment(double x, double y, const QgsPoint &segmentStart, const QgsPoint &segmentEnd)
Interpolates a point on a segment with proper Z and M value interpolation.
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 QgsPoint closestVertex(const QgsAbstractGeometry &geom, const QgsPoint &pt, QgsVertexId &id)
Returns the closest vertex to a geometry for a specified point.
static Q_DECL_DEPRECATED double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
static std::unique_ptr< QgsAbstractGeometry > chamferVertex(const QgsCurve *curve, int vertexIndex, double distance1, double distance2)
Applies chamfer to a vertex in a curve geometry.
static std::unique_ptr< QgsAbstractGeometry > filletVertex(const QgsCurve *curve, int vertexIndex, double radius, int segments)
Applies fillet to a vertex in a curve geometry.
static void validateGeometry(const QgsGeometry &geometry, QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal)
Validate geometry and produce a list of geometry errors.
A geometry error.
bool hasWhere() const
true if the location available from
QgsPointXY where() const
The coordinates at which the error is located and should be visualized.
QString what() const
A human readable error message containing details about the error.
A geometry is the spatial representation of a feature.
Q_DECL_DEPRECATED QgsGeometry makeDifference(const QgsGeometry &other, QgsFeedback *feedback=nullptr) const
Returns the geometry formed by modifying this geometry such that it does not intersect the other geom...
QPolygonF asQPolygonF() const
Returns contents of the geometry as a QPolygonF.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=Qgis::DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
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...
double area3D() const
Returns the 3-dimensional surface area of the geometry.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points shared by this geometry and other.
void adjacentVertices(int atVertex, int &beforeVertex, int &afterVertex) const
Returns the indexes of the vertices before and after the given vertex index.
QgsMultiPolygonXY asMultiPolygon() const
Returns the contents of the geometry as a multi-polygon.
QgsGeometry concaveHullOfPolygons(double lengthRatio, bool allowHoles=false, bool isTight=false, QgsFeedback *feedback=nullptr) const
Constructs a concave hull of a set of polygons, respecting the polygons as constraints.
QgsGeometry chamfer(int vertexIndex, double distance1, double distance2=-1.0) const
Creates a chamfer (angled corner) at the specified vertex.
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...
ChamferFilletOperationType
Privatly used in chamfer/fillet functions.
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false, QgsFeedback *feedback=nullptr) const
Returns a possibly concave polygon that contains all the points in the geometry.
static QgsGeometry fromQPointF(QPointF point)
Construct geometry from a QPointF.
static QgsGeometry collectTinPatches(const QVector< QgsGeometry > &geometries)
Collects all patches from a list of TIN or Triangle geometries into a single TIN geometry.
static QgsGeometry polygonize(const QVector< QgsGeometry > &geometries)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
bool addTopologicalPoint(const QgsPoint &point, double snappingTolerance=1e-8, double segmentSearchEpsilon=1e-12)
Adds a vertex to the segment which intersect point but don't already have a vertex there.
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...
Q_INVOKABLE 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.
Q_INVOKABLE bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
Q_DECL_DEPRECATED int makeDifferenceInPlace(const QgsGeometry &other, QgsFeedback *feedback=nullptr)
Changes this geometry such that it does not intersect the other geometry.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform)
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
bool isExactlyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometry minimumWidth() const
Returns a linestring geometry which represents the minimum diameter of the geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
Qgis::CoverageValidityResult validateCoverage(double gapWidth, QgsGeometry *invalidEdges=nullptr) const
Analyze a coverage (represented as a collection of polygonal geometry with exactly matching edge geom...
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 simplifyCoverageVW(double tolerance, bool preserveBoundary) const
Operates on a coverage (represented as a list of polygonal geometry with exactly matching edge geomet...
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry fillet(int vertexIndex, double radius, int segments=8) const
Creates a fillet (rounded corner) at the specified vertex.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
double frechetDistance(const QgsGeometry &geom) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry convertToType(Qgis::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
QgsGeometry clipped(const QgsRectangle &rectangle, QgsFeedback *feedback=nullptr)
Clips the geometry using the specified rectangle.
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
void normalize()
Reorganizes the geometry into a normalized form (or "canonical" form).
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Returns the length of the QByteArray returned by asWkb().
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
Q_INVOKABLE bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static Q_DECL_DEPRECATED QgsPolylineXY createPolylineFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolylineXY from a QPolygonF.
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
virtual json asJsonObject(int precision=17, Qgis::GeoJsonProfile profile=Qgis::GeoJsonProfile::Legacy) const
Exports the geometry to a json object with the give precision and following the specified GeoJSON pro...
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
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.
QgsBox3D boundingBox3D() const
Returns the 3D bounding box of the geometry.
friend class QgsInternalGeometryEngine
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
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.
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...
Q_DECL_DEPRECATED bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
bool insertVertex(double x, double y, int beforeVertex)
Insert a new vertex before the given vertex index, ring and item (first number is index 0) If the req...
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
QgsGeometry subdivide(int maxNodes=256, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Subdivides the geometry.
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.
Qgis::GeometryType type
QgsGeometry extendLine(double startDistance, double endDistance, double startDeflection=0, double endDeflection=0) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
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.
Qgis::GeometryOperationResult avoidIntersectionsV2(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.
Q_INVOKABLE bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QPointF asQPointF() const
Returns contents of the geometry as a QPointF if wkbType is WKBPoint, otherwise returns a null QPoint...
QString asGeoJson(int precision=17, Qgis::GeoJsonProfile profile=Qgis::GeoJsonProfile::Legacy) const
Export the geometry to a GeoJSON string, with the given precision and following the specified GeoJSON...
void convertToStraightSegment(double tolerance=M_PI/180., QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle)
Converts the geometry to straight line segments, if it is a curved geometry type.
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
Q_INVOKABLE bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
QgsGeometry & operator=(QgsGeometry const &rhs)
Creates a shallow copy 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...
Qgis::AngularDirection polygonOrientation() const
Returns the orientation of the polygon.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
bool deleteVertices(const QSet< int > &atVertices)
Deletes vertices at the given positions (first number is index 0).
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QgsFeedback *feedback=nullptr) const
Attempts to make an invalid geometry valid without losing vertices.
QgsGeometry largestEmptyCircle(double tolerance, const QgsGeometry &boundary=QgsGeometry()) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult addPart(const QVector< QgsPointXY > &points, Qgis::GeometryType geomType=Qgis::GeometryType::Unknown)
Adds a new part to a the geometry.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr)
Compute the unary union on a list of geometries.
QgsGeometryPartIterator parts()
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry convertToCurves(double distanceTolerance=1e-8, double angleTolerance=1e-8) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
QgsGeometry voronoiDiagram(const QgsGeometry &extent=QgsGeometry(), double tolerance=0.0, bool edgesOnly=false) const
Creates a Voronoi diagram for the nodes contained within the geometry.
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry minimumClearanceLine() const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
virtual ~QgsGeometry()
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygonXY.
double sqrDistToVertexAt(QgsPointXY &point, int atVertex) const
Returns the squared Cartesian distance between the given point to the given vertex index (vertex at t...
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments, QgsFeedback *feedback=nullptr) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
QVector< QgsGeometry > coerceToType(Qgis::WkbType type, double defaultZ=0, double defaultM=0, bool avoidDuplicates=true) const
Attempts to coerce this geometry into the specified destination type.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsGeometry node() const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false)
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
bool convertGeometryCollectionToSubclass(Qgis::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
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.
bool isFuzzyEqual(const QgsGeometry &geometry, double epsilon=1e-4, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry within the tolerance epsilon using the specified backend.
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 RFC7946 string.
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
double frechetDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
QgsGeometry unionCoverage() const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
bool convertToCurvedMultiType()
Converts a geometry into a multitype geometry of curve kind (when there is a corresponding curve type...
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 ...
bool isTopologicallyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::GEOS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometryConstPartIterator constParts() const
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry simplify(double tolerance, QgsFeedback *feedback=nullptr) 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 addPartV2(const QVector< QgsPointXY > &points, Qgis::WkbType wkbType=Qgis::WkbType::Unknown)
Adds a new part to a the geometry.
double minimumClearance() const
Computes the minimum clearance of a geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
Qgis::GeometryOperationResult reshapeGeometry(const QgsLineString &reshapeLineString)
Replaces a part of this geometry with another line.
double closestVertexWithContext(const QgsPointXY &point, int &atVertex) const
Searches for the closest vertex in this geometry to the given point.
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false) const
Returns the Delaunay triangulation for the vertices of the geometry.
void draw(QPainter &p) const
Draws the geometry onto a QPainter.
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...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
QgsGeometry cleanCoverage(const QgsCoverageCleanParameters &parameters, QgsFeedback *feedback=nullptr) const
Operates on a coverage (represented as a list of polygonal geometry), to fix cases where the geometry...
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.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
bool moveVertex(double x, double y, int atVertex)
Moves the vertex at the given position number and item (first number is index 0) to the given coordin...
QgsGeometry constrainedDelaunayTriangulation() const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
Q_DECL_DEPRECATED bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Q_INVOKABLE bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
static QgsGeometry fromBox3D(const QgsBox3D &box)
Creates a new geometry from a QgsBox3D object Returns a 2D polygon geometry if the box is purely 2d,...
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
static QgsGeometry createWedgeBufferFromAngles(const QgsPoint &center, double startAngle, double endAngle, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
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.
static QgsGeometry fromPoint(const QgsPoint &point)
Creates a new geometry from a QgsPoint object.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
Q_INVOKABLE bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
Q_DECL_DEPRECATED 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, and exception handling.
Definition qgsgeos.h:192
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:624
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Buffers the geometry.
Definition qgsgeos.cpp:2428
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:833
double frechetDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
Definition qgsgeos.cpp:881
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:809
double frechetDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
Definition qgsgeos.cpp:857
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
Definition qgsgeos.cpp:3894
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...
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.
Custom exception class when argument are invalid.
Line string geometry type, with support for z-dimension and m-values.
static std::unique_ptr< QgsLineString > fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Perform transforms between map coordinates and device coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
QgsCurve * curveN(int index)
Returns the curve with the specified index.
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
QgsPoint * pointN(int index)
Returns the point with the specified index.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
QgsSurface * surfaceN(int index)
Returns the surface with the specified index.
Custom exception class which is raised when an operation is not supported.
Represents a 2D point.
Definition qgspointxy.h:62
void setY(double y)
Sets the y value of the point.
Definition qgspointxy.h:132
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
void setX(double x)
Sets the x value of the point.
Definition qgspointxy.h:122
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:168
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:138
double x
Definition qgspoint.h:56
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:743
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
Polyhedral surface geometry type.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
bool dropMValue() override
Drops any measure values which exist in the geometry.
int numPoints() const override
Returns the number of points in the curve.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
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.
Triangle geometry type.
Definition qgstriangle.h:33
Triangulated surface geometry type.
Represents a vector layer which manages a vector based dataset.
Java-style iterator for traversal of vertices of a geometry.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition qgswkbtypes.h:53
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Q_INVOKABLE bool isNurbsType(Qgis::WkbType type)
Returns true if the WKB type is a NURBS curve type.
static Q_INVOKABLE bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static Qgis::WkbType curveType(Qgis::WkbType type)
Returns the curve type for a WKB type.
Contains geos related utilities and functions.
Definition qgsgeos.h:129
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:8379
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:8017
#define BUILTIN_UNREACHABLE
Definition qgis.h:8415
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8378
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7743
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
Q_GLOBAL_STATIC_WITH_ARGS(PalPropertyList, palHiddenProperties,({ static_cast< int >(QgsPalLayerSettings::Property::PositionX), static_cast< int >(QgsPalLayerSettings::Property::PositionY), static_cast< int >(QgsPalLayerSettings::Property::Show), static_cast< int >(QgsPalLayerSettings::Property::LabelRotation), static_cast< int >(QgsPalLayerSettings::Property::Family), static_cast< int >(QgsPalLayerSettings::Property::FontStyle), static_cast< int >(QgsPalLayerSettings::Property::Size), static_cast< int >(QgsPalLayerSettings::Property::Bold), static_cast< int >(QgsPalLayerSettings::Property::Italic), static_cast< int >(QgsPalLayerSettings::Property::Underline), static_cast< int >(QgsPalLayerSettings::Property::Color), static_cast< int >(QgsPalLayerSettings::Property::Strikeout), static_cast< int >(QgsPalLayerSettings::Property::MultiLineAlignment), static_cast< int >(QgsPalLayerSettings::Property::BufferSize), static_cast< int >(QgsPalLayerSettings::Property::BufferDraw), static_cast< int >(QgsPalLayerSettings::Property::BufferColor), static_cast< int >(QgsPalLayerSettings::Property::LabelDistance), static_cast< int >(QgsPalLayerSettings::Property::Hali), static_cast< int >(QgsPalLayerSettings::Property::Vali), static_cast< int >(QgsPalLayerSettings::Property::ScaleVisibility), static_cast< int >(QgsPalLayerSettings::Property::MinScale), static_cast< int >(QgsPalLayerSettings::Property::MaxScale), static_cast< int >(QgsPalLayerSettings::Property::AlwaysShow), static_cast< int >(QgsPalLayerSettings::Property::CalloutDraw), static_cast< int >(QgsPalLayerSettings::Property::LabelAllParts) })) Q_GLOBAL_STATIC_WITH_ARGS(SymbolPropertyList
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
std::unique_ptr< QgsLineString > smoothCurve(const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing)
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:92
CORE_EXPORT QDataStream & operator>>(QDataStream &in, QgsGeometry &geometry)
Reads a geometry from stream in into geometry. QGIS version compatibility is not guaranteed.
CORE_EXPORT QDataStream & operator<<(QDataStream &out, const QgsGeometry &geometry)
Writes the geometry to stream out. QGIS version compatibility is not guaranteed.
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:98
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.
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition qgsgeometry.h:72
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71
std::unique_ptr< QgsAbstractGeometry > geometry
QgsGeometryPrivate(std::unique_ptr< QgsAbstractGeometry > geometry)
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:35
int vertex
Vertex number.
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:51
int part
Part number.
Definition qgsvertexid.h:96
int ring
Ring number.
Definition qgsvertexid.h:99