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