QGIS API Documentation 4.3.0-Master (153eea77ea7)
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
1209static void removeDuplicateAdjacentPointsAt( QgsAbstractGeometry *geom, const QgsPointSequence &points )
1210{
1211 // this is a workaround for removing duplicated points introduced by GEOS when splitting 3d geometries
1212 // on topologically added points. It makes no sense to be called for 2d geometries, so it shouldn't.
1213 if ( !geom->is3D() )
1214 {
1215 Q_ASSERT( false );
1216 return;
1217 }
1218
1219 for ( const QgsPoint &pt : points )
1220 {
1221 QgsVertexId vertexId, prevVertexId, nextVertexId;
1222 const QgsPoint closestPt = QgsGeometryUtils::closestVertex( *geom, pt, vertexId );
1223 geom->adjacentVertices( vertexId, prevVertexId, nextVertexId );
1224 const double dist = QgsGeometryUtils::sqrDistance2D( pt, closestPt );
1225 if ( dist == 0 )
1226 {
1227 // make sure the geometry is snapped (z) to the topo point
1228 ( void ) geom->moveVertex( vertexId, pt );
1229 // remove adjacent vertices which are duplicates on the XY plane
1230 if ( const QgsPoint v = geom->vertexAt( prevVertexId ); v.x() == pt.x() && v.y() == pt.y() )
1231 ( void ) geom->deleteVertex( prevVertexId );
1232 else if ( const QgsPoint v = geom->vertexAt( nextVertexId ); v.x() == pt.x() && v.y() == pt.y() )
1233 ( void ) geom->deleteVertex( nextVertexId );
1234 }
1235 }
1236}
1237
1239 const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature
1240)
1241{
1242 QgsPointSequence split, topology;
1243 convertPointList( splitLine, split );
1244 convertPointList( topologyTestPoints, topology );
1245 Qgis::GeometryOperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
1246 convertPointList( topology, topologyTestPoints );
1247 return result;
1248}
1250 const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest
1251)
1252{
1253 if ( !d->geometry )
1254 {
1256 }
1257
1258 // We're trying adding the split line's vertices to the geometry so that
1259 // snap to segment always produces a valid split (see https://github.com/qgis/QGIS/issues/29270)
1260 QgsGeometry tmpGeom( *this );
1261 QgsPointSequence addedTopologicalPoints;
1262 for ( const QgsPoint &v : splitLine )
1263 {
1264 if ( tmpGeom.addTopologicalPoint( v ) )
1265 {
1266 // POLYGON Z geometries need special handling to cater for GEOS limitations.
1267 // Splitting of polygons relies on GEOS extracting lines, unioning with the split line and then polygonizing.
1268 // The problem is that during the union operation GEOS will interpolate new Z values where the split line intersects
1269 // the polygon rings, even though we have added topological points with the correct Z values at that location.
1270 // This results in duplicate vertices and/or wrong Z values on the split geometry.
1271 // Our solution for that is:
1272 // 1. Collect the topo points that were added (these have the desired interpolated Z values).
1273 // 2. Visit the split geometries at the XY location of those topo points and make sure they still have the desired Z value.
1274 // 3. Remove the adjacent vertex to the topo point if it has same XY coordinates. Any vertex with XY coordinates same as a
1275 // topo point was introduced by GEOS and is not wanted.
1276 if ( tmpGeom.constGet()->is3D() && tmpGeom.constGet()->dimension() == 2 )
1277 {
1278 QgsVertexId vId;
1279 const QgsPoint topoPoint = QgsGeometryUtils::closestVertex( *tmpGeom.constGet(), v, vId );
1280 addedTopologicalPoints.append( topoPoint );
1281 }
1282 }
1283 }
1284
1285 QVector<QgsGeometry > newGeoms;
1286 QgsLineString splitLineString( splitLine );
1287 splitLineString.dropZValue();
1288 splitLineString.dropMValue();
1289
1290 QgsGeos geos( tmpGeom.get() );
1291 mLastError.clear();
1292 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
1293
1294 if ( result == QgsGeometryEngine::Success )
1295 {
1296 if ( !addedTopologicalPoints.isEmpty() )
1297 {
1298 for ( int i = 0; i < newGeoms.size(); ++i )
1299 {
1300 QgsAbstractGeometry *geom = newGeoms[i].get();
1301 removeDuplicateAdjacentPointsAt( geom, addedTopologicalPoints );
1302 }
1303 }
1304 if ( splitFeature )
1305 *this = newGeoms.takeAt( 0 );
1306 newGeometries = newGeoms;
1307 }
1308
1309 switch ( result )
1310 {
1325 //default: do not implement default to handle properly all cases
1326 }
1327
1328 // this should never be reached
1329 Q_ASSERT( false );
1331}
1332
1334 const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature
1335)
1336{
1337 std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
1338 QgsPointSequence points;
1339 segmentizedLine->points( points );
1340 Qgis::GeometryOperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
1341
1343 {
1344 if ( preserveCircular )
1345 {
1346 for ( int i = 0; i < newGeometries.count(); ++i )
1347 newGeometries[i] = newGeometries[i].convertToCurves();
1348 *this = convertToCurves();
1349 }
1350 }
1351
1352 return result;
1353}
1354
1356{
1357 if ( !d->geometry )
1358 {
1360 }
1361
1362 // We're trying adding the reshape line's vertices to the geometry so that
1363 // snap to segment always produces a valid reshape
1364 QgsPointSequence reshapePoints;
1365 reshapeLineString.points( reshapePoints );
1366 QgsGeometry tmpGeom( *this );
1367 QgsPointSequence addedTopologicalPoints;
1368 for ( const QgsPoint &v : std::as_const( reshapePoints ) )
1369 {
1370 if ( tmpGeom.addTopologicalPoint( v ) )
1371 {
1372 // When reshaping 3D lines or polygons we want to make sure that any topological points added
1373 // are preserved in the final geometry. GEOS will interpolate between geometry and reshapeLineString
1374 // and may create duplicate vertices with different Z values. We will manually snap Z to those topo
1375 // points later and remove any duplicated vertices.
1376 if ( tmpGeom.constGet()->is3D() )
1377 {
1378 QgsVertexId vId;
1379 const QgsPoint topoPoint = QgsGeometryUtils::closestVertex( *tmpGeom.constGet(), v, vId );
1380 addedTopologicalPoints.append( topoPoint );
1381 }
1382 }
1383 }
1384
1385 QgsGeos geos( tmpGeom.get() );
1387 mLastError.clear();
1388 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1389 if ( errorCode == QgsGeometryEngine::Success && geom )
1390 {
1391 if ( !addedTopologicalPoints.isEmpty() )
1392 {
1393 removeDuplicateAdjacentPointsAt( geom.get(), addedTopologicalPoints );
1394 }
1395 reset( std::move( geom ) );
1397 }
1398
1399 switch ( errorCode )
1400 {
1411 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1415 }
1416
1417 // should not be reached
1419}
1420
1422{
1423 if ( !d->geometry || !other.d->geometry )
1424 {
1425 return 0;
1426 }
1427
1428 QgsGeos geos( d->geometry.get() );
1429
1430 mLastError.clear();
1431 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError, QgsGeometryParameters(), feedback ) );
1432 if ( !diffGeom )
1433 {
1434 return 1;
1435 }
1436
1437 reset( std::move( diffGeom ) );
1438 return 0;
1439}
1440
1442{
1443 if ( !d->geometry || other.isNull() )
1444 {
1445 return QgsGeometry();
1446 }
1447
1448 QgsGeos geos( d->geometry.get() );
1449
1450 mLastError.clear();
1451 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError, QgsGeometryParameters(), feedback ) );
1452 if ( !diffGeom )
1453 {
1454 QgsGeometry result;
1455 result.mLastError = mLastError;
1456 return result;
1457 }
1458
1459 return QgsGeometry( diffGeom.release() );
1460}
1461
1463{
1464 if ( d->geometry )
1465 {
1466 return d->geometry->boundingBox();
1467 }
1468 return QgsRectangle();
1469}
1470
1472{
1473 if ( d->geometry )
1474 {
1475 return d->geometry->boundingBox3D();
1476 }
1477 return QgsBox3D();
1478}
1479
1480
1481QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1482{
1483 mLastError.clear();
1484
1485 if ( isNull() )
1486 return QgsGeometry();
1487
1488 if ( type() == Qgis::GeometryType::Point && d->geometry->partCount() == 1 )
1489 {
1490 area = 0;
1491 angle = 0;
1492 width = 0;
1493 height = 0;
1494 return QgsGeometry::fromRect( d->geometry->boundingBox() );
1495 }
1496
1497 QgsInternalGeometryEngine engine( *this );
1498 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1499 if ( res.isNull() )
1500 mLastError = engine.lastError();
1501 return res;
1502}
1503
1505{
1506 double area, angle, width, height;
1507 return orientedMinimumBoundingBox( area, angle, width, height );
1508}
1509
1510static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1511{
1512 auto l_boundary = boundary.length();
1513 QgsCircle circ_mec;
1514 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1515 {
1516 switch ( l_boundary )
1517 {
1518 case 0:
1519 circ_mec = QgsCircle();
1520 break;
1521 case 1:
1522 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1523 boundary.pop_back();
1524 break;
1525 case 2:
1526 {
1527 QgsPointXY p1 = boundary.last();
1528 boundary.pop_back();
1529 QgsPointXY p2 = boundary.last();
1530 boundary.pop_back();
1531 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1532 }
1533 break;
1534 default:
1535 QgsPoint p1( boundary.at( 0 ) );
1536 QgsPoint p2( boundary.at( 1 ) );
1537 QgsPoint p3( boundary.at( 2 ) );
1538 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1539 break;
1540 }
1541 return circ_mec;
1542 }
1543 else
1544 {
1545 QgsPointXY pxy = points.last();
1546 points.pop_back();
1547 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1548 QgsPoint p( pxy );
1549 if ( !circ_mec.contains( p ) )
1550 {
1551 boundary.append( pxy );
1552 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1553 }
1554 }
1555 return circ_mec;
1556}
1557
1558QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1559{
1560 center = QgsPointXY();
1561 radius = 0;
1562
1563 if ( isEmpty() )
1564 {
1565 return QgsGeometry();
1566 }
1567
1568 /* optimization */
1569 QgsGeometry hull = convexHull();
1570 if ( hull.isNull() )
1571 return QgsGeometry();
1572
1573 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1575
1576 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1577 center = QgsPointXY( circ.center() );
1578 radius = circ.radius();
1579 QgsGeometry geom;
1580 geom.set( circ.toPolygon( segments ) );
1581 return geom;
1582}
1583
1585{
1586 QgsPointXY center;
1587 double radius;
1588 return minimalEnclosingCircle( center, radius, segments );
1589}
1590
1591QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1592{
1593 QgsInternalGeometryEngine engine( *this );
1594
1595 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1596}
1597
1598QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1599{
1600 QgsInternalGeometryEngine engine( *this );
1601 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1602}
1603
1604QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1605{
1606 QgsInternalGeometryEngine engine( *this );
1607 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1608}
1609
1610QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1611{
1612 QgsInternalGeometryEngine engine( *this );
1613 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1614}
1615
1616QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1617{
1618 QgsInternalGeometryEngine engine( *this );
1619 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1620}
1621
1622QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1623{
1624 QgsInternalGeometryEngine engine( *this );
1625 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1626}
1627
1628QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1629{
1630 QgsInternalGeometryEngine engine( *this );
1631 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1632}
1633
1635 const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset
1636) const
1637{
1638 QgsInternalGeometryEngine engine( *this );
1639 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1640}
1641
1642QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1643{
1644 if ( !d->geometry )
1645 {
1646 return QgsGeometry();
1647 }
1648 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1649}
1650
1651bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1652{
1653 if ( !d->geometry )
1654 return false;
1655
1656 detach();
1657 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1658}
1659
1661{
1662 // fast case, check bounding boxes
1663 if ( !boundingBoxIntersects( r ) )
1664 return false;
1665
1666 const Qgis::WkbType flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1667 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1668 if ( flatType == Qgis::WkbType::Point )
1669 {
1670 return true;
1671 }
1672
1673#if ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12 )
1674 // Workaround for issue issue GH #51492
1675 // in case of multi polygon, intersection with an empty rect fails
1676 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1677 {
1678 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1679 return contains( QgsGeometry::fromPointXY( center ) );
1680 }
1681#endif
1682
1683 QgsGeometry g = fromRect( r );
1684 return intersects( g );
1685}
1686
1687bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1688{
1689 if ( !d->geometry || geometry.isNull() )
1690 {
1691 return false;
1692 }
1693
1694 QgsGeos geos( d->geometry.get() );
1695 mLastError.clear();
1696 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1697}
1698
1700{
1701 if ( !d->geometry )
1702 {
1703 return false;
1704 }
1705
1706 return d->geometry->boundingBoxIntersects( rectangle );
1707}
1708
1710{
1711 if ( !d->geometry || geometry.isNull() )
1712 {
1713 return false;
1714 }
1715
1716 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1717}
1718
1719bool QgsGeometry::contains( const QgsPointXY *p ) const
1720{
1721 if ( !d->geometry || !p )
1722 {
1723 return false;
1724 }
1725
1726 QgsGeos geos( d->geometry.get() );
1727 mLastError.clear();
1728 return geos.contains( p->x(), p->y(), &mLastError );
1729}
1730
1731bool QgsGeometry::contains( double x, double y ) const
1732{
1733 if ( !d->geometry )
1734 {
1735 return false;
1736 }
1737
1738 QgsGeos geos( d->geometry.get() );
1739 mLastError.clear();
1740 return geos.contains( x, y, &mLastError );
1741}
1742
1743bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1744{
1745 if ( !d->geometry || geometry.isNull() )
1746 {
1747 return false;
1748 }
1749
1750 QgsGeos geos( d->geometry.get() );
1751 mLastError.clear();
1752 return geos.contains( geometry.d->geometry.get(), &mLastError );
1753}
1754
1755bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1756{
1757 if ( !d->geometry || geometry.isNull() )
1758 {
1759 return false;
1760 }
1761
1762 QgsGeos geos( d->geometry.get() );
1763 mLastError.clear();
1764 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1765}
1766
1767bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1768{
1769 return isExactlyEqual( geometry );
1770}
1771
1772bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1773{
1774 if ( !d->geometry || geometry.isNull() )
1775 {
1776 return false;
1777 }
1778
1779 QgsGeos geos( d->geometry.get() );
1780 mLastError.clear();
1781 return geos.touches( geometry.d->geometry.get(), &mLastError );
1782}
1783
1784bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1785{
1786 if ( !d->geometry || geometry.isNull() )
1787 {
1788 return false;
1789 }
1790
1791 QgsGeos geos( d->geometry.get() );
1792 mLastError.clear();
1793 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1794}
1795
1796bool QgsGeometry::within( const QgsGeometry &geometry ) const
1797{
1798 if ( !d->geometry || geometry.isNull() )
1799 {
1800 return false;
1801 }
1802
1803 QgsGeos geos( d->geometry.get() );
1804 mLastError.clear();
1805 return geos.within( geometry.d->geometry.get(), &mLastError );
1806}
1807
1808bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1809{
1810 if ( !d->geometry || geometry.isNull() )
1811 {
1812 return false;
1813 }
1814
1815 QgsGeos geos( d->geometry.get() );
1816 mLastError.clear();
1817 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1818}
1819
1820QString QgsGeometry::asWkt( int precision ) const
1821{
1822 if ( !d->geometry )
1823 {
1824 return QString();
1825 }
1826 return d->geometry->asWkt( precision );
1827}
1828
1829QString QgsGeometry::asJson( int precision ) const
1830{
1831 return asGeoJson( precision, Qgis::GeoJsonProfile::Rfc7946 );
1832}
1833
1834QString QgsGeometry::asGeoJson( int precision, Qgis::GeoJsonProfile profile ) const
1835{
1836 return QString::fromStdString( asJsonObject( precision, profile ).dump() );
1837}
1838
1839json QgsGeometry::asJsonObject( int precision, Qgis::GeoJsonProfile profile ) const
1840{
1841 if ( !d->geometry )
1842 {
1843 return nullptr;
1844 }
1845 return d->geometry->asJsonObject( precision, profile );
1846}
1847
1848QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM, bool avoidDuplicates ) const
1849{
1850 mLastError.clear();
1851 QVector< QgsGeometry > res;
1852 if ( isNull() )
1853 return res;
1854
1855 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1856 {
1857 res << *this;
1858 return res;
1859 }
1860
1862 {
1863 return res;
1864 }
1865
1866 QgsGeometry newGeom = *this;
1867
1868 // Curved -> straight
1870 {
1871 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1872 }
1873
1874 // Handle NurbsCurve: if target is curved but NOT NurbsCurve, and source contains NurbsCurve,
1875 // we need to segmentize the NURBS parts first
1877 {
1878 // Check if geometry contains NurbsCurve that needs conversion
1879 bool hasNurbs = false;
1880 if ( QgsWkbTypes::isNurbsType( newGeom.wkbType() ) )
1881 {
1882 hasNurbs = true;
1883 }
1884 else if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( newGeom.constGet() ) )
1885 {
1886 for ( int i = 0; i < collection->numGeometries(); ++i )
1887 {
1888 if ( QgsWkbTypes::isNurbsType( collection->geometryN( i )->wkbType() ) )
1889 {
1890 hasNurbs = true;
1891 break;
1892 }
1893 }
1894 }
1895 else if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( newGeom.constGet() ) )
1896 {
1897 if ( cp->exteriorRing() && QgsWkbTypes::isNurbsType( cp->exteriorRing()->wkbType() ) )
1898 hasNurbs = true;
1899 for ( int i = 0; !hasNurbs && i < cp->numInteriorRings(); ++i )
1900 {
1901 if ( QgsWkbTypes::isNurbsType( cp->interiorRing( i )->wkbType() ) )
1902 hasNurbs = true;
1903 }
1904 }
1906 {
1907 for ( int i = 0; i < cc->nCurves(); ++i )
1908 {
1909 if ( QgsWkbTypes::isNurbsType( cc->curveAt( i )->wkbType() ) )
1910 {
1911 hasNurbs = true;
1912 break;
1913 }
1914 }
1915 }
1916
1917 if ( hasNurbs )
1918 {
1919 // Segmentize to remove NURBS, then we'll convert back to curve type below
1920 newGeom = QgsGeometry( newGeom.constGet()->segmentize() );
1921 }
1922 }
1923
1924 // polygon -> line
1926 {
1927 // boundary gives us a (multi)line string of exterior + interior rings
1928 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1929 }
1930 // line -> polygon
1932 {
1933 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1934 const QgsGeometry source = newGeom;
1935 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1936 {
1937 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1938 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1939 {
1941 {
1942 auto cp = std::make_unique< QgsCurvePolygon >();
1943 cp->setExteriorRing( curve );
1944 ( void ) exterior.release();
1945 gc->addGeometry( cp.release() );
1946 }
1947 else
1948 {
1949 auto p = std::make_unique< QgsPolygon >();
1950 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1951 ( void ) exterior.release();
1952 gc->addGeometry( p.release() );
1953 }
1954 }
1955 }
1956 newGeom = QgsGeometry( std::move( gc ) );
1957 }
1958
1959 // line/polygon -> points
1961 {
1962 // lines/polygons to a point layer, extract all vertices
1963 auto mp = std::make_unique< QgsMultiPoint >();
1964 const QgsGeometry source = newGeom;
1965 QSet< QgsPoint > added;
1966 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1967 {
1968 if ( avoidDuplicates && added.contains( *vertex ) )
1969 continue; // avoid duplicate points, e.g. start/end of rings
1970 mp->addGeometry( ( *vertex ).clone() );
1971 added.insert( *vertex );
1972 }
1973 newGeom = QgsGeometry( std::move( mp ) );
1974 }
1975
1976 //(Multi)Polygon to PolyhedralSurface
1978 {
1979 auto polySurface = std::make_unique< QgsPolyhedralSurface >();
1980 const QgsGeometry source = newGeom;
1981 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1982 {
1983 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1984 {
1985 polySurface->addPatch( polygon->clone() );
1986 }
1987 }
1988 newGeom = QgsGeometry( std::move( polySurface ) );
1989 }
1990
1991 //(Multi)Polygon/Triangle to TIN
1994 {
1995 auto tin = std::make_unique< QgsTriangulatedSurface >();
1996 const QgsGeometry source = newGeom;
1997 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1998 {
1999 if ( const QgsTriangle *triangle = qgsgeometry_cast< const QgsTriangle * >( *part ) )
2000 {
2001 tin->addPatch( triangle->clone() );
2002 }
2003 else if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
2004 {
2005 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
2006 if ( polygon->exteriorRing() )
2007 {
2008 const int numPoints = polygon->exteriorRing()->numPoints();
2009 if ( numPoints != 4 )
2010 {
2011 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
2012 return res;
2013 }
2014 auto triangle = std::make_unique< QgsTriangle >();
2015 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
2016 tin->addPatch( triangle.release() );
2017 }
2018 }
2019 }
2020 newGeom = QgsGeometry( std::move( tin ) );
2021 }
2022
2023 // PolyhedralSurface/TIN to (Multi)Polygon
2026 {
2027 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
2029 {
2030 for ( int i = 0; i < polySurface->numPatches(); ++i )
2031 {
2032 const QgsPolygon *patch = polySurface->patchN( i );
2033 auto polygon = std::make_unique< QgsPolygon >();
2034 polygon->setExteriorRing( patch->exteriorRing()->clone() );
2035 for ( int j = 0; j < patch->numInteriorRings(); ++j )
2036 {
2037 polygon->addInteriorRing( patch->interiorRing( j )->clone() );
2038 }
2039 multiPolygon->addGeometry( polygon.release() );
2040 }
2041 }
2042 newGeom = QgsGeometry( std::move( multiPolygon ) );
2043 }
2044
2045 // Polygon -> Triangle
2047 {
2048 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( newGeom.constGet() ) )
2049 {
2050 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
2051 if ( polygon->exteriorRing() )
2052 {
2053 const int numPoints = polygon->exteriorRing()->numPoints();
2054 if ( numPoints != 4 )
2055 {
2056 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
2057 return res;
2058 }
2059 auto triangle = std::make_unique< QgsTriangle >();
2060 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
2061 newGeom = QgsGeometry( std::move( triangle ) );
2062 }
2063 }
2064 }
2065
2066
2067 // Single -> multi
2068 if ( QgsWkbTypes::isMultiType( type ) && !newGeom.isMultipart() )
2069 {
2070 newGeom.convertToMultiType();
2071 }
2072 // Drop Z/M
2073 if ( newGeom.constGet()->is3D() && !QgsWkbTypes::hasZ( type ) )
2074 {
2075 newGeom.get()->dropZValue();
2076 }
2077 if ( newGeom.constGet()->isMeasure() && !QgsWkbTypes::hasM( type ) )
2078 {
2079 newGeom.get()->dropMValue();
2080 }
2081 // Add Z/M back, set to 0
2082 if ( !newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
2083 {
2084 newGeom.get()->addZValue( defaultZ );
2085 }
2086 if ( !newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
2087 {
2088 newGeom.get()->addMValue( defaultM );
2089 }
2090
2091 // Straight -> curve
2093 {
2094 newGeom.convertToCurvedMultiType();
2095 }
2096
2097 // Multi -> single
2098 if ( !QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart() )
2099 {
2100 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
2101 res.reserve( parts->partCount() );
2102 for ( int i = 0; i < parts->partCount(); i++ )
2103 {
2104 res << QgsGeometry( parts->geometryN( i )->clone() );
2105 }
2106 }
2107 // GeometryCollection (of Point/LineString/Polygon) -> MultiPoint/MultiLineString/MultiPolygon
2109 {
2111 const QgsGeometryCollection *geomColl( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
2112
2113 bool allExpectedType = true;
2114 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2115 {
2116 if ( geomColl->geometryN( i )->wkbType() != singleType )
2117 {
2118 allExpectedType = false;
2119 break;
2120 }
2121 }
2122 if ( allExpectedType )
2123 {
2124 std::unique_ptr< QgsGeometryCollection > newGeomCol;
2126 {
2127 newGeomCol = std::make_unique< QgsMultiPoint >();
2128 }
2130 {
2131 newGeomCol = std::make_unique< QgsMultiLineString >();
2132 }
2133 else
2134 {
2135 newGeomCol = std::make_unique< QgsMultiPolygon >();
2136 }
2137 newGeomCol->reserve( geomColl->numGeometries() );
2138 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2139 {
2140 newGeomCol->addGeometry( geomColl->geometryN( i )->clone() );
2141 }
2142 res << QgsGeometry( std::move( newGeomCol ) );
2143 }
2144 else
2145 {
2146 res << newGeom;
2147 }
2148 }
2149 else
2150 {
2151 res << newGeom;
2152 }
2153 return res;
2154}
2155
2156QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
2157{
2158 switch ( destType )
2159 {
2161 return convertToPoint( destMultipart );
2162
2164 return convertToLine( destMultipart );
2165
2167 return convertToPolygon( destMultipart );
2168
2169 default:
2170 return QgsGeometry();
2171 }
2172}
2173
2175{
2176 if ( !d->geometry )
2177 {
2178 return false;
2179 }
2180
2181 if ( isMultipart() ) //already multitype, no need to convert
2182 {
2183 return true;
2184 }
2185
2186 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
2188 if ( !multiGeom )
2189 {
2190 return false;
2191 }
2192
2193 //try to avoid cloning existing geometry whenever we can
2194
2195 //want to see a magic trick?... gather round kiddies...
2196 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
2197 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
2198 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
2199 multiGeom->addGeometry( d->geometry.release() );
2200 // and replace it with the multi geometry.
2201 // TADA! a clone free conversion in some cases
2202 d->geometry = std::move( geom );
2203 return true;
2204}
2205
2207{
2208 if ( !d->geometry )
2209 {
2210 return false;
2211 }
2212
2213 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
2214 {
2219 {
2220 return true;
2221 }
2222 default:
2223 break;
2224 }
2225
2226 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
2228 if ( !multiGeom )
2229 {
2230 return false;
2231 }
2232
2233 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2234 if ( sourceMultiGeom )
2235 {
2236 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
2237 {
2238 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
2239 return false;
2240 }
2241 }
2242 else
2243 {
2244 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
2245 return false;
2246 }
2247
2248 reset( std::move( geom ) );
2249 return true;
2250}
2251
2253{
2254 if ( !d->geometry )
2255 {
2256 return false;
2257 }
2258
2259 if ( !isMultipart() ) //already single part, no need to convert
2260 {
2261 return true;
2262 }
2263
2264 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2265 if ( !multiGeom || multiGeom->partCount() < 1 )
2266 return false;
2267
2268 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
2269 reset( std::move( firstPart ) );
2270 return true;
2271}
2272
2273
2275{
2277 if ( !origGeom )
2278 return false;
2279
2280 std::unique_ptr<QgsGeometryCollection> resGeom;
2281 switch ( geomType )
2282 {
2284 resGeom = std::make_unique<QgsMultiPoint>();
2285 break;
2287 resGeom = std::make_unique<QgsMultiLineString>();
2288 break;
2290 resGeom = std::make_unique<QgsMultiPolygon>();
2291 break;
2292 default:
2293 break;
2294 }
2295 if ( !resGeom )
2296 return false;
2297
2298 resGeom->reserve( origGeom->numGeometries() );
2299 for ( int i = 0; i < origGeom->numGeometries(); ++i )
2300 {
2301 const QgsAbstractGeometry *g = origGeom->geometryN( i );
2302 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
2303 resGeom->addGeometry( g->clone() );
2304 }
2305
2306 set( resGeom.release() );
2307 return true;
2308}
2309
2310
2312{
2313 if ( !d->geometry )
2314 {
2315 return QgsPointXY();
2316 }
2317 if ( const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
2318 {
2319 return QgsPointXY( pt->x(), pt->y() );
2320 }
2321 else
2322 {
2323 return QgsPointXY();
2324 }
2325}
2326
2328{
2329 QgsPolylineXY polyLine;
2330 if ( !d->geometry )
2331 {
2332 return polyLine;
2333 }
2334
2335 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve || QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CircularString );
2336 std::unique_ptr< QgsLineString > segmentizedLine;
2337 QgsLineString *line = nullptr;
2338 if ( doSegmentation )
2339 {
2340 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
2341 if ( !curve )
2342 {
2343 return polyLine;
2344 }
2345 segmentizedLine.reset( curve->curveToLine() );
2346 line = segmentizedLine.get();
2347 }
2348 else
2349 {
2350 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
2351 if ( !line )
2352 {
2353 return polyLine;
2354 }
2355 }
2356
2357 int nVertices = line->numPoints();
2358 polyLine.resize( nVertices );
2359 QgsPointXY *data = polyLine.data();
2360 const double *xData = line->xData();
2361 const double *yData = line->yData();
2362 for ( int i = 0; i < nVertices; ++i )
2363 {
2364 data->setX( *xData++ );
2365 data->setY( *yData++ );
2366 data++;
2367 }
2368
2369 return polyLine;
2370}
2371
2373{
2374 if ( !d->geometry )
2375 return QgsPolygonXY();
2376
2377 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
2378
2379 QgsPolygon *p = nullptr;
2380 std::unique_ptr< QgsPolygon > segmentized;
2381 if ( doSegmentation )
2382 {
2383 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
2384 if ( !curvePoly )
2385 {
2386 return QgsPolygonXY();
2387 }
2388 segmentized.reset( curvePoly->toPolygon() );
2389 p = segmentized.get();
2390 }
2391 else
2392 {
2393 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
2394 }
2395
2396 if ( !p )
2397 {
2398 return QgsPolygonXY();
2399 }
2400
2401 QgsPolygonXY polygon;
2402 convertPolygon( *p, polygon );
2403
2404 return polygon;
2405}
2406
2408{
2409 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
2410 {
2411 return QgsMultiPointXY();
2412 }
2413
2414 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
2415 if ( !mp )
2416 {
2417 return QgsMultiPointXY();
2418 }
2419
2420 int nPoints = mp->numGeometries();
2421 QgsMultiPointXY multiPoint( nPoints );
2422 for ( int i = 0; i < nPoints; ++i )
2423 {
2424 const QgsPoint *pt = mp->pointN( i );
2425 multiPoint[i].setX( pt->x() );
2426 multiPoint[i].setY( pt->y() );
2427 }
2428 return multiPoint;
2429}
2430
2432{
2433 if ( !d->geometry )
2434 {
2435 return QgsMultiPolylineXY();
2436 }
2437
2438 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2439 if ( !geomCollection )
2440 {
2441 return QgsMultiPolylineXY();
2442 }
2443
2444 int nLines = geomCollection->numGeometries();
2445 if ( nLines < 1 )
2446 {
2447 return QgsMultiPolylineXY();
2448 }
2449
2451 mpl.reserve( nLines );
2452 for ( int i = 0; i < nLines; ++i )
2453 {
2454 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2455 std::unique_ptr< QgsLineString > segmentized;
2456 if ( !line )
2457 {
2458 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2459 if ( !curve )
2460 {
2461 continue;
2462 }
2463 segmentized.reset( curve->curveToLine() );
2464 line = segmentized.get();
2465 }
2466
2467 QgsPolylineXY polyLine;
2468 int nVertices = line->numPoints();
2469 polyLine.resize( nVertices );
2470 QgsPointXY *data = polyLine.data();
2471 const double *xData = line->xData();
2472 const double *yData = line->yData();
2473 for ( int i = 0; i < nVertices; ++i )
2474 {
2475 data->setX( *xData++ );
2476 data->setY( *yData++ );
2477 data++;
2478 }
2479 mpl.append( polyLine );
2480 }
2481 return mpl;
2482}
2483
2485{
2486 if ( !d->geometry )
2487 {
2488 return QgsMultiPolygonXY();
2489 }
2490
2491 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2492 if ( !geomCollection )
2493 {
2494 return QgsMultiPolygonXY();
2495 }
2496
2497 const int nPolygons = geomCollection->numGeometries();
2498 if ( nPolygons < 1 )
2499 {
2500 return QgsMultiPolygonXY();
2501 }
2502
2504 mp.reserve( nPolygons );
2505 for ( int i = 0; i < nPolygons; ++i )
2506 {
2507 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2508 if ( !polygon )
2509 {
2510 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2511 if ( cPolygon )
2512 {
2513 polygon = cPolygon->toPolygon();
2514 }
2515 else
2516 {
2517 continue;
2518 }
2519 }
2520
2521 QgsPolygonXY poly;
2522 convertPolygon( *polygon, poly );
2523 mp.push_back( poly );
2524 }
2525 return mp;
2526}
2527
2528double QgsGeometry::area() const
2529{
2530 if ( !d->geometry )
2531 {
2532 return -1.0;
2533 }
2534
2535 return d->geometry->area();
2536}
2537
2539{
2540 if ( !d->geometry )
2541 {
2542 throw QgsInvalidArgumentException( "Cannot compute 3D area: geometry is null." );
2543 }
2544
2545 return d->geometry->area3D();
2546}
2547
2549{
2550 if ( !d->geometry )
2551 {
2552 return -1.0;
2553 }
2554
2555 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2556 {
2558 return 0.0;
2559
2561 return d->geometry->length();
2562
2564 return d->geometry->perimeter();
2565
2568 return d->geometry->length();
2569 }
2570 return -1;
2571}
2572
2573double QgsGeometry::distance( const QgsGeometry &geom ) const
2574{
2575 if ( !d->geometry || !geom.d->geometry )
2576 {
2577 return -1.0;
2578 }
2579
2580 // avoid calling geos for trivial point-to-point distance calculations
2581 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( geom.wkbType() ) == Qgis::WkbType::Point )
2582 {
2583 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2584 }
2585
2586 QgsGeos g( d->geometry.get() );
2587 mLastError.clear();
2588 return g.distance( geom.d->geometry.get(), &mLastError );
2589}
2590
2592{
2593 if ( !d->geometry || !geom.d->geometry )
2594 {
2595 return -1.0;
2596 }
2597
2598 QgsGeos g( d->geometry.get() );
2599 mLastError.clear();
2600 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2601}
2602
2603double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2604{
2605 if ( !d->geometry || !geom.d->geometry )
2606 {
2607 return -1.0;
2608 }
2609
2610 QgsGeos g( d->geometry.get() );
2611 mLastError.clear();
2612 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2613}
2614
2615
2617{
2618 if ( !d->geometry || !geom.d->geometry )
2619 {
2620 return -1.0;
2621 }
2622
2623 QgsGeos g( d->geometry.get() );
2624 mLastError.clear();
2625 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
2626}
2627
2628double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2629{
2630 if ( !d->geometry || !geom.d->geometry )
2631 {
2632 return -1.0;
2633 }
2634
2635 QgsGeos g( d->geometry.get() );
2636 mLastError.clear();
2637 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2638}
2639
2641{
2642 if ( !d->geometry || d->geometry.get()->isEmpty() )
2644 return d->geometry->vertices_begin();
2645}
2646
2648{
2649 if ( !d->geometry || d->geometry.get()->isEmpty() )
2651 return d->geometry->vertices_end();
2652}
2653
2655{
2656 if ( !d->geometry || d->geometry.get()->isEmpty() )
2657 return QgsVertexIterator();
2658 return QgsVertexIterator( d->geometry.get() );
2659}
2660
2662{
2663 if ( !d->geometry )
2665
2666 detach();
2667 return d->geometry->parts_begin();
2668}
2669
2671{
2672 if ( !d->geometry )
2674 return d->geometry->parts_end();
2675}
2676
2678{
2679 if ( !d->geometry )
2681 return d->geometry->const_parts_begin();
2682}
2683
2685{
2686 if ( !d->geometry )
2688 return d->geometry->const_parts_end();
2689}
2690
2692{
2693 if ( !d->geometry )
2694 return QgsGeometryPartIterator();
2695
2696 detach();
2697 return QgsGeometryPartIterator( d->geometry.get() );
2698}
2699
2701{
2702 if ( !d->geometry )
2704
2705 return QgsGeometryConstPartIterator( d->geometry.get() );
2706}
2707
2708QgsGeometry QgsGeometry::buffer( double distance, int segments, QgsFeedback *feedback ) const
2709{
2710 if ( !d->geometry )
2711 {
2712 return QgsGeometry();
2713 }
2714
2715 QgsGeos g( d->geometry.get() );
2716 mLastError.clear();
2717 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError, feedback ) );
2718 if ( !geom )
2719 {
2720 QgsGeometry result;
2721 result.mLastError = mLastError;
2722 return result;
2723 }
2724 return QgsGeometry( std::move( geom ) );
2725}
2726
2727QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, QgsFeedback *feedback ) const
2728{
2729 if ( !d->geometry )
2730 {
2731 return QgsGeometry();
2732 }
2733
2734 QgsGeos g( d->geometry.get() );
2735 mLastError.clear();
2736 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError, feedback );
2737 if ( !geom )
2738 {
2739 QgsGeometry result;
2740 result.mLastError = mLastError;
2741 return result;
2742 }
2743 return QgsGeometry( geom );
2744}
2745
2746QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2747{
2748 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2749 {
2750 return QgsGeometry();
2751 }
2752
2753 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2754 {
2755 const QVector<QgsGeometry> parts = asGeometryCollection();
2756 QVector<QgsGeometry> results;
2757 results.reserve( parts.count() );
2758 for ( const QgsGeometry &part : parts )
2759 {
2760 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2761 if ( !result.isNull() )
2762 results << result;
2763 }
2764 if ( results.isEmpty() )
2765 return QgsGeometry();
2766
2767 QgsGeometry first = results.takeAt( 0 );
2768 for ( const QgsGeometry &result : std::as_const( results ) )
2769 {
2770 first.addPart( result );
2771 }
2772 return first;
2773 }
2774 else
2775 {
2776 QgsGeos geos( d->geometry.get() );
2777 mLastError.clear();
2778
2779 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2780 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2781
2782 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2783 if ( !offsetGeom )
2784 {
2785 QgsGeometry result;
2786 result.mLastError = mLastError;
2787 return result;
2788 }
2789
2790 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2791 {
2792 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2793 if ( newOrientation != prevOrientation )
2794 {
2795 // GEOS has flipped line orientation, flip it back
2796 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2797 offsetGeom = std::move( flipped );
2798 }
2799 }
2800 return QgsGeometry( std::move( offsetGeom ) );
2801 }
2802}
2803
2804QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2805{
2806 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2807 {
2808 return QgsGeometry();
2809 }
2810
2811 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2812 {
2813 const QVector<QgsGeometry> parts = asGeometryCollection();
2814 QVector<QgsGeometry> results;
2815 results.reserve( parts.count() );
2816 for ( const QgsGeometry &part : parts )
2817 {
2818 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2819 if ( !result.isNull() )
2820 results << result;
2821 }
2822 if ( results.isEmpty() )
2823 return QgsGeometry();
2824
2825 QgsGeometry first = results.takeAt( 0 );
2826 for ( const QgsGeometry &result : std::as_const( results ) )
2827 {
2828 first.addPart( result );
2829 }
2830 return first;
2831 }
2832 else
2833 {
2834 QgsGeos geos( d->geometry.get() );
2835 mLastError.clear();
2836 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit, &mLastError );
2837 if ( !bufferGeom )
2838 {
2839 QgsGeometry result;
2840 result.mLastError = mLastError;
2841 return result;
2842 }
2843 return QgsGeometry( std::move( bufferGeom ) );
2844 }
2845}
2846
2847QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2848{
2849 QgsInternalGeometryEngine engine( *this );
2850
2851 return engine.taperedBuffer( startWidth, endWidth, segments );
2852}
2853
2855{
2856 QgsInternalGeometryEngine engine( *this );
2857
2858 return engine.variableWidthBufferByM( segments );
2859}
2860
2861QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance, double startDeflection, double endDeflection ) const
2862{
2863 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2864 {
2865 return QgsGeometry();
2866 }
2867
2868 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2869 {
2870 const QVector<QgsGeometry> parts = asGeometryCollection();
2871 QVector<QgsGeometry> results;
2872 results.reserve( parts.count() );
2873 for ( const QgsGeometry &part : parts )
2874 {
2875 QgsGeometry result = part.extendLine( startDistance, endDistance, startDeflection, endDeflection );
2876 if ( !result.isNull() )
2877 results << result;
2878 }
2879 if ( results.isEmpty() )
2880 return QgsGeometry();
2881
2882 QgsGeometry first = results.takeAt( 0 );
2883 for ( const QgsGeometry &result : std::as_const( results ) )
2884 {
2885 first.addPart( result );
2886 }
2887 return first;
2888 }
2889 else
2890 {
2891 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2892 if ( !line )
2893 return QgsGeometry();
2894
2895 std::unique_ptr< QgsLineString > newLine( line->clone() );
2896 newLine->extend( startDistance, endDistance, startDeflection, endDeflection );
2897 return QgsGeometry( std::move( newLine ) );
2898 }
2899}
2900
2901QgsGeometry QgsGeometry::simplify( double tolerance, QgsFeedback *feedback ) const
2902{
2903 if ( !d->geometry )
2904 {
2905 return QgsGeometry();
2906 }
2907
2908 QgsGeos geos( d->geometry.get() );
2909 mLastError.clear();
2910 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError, feedback ) );
2911 if ( !simplifiedGeom )
2912 {
2913 QgsGeometry result;
2914 result.mLastError = mLastError;
2915 return result;
2916 }
2917 return QgsGeometry( std::move( simplifiedGeom ) );
2918}
2919
2920QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2921{
2922 QgsInternalGeometryEngine engine( *this );
2923
2924 return engine.densifyByCount( extraNodesPerSegment );
2925}
2926
2928{
2929 QgsInternalGeometryEngine engine( *this );
2930
2931 return engine.densifyByDistance( distance );
2932}
2933
2934QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2935{
2936 QgsInternalGeometryEngine engine( *this );
2937
2938 return engine.convertToCurves( distanceTolerance, angleTolerance );
2939}
2940
2942{
2943 if ( !d->geometry )
2944 {
2945 return QgsGeometry();
2946 }
2947
2948 // avoid calling geos for trivial point centroids
2949 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
2950 {
2951 QgsGeometry c = *this;
2952 c.get()->dropZValue();
2953 c.get()->dropMValue();
2954 return c;
2955 }
2956
2957 QgsGeos geos( d->geometry.get() );
2958
2959 mLastError.clear();
2960 QgsGeometry result( geos.centroid( &mLastError ) );
2961 result.mLastError = mLastError;
2962 return result;
2963}
2964
2966{
2967 if ( !d->geometry )
2968 {
2969 return QgsGeometry();
2970 }
2971
2972 QgsGeos geos( d->geometry.get() );
2973
2974 mLastError.clear();
2975 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2976 result.mLastError = mLastError;
2977 return result;
2978}
2979
2980QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2981{
2982 QgsInternalGeometryEngine engine( *this );
2983
2984 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2985}
2986
2987QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2988{
2989 if ( !d->geometry )
2990 {
2991 return QgsGeometry();
2992 }
2993
2994 QgsGeos geos( d->geometry.get() );
2995
2996 mLastError.clear();
2997 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2998 result.mLastError = mLastError;
2999 return result;
3000}
3001
3003{
3004 if ( !d->geometry )
3005 {
3006 return QgsGeometry();
3007 }
3008
3009 QgsGeos geos( d->geometry.get() );
3010
3011 mLastError.clear();
3012 QgsGeometry result( geos.minimumWidth( &mLastError ) );
3013 result.mLastError = mLastError;
3014 return result;
3015}
3016
3018{
3019 if ( !d->geometry )
3020 {
3021 return std::numeric_limits< double >::quiet_NaN();
3022 }
3023
3024 QgsGeos geos( d->geometry.get() );
3025
3026 mLastError.clear();
3027 return geos.minimumClearance( &mLastError );
3028}
3029
3031{
3032 if ( !d->geometry )
3033 {
3034 return QgsGeometry();
3035 }
3036
3037 QgsGeos geos( d->geometry.get() );
3038
3039 mLastError.clear();
3040 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
3041 result.mLastError = mLastError;
3042 return result;
3043}
3044
3046{
3047 if ( !d->geometry )
3048 {
3049 return QgsGeometry();
3050 }
3051 QgsGeos geos( d->geometry.get() );
3052 mLastError.clear();
3053 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
3054 if ( !cHull )
3055 {
3056 QgsGeometry geom;
3057 geom.mLastError = mLastError;
3058 return geom;
3059 }
3060 return QgsGeometry( std::move( cHull ) );
3061}
3062
3063QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles, QgsFeedback *feedback ) const
3064{
3065 if ( !d->geometry )
3066 {
3067 return QgsGeometry();
3068 }
3069 QgsGeos geos( d->geometry.get() );
3070 mLastError.clear();
3071 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError, feedback ) );
3072 if ( !concaveHull )
3073 {
3074 QgsGeometry geom;
3075 geom.mLastError = mLastError;
3076 return geom;
3077 }
3078 return QgsGeometry( std::move( concaveHull ) );
3079}
3080
3081QgsGeometry QgsGeometry::concaveHullOfPolygons( double lengthRatio, bool allowHoles, bool isTight, QgsFeedback *feedback ) const
3082{
3083 if ( !d->geometry )
3084 {
3085 return QgsGeometry();
3086 }
3087
3089 {
3090 QgsGeometry geom;
3091 geom.mLastError = u"Only Polygon or MultiPolygon geometries are supported"_s;
3092 return geom;
3093 }
3094
3095 QgsGeos geos( d->geometry.get() );
3096 mLastError.clear();
3097 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHullOfPolygons( lengthRatio, allowHoles, isTight, &mLastError, feedback ) );
3098 if ( !concaveHull )
3099 {
3100 QgsGeometry geom;
3101 geom.mLastError = mLastError;
3102 return geom;
3103 }
3104 return QgsGeometry( std::move( concaveHull ) );
3105}
3106
3107QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
3108{
3109 if ( !d->geometry )
3110 {
3111 return QgsGeometry();
3112 }
3113
3114 QgsGeos geos( d->geometry.get() );
3115 mLastError.clear();
3116 QgsGeometry result = QgsGeometry( geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError ) );
3117 result.mLastError = mLastError;
3118 return result;
3119}
3120
3121QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
3122{
3123 if ( !d->geometry )
3124 {
3125 return QgsGeometry();
3126 }
3127
3128 QgsGeos geos( d->geometry.get() );
3129 mLastError.clear();
3130 QgsGeometry result = QgsGeometry( geos.delaunayTriangulation( tolerance, edgesOnly ) );
3131 result.mLastError = mLastError;
3132 return result;
3133}
3134
3136{
3137 if ( !d->geometry )
3138 {
3139 return QgsGeometry();
3140 }
3141
3142 QgsGeos geos( d->geometry.get() );
3143 mLastError.clear();
3144 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
3145 result.mLastError = mLastError;
3146 return result;
3147}
3148
3150{
3151 if ( !d->geometry )
3152 {
3153 return QgsGeometry();
3154 }
3155
3156 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::GeometryCollection
3157 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPolygon
3158 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::Polygon )
3159 return QgsGeometry();
3160
3161 QgsGeos geos( d->geometry.get() );
3162 mLastError.clear();
3163 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
3164 result.mLastError = mLastError;
3165 return result;
3166}
3167
3169{
3170 if ( !d->geometry )
3171 {
3173 }
3174
3175 QgsGeos geos( d->geometry.get() );
3176 mLastError.clear();
3177 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
3178
3179 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
3180
3181 if ( invalidEdges && invalidEdgesGeom )
3182 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
3183
3184 return result;
3185}
3186
3187QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
3188{
3189 if ( !d->geometry )
3190 {
3191 return QgsGeometry();
3192 }
3193
3194 QgsGeos geos( d->geometry.get() );
3195 mLastError.clear();
3196 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
3197 result.mLastError = mLastError;
3198 return result;
3199}
3200
3202{
3203 if ( !d->geometry )
3204 {
3205 return QgsGeometry();
3206 }
3207
3208 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::GeometryCollection
3209 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPolygon
3210 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::Polygon )
3211 return QgsGeometry();
3212
3213 QgsGeos geos( d->geometry.get() );
3214 mLastError.clear();
3215 const QgsGeometry result( geos.cleanCoverage( parameters, &mLastError, feedback ) );
3216 result.mLastError = mLastError;
3217 return result;
3218}
3219
3221{
3222 if ( !d->geometry )
3223 {
3224 return QgsGeometry();
3225 }
3226
3227 QgsGeos geos( d->geometry.get() );
3228 mLastError.clear();
3229 QgsGeometry result( geos.node( &mLastError ) );
3230 result.mLastError = mLastError;
3231 return result;
3232}
3233
3235{
3236 if ( !d->geometry )
3237 {
3238 return QgsGeometry();
3239 }
3240
3241 QgsGeos geos( d->geometry.get() );
3242 mLastError.clear();
3243 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
3244 result.mLastError = mLastError;
3245 return result;
3246}
3247
3248QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3249{
3250 if ( !d->geometry )
3251 {
3252 return QgsGeometry();
3253 }
3254
3255 const QgsAbstractGeometry *geom = d->geometry.get();
3256 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
3257 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
3258 {
3259 segmentizedCopy.reset( d->geometry->segmentize() );
3260 geom = segmentizedCopy.get();
3261 }
3262
3263 QgsGeos geos( geom );
3264 mLastError.clear();
3265 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters, feedback ) );
3266 if ( !result )
3267 {
3268 QgsGeometry geom;
3269 geom.mLastError = mLastError;
3270 return geom;
3271 }
3272 return QgsGeometry( std::move( result ) );
3273}
3274
3276{
3277 if ( !d->geometry )
3278 {
3279 return QgsGeometry();
3280 }
3281
3282 QgsGeometry line = *this;
3284 return QgsGeometry();
3285 else if ( type() == Qgis::GeometryType::Polygon )
3286 {
3287 line = QgsGeometry( d->geometry->boundary() );
3288 }
3289
3290 const QgsCurve *curve = nullptr;
3292 {
3293 // if multi part, iterate through parts to find target part
3294 for ( int part = 0; part < collection->numGeometries(); ++part )
3295 {
3296 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
3297 if ( !candidate )
3298 continue;
3299 const double candidateLength = candidate->length();
3300 if ( candidateLength >= distance )
3301 {
3302 curve = candidate;
3303 break;
3304 }
3305
3306 distance -= candidateLength;
3307 }
3308 }
3309 else
3310 {
3312 }
3313 if ( !curve )
3314 return QgsGeometry();
3315
3316 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
3317 if ( !result )
3318 {
3319 return QgsGeometry();
3320 }
3321 return QgsGeometry( std::move( result ) );
3322}
3323
3324double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
3325{
3326 if ( type() != Qgis::GeometryType::Line )
3327 return -1;
3328
3330 return -1;
3331
3332 QgsGeometry segmentized = *this;
3334 {
3335 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
3336 }
3337
3338 QgsGeos geos( d->geometry.get() );
3339 mLastError.clear();
3340 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
3341}
3342
3344{
3345 if ( !d->geometry || d->geometry->isEmpty() )
3346 return 0.0;
3347
3348 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
3350 return 0.0;
3351
3352 // always operate on segmentized geometries
3353 QgsGeometry segmentized = *this;
3354 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
3355 {
3356 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
3357 }
3358
3359 QgsVertexId previous;
3360 QgsVertexId next;
3361 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
3362 return 0.0;
3363
3364 if ( previous == next )
3365 {
3366 // distance coincided exactly with a vertex
3367 QgsVertexId v2 = previous;
3368 QgsVertexId v1;
3369 QgsVertexId v3;
3370 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
3371 if ( v1.isValid() && v3.isValid() )
3372 {
3373 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3374 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3375 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
3376 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3377 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
3378 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
3379 }
3380 else if ( v3.isValid() )
3381 {
3382 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
3383 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
3384 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3385 }
3386 else
3387 {
3388 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3389 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3390 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3391 }
3392 }
3393 else
3394 {
3395 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
3396 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
3397 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3398 }
3399}
3400
3401QgsGeometry QgsGeometry::intersection( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3402{
3403 if ( !d->geometry || geometry.isNull() )
3404 {
3405 return QgsGeometry();
3406 }
3407
3408 QgsGeos geos( d->geometry.get() );
3409
3410 mLastError.clear();
3411 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3412
3413 if ( !resultGeom )
3414 {
3415 QgsGeometry geom;
3416 geom.mLastError = mLastError;
3417 return geom;
3418 }
3419
3420 return QgsGeometry( std::move( resultGeom ) );
3421}
3422
3423QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3424{
3425 if ( !d->geometry || geometry.isNull() )
3426 {
3427 return QgsGeometry();
3428 }
3429
3430 QgsGeos geos( d->geometry.get() );
3431 mLastError.clear();
3432 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3433 if ( !resultGeom )
3434 {
3435 QgsGeometry geom;
3436 geom.mLastError = mLastError;
3437 return geom;
3438 }
3439 return QgsGeometry( std::move( resultGeom ) );
3440}
3441
3443{
3444 if ( !d->geometry )
3445 {
3446 return QgsGeometry();
3447 }
3448
3449 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::LineString )
3450 {
3451 // special case - a single linestring was passed
3452 return QgsGeometry( *this );
3453 }
3454
3455 QgsGeos geos( d->geometry.get() );
3456 mLastError.clear();
3457 QgsGeometry result( geos.mergeLines( &mLastError, parameters ) );
3458 result.mLastError = mLastError;
3459 return result;
3460}
3461
3462QgsGeometry QgsGeometry::difference( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3463{
3464 if ( !d->geometry || geometry.isNull() )
3465 {
3466 return QgsGeometry();
3467 }
3468
3469 QgsGeos geos( d->geometry.get() );
3470
3471 mLastError.clear();
3472 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3473 if ( !resultGeom )
3474 {
3475 QgsGeometry geom;
3476 geom.mLastError = mLastError;
3477 return geom;
3478 }
3479 return QgsGeometry( std::move( resultGeom ) );
3480}
3481
3482QgsGeometry QgsGeometry::symDifference( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3483{
3484 if ( !d->geometry || geometry.isNull() )
3485 {
3486 return QgsGeometry();
3487 }
3488
3489 QgsGeos geos( d->geometry.get() );
3490
3491 mLastError.clear();
3492 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3493 if ( !resultGeom )
3494 {
3495 QgsGeometry geom;
3496 geom.mLastError = mLastError;
3497 return geom;
3498 }
3499 return QgsGeometry( std::move( resultGeom ) );
3500}
3501
3503{
3504 QgsInternalGeometryEngine engine( *this );
3505
3506 return engine.extrude( x, y );
3507}
3508
3510
3511QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3512{
3514 return QVector< QgsPointXY >();
3515
3516 QgsInternalGeometryEngine engine( *this );
3517 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3518 mLastError = engine.lastError();
3519 return res;
3520}
3521
3522QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3523{
3525 return QVector< QgsPointXY >();
3526
3527 QgsInternalGeometryEngine engine( *this );
3528 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3529 mLastError = engine.lastError();
3530 return res;
3531}
3533
3535{
3536 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3537}
3538
3540{
3541 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3542}
3543
3544QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3545{
3546 QVector<QgsGeometry> geometryList;
3547 if ( !d->geometry )
3548 {
3549 return geometryList;
3550 }
3551
3553 if ( gc )
3554 {
3555 int numGeom = gc->numGeometries();
3556 geometryList.reserve( numGeom );
3557 for ( int i = 0; i < numGeom; ++i )
3558 {
3559 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3560 }
3561 }
3562 else //a singlepart geometry
3563 {
3564 geometryList.append( *this );
3565 }
3566
3567 return geometryList;
3568}
3569
3571{
3572 QgsPointXY point = asPoint();
3573 return point.toQPointF();
3574}
3575
3577{
3578 const QgsAbstractGeometry *part = constGet();
3579
3580 // if a geometry collection, get first part only
3582 {
3583 if ( collection->numGeometries() > 0 )
3584 part = collection->geometryN( 0 );
3585 else
3586 return QPolygonF();
3587 }
3588
3589 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3590 return curve->asQPolygonF();
3591 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3592 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3593 return QPolygonF();
3594}
3595
3596bool QgsGeometry::deleteRing( int ringNum, int partNum )
3597{
3598 if ( !d->geometry )
3599 {
3600 return false;
3601 }
3602
3603 detach();
3604 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3605 return ok;
3606}
3607
3608bool QgsGeometry::deletePart( int partNum )
3609{
3610 if ( !d->geometry )
3611 {
3612 return false;
3613 }
3614
3615 if ( !isMultipart() && partNum < 1 )
3616 {
3617 set( nullptr );
3618 return true;
3619 }
3620
3621 detach();
3622 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3623 return ok;
3624}
3625
3626Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3627{
3628 if ( !d->geometry )
3629 {
3631 }
3632
3633 Qgis::WkbType geomTypeBeforeModification = wkbType();
3634
3635 bool haveInvalidGeometry = false;
3636 bool geomModified = false;
3637
3638 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3639 if ( diffGeom )
3640 {
3641 reset( std::move( diffGeom ) );
3642 geomModified = true;
3643 }
3644
3645 if ( geomTypeBeforeModification != wkbType() )
3647 if ( haveInvalidGeometry )
3649 if ( !geomModified )
3651
3653}
3654
3686
3687QgsGeometry QgsGeometry::makeValid( Qgis::MakeValidMethod method, bool keepCollapsed, QgsFeedback *feedback ) const
3688{
3689 if ( !d->geometry )
3690 return QgsGeometry();
3691
3692 mLastError.clear();
3693 QgsGeos geos( d->geometry.get() );
3694 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError, feedback ) );
3695
3696 QgsGeometry result = QgsGeometry( std::move( g ) );
3697 result.mLastError = mLastError;
3698 return result;
3699}
3700
3705
3707{
3708 if ( !d->geometry )
3709 {
3711 }
3712
3713 if ( isMultipart() )
3714 {
3715 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3716 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3718 {
3719 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3720 }
3721 }
3722 else
3723 {
3724 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3725 {
3726 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3727 }
3728 }
3729
3731}
3732
3734{
3735 if ( !d->geometry )
3736 return QgsGeometry();
3737
3738 if ( isMultipart() )
3739 {
3740 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3741 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3742 newCollection->reserve( collection->numGeometries() );
3743 for ( int i = 0; i < collection->numGeometries(); ++i )
3744 {
3745 const QgsAbstractGeometry *g = collection->geometryN( i );
3747 {
3748 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3749 corrected->forceClockwise();
3750 newCollection->addGeometry( corrected.release() );
3751 }
3752 else
3753 {
3754 newCollection->addGeometry( g->clone() );
3755 }
3756 }
3757 return QgsGeometry( std::move( newCollection ) );
3758 }
3759 else
3760 {
3761 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3762 {
3763 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3764 corrected->forceClockwise();
3765 return QgsGeometry( std::move( corrected ) );
3766 }
3767 else
3768 {
3769 // not a curve polygon, so return unchanged
3770 return *this;
3771 }
3772 }
3773}
3774
3776{
3777 if ( !d->geometry )
3778 return QgsGeometry();
3779
3780 if ( isMultipart() )
3781 {
3782 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3783 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3784 newCollection->reserve( collection->numGeometries() );
3785 for ( int i = 0; i < collection->numGeometries(); ++i )
3786 {
3787 const QgsAbstractGeometry *g = collection->geometryN( i );
3789 {
3790 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3791 corrected->forceCounterClockwise();
3792 newCollection->addGeometry( corrected.release() );
3793 }
3794 else
3795 {
3796 newCollection->addGeometry( g->clone() );
3797 }
3798 }
3799 return QgsGeometry( std::move( newCollection ) );
3800 }
3801 else
3802 {
3803 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3804 {
3805 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3806 corrected->forceCounterClockwise();
3807 return QgsGeometry( std::move( corrected ) );
3808 }
3809 else
3810 {
3811 // not a curve polygon, so return unchanged
3812 return *this;
3813 }
3814 }
3815}
3816
3817
3818void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3819{
3820 errors.clear();
3821 if ( !d->geometry )
3822 return;
3823
3824 // avoid expensive calcs for trivial point geometries
3825 if ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) == Qgis::GeometryType::Point )
3826 {
3827 return;
3828 }
3829
3830 switch ( method )
3831 {
3833 QgsGeometryValidator::validateGeometry( *this, errors, method );
3834 return;
3835
3837 {
3838 QgsGeos geos( d->geometry.get(), 0, Qgis::GeosCreationFlags() );
3839 QString error;
3840 QgsGeometry errorLoc;
3841 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3842 {
3843 if ( errorLoc.isNull() )
3844 {
3845 errors.append( QgsGeometry::Error( error ) );
3846 }
3847 else
3848 {
3849 const QgsPointXY point = errorLoc.asPoint();
3850 errors.append( QgsGeometry::Error( error, point ) );
3851 }
3852 return;
3853 }
3854 break;
3855 }
3857 {
3858#ifdef WITH_SFCGAL
3859 QString errorMsg;
3860 QgsGeometry errorLoc;
3861 const QgsSfcgalGeometry sfcgalGeom( d->geometry.get() );
3862 if ( !QgsSfcgalEngine::isValid( sfcgalGeom.sfcgalGeometry().get(), nullptr, &errorMsg, &errorLoc ) )
3863 {
3864 if ( errorLoc.isNull() )
3865 {
3866 errors.append( QgsGeometry::Error( errorMsg ) );
3867 }
3868 else
3869 {
3870 const QgsPointXY point = errorLoc.asPoint();
3871 errors.append( QgsGeometry::Error( errorMsg, point ) );
3872 }
3873 return;
3874 }
3875#else
3876 throw QgsNotSupportedException( u"This operation requires a QGIS installation with SFCGAL support enabled. Please use a version of QGIS that includes SFCGAL."_s );
3877#endif
3878 }
3879 }
3880}
3881
3883{
3884 if ( !d->geometry )
3885 {
3886 return;
3887 }
3888
3889 detach();
3890 d->geometry->normalize();
3891}
3892
3894{
3895 if ( !d->geometry )
3896 {
3897 return false;
3898 }
3899
3900 return d->geometry->isValid( mLastError, flags );
3901}
3902
3904{
3905 if ( !d->geometry )
3906 return false;
3907
3908 QgsGeos geos( d->geometry.get() );
3909 mLastError.clear();
3910 return geos.isSimple( &mLastError );
3911}
3912
3913bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3914{
3915 if ( !d->geometry )
3916 return false;
3917
3918 QgsInternalGeometryEngine engine( *this );
3919 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3920}
3921
3923{
3925}
3926
3928{
3929 // === WARNING ===
3930 // 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)
3931
3932 if ( !d->geometry || g.isNull() )
3933 {
3934 return false;
3935 }
3936
3937 // fast check - are they shared copies of the same underlying geometry?
3938 if ( d == g.d )
3939 return true;
3940
3941 // fast check - distinct geometry types?
3942 if ( type() != g.type() )
3943 return false;
3944
3945 mLastError.clear();
3946 switch ( backend )
3947 {
3949 {
3950 // avoid calling geos for trivial point case
3951 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3952 return *d->geometry == *g.d->geometry;
3953
3954 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3955 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3956 return false;
3957
3958 QgsGeos geos( d->geometry.get() );
3959 // fuzzy check call, with near zero epsilon, will behave as an exact comparison
3960 return geos.isFuzzyEqual( g.d->geometry.get(), 1e-8, &mLastError );
3961 }
3962
3964 {
3965 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3966 if ( ( !d->geometry->is3D() && d->geometry->boundingBox() != g.d->geometry->boundingBox() ) || ( d->geometry->is3D() && d->geometry->boundingBox3D() != g.d->geometry->boundingBox3D() ) )
3967 return false;
3968
3969 // slower check - actually test the geometries
3970 return *d->geometry == *g.d->geometry;
3971 }
3972 }
3974}
3975
3977{
3978 if ( !d->geometry || !g.d->geometry )
3979 {
3980 return false;
3981 }
3982
3983 // fast check - are they shared copies of the same underlying geometry?
3984 if ( d == g.d )
3985 return true;
3986
3987 // fast check - distinct geometry types?
3988 if ( type() != g.type() )
3989 return false;
3990
3991 mLastError.clear();
3992 switch ( backend )
3993 {
3995 {
3996 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3997 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3998 return false;
3999
4000 QgsGeos geos( d->geometry.get() );
4001 return geos.isEqual( g.d->geometry.get(), &mLastError );
4002 }
4003
4005 throw QgsNotSupportedException( u"Geometry backend '%1' is not supported by this function."_s.arg( qgsEnumValueToKey( backend ) ) );
4006 }
4008}
4009
4010bool QgsGeometry::isFuzzyEqual( const QgsGeometry &g, double epsilon, Qgis::GeometryBackend backend ) const
4011{
4012 if ( !d->geometry || g.isNull() )
4013 {
4014 return false;
4015 }
4016
4017 // fast check - are they shared copies of the same underlying geometry?
4018 if ( d == g.d )
4019 return true;
4020
4021 // fast check - distinct geometry types?
4022 if ( type() != g.type() )
4023 return false;
4024
4025 mLastError.clear();
4026 switch ( backend )
4027 {
4029 {
4030 QgsGeos geos( d->geometry.get() );
4031 return geos.isFuzzyEqual( g.d->geometry.get(), epsilon, &mLastError );
4032 }
4033
4035 {
4036 // slower check - actually test the geometries
4037 return d->geometry->fuzzyEqual( *g.d->geometry, epsilon );
4038 }
4039 }
4041}
4042
4043QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters, QgsFeedback *feedback )
4044{
4045 QgsGeos geos( nullptr );
4046
4047 QString error;
4048 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters, feedback ) );
4049 QgsGeometry result( std::move( geom ) );
4050 result.mLastError = error;
4051 return result;
4052}
4053
4054QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
4055{
4056 QVector<const QgsAbstractGeometry *> geomV2List;
4057 for ( const QgsGeometry &g : geometryList )
4058 {
4059 if ( !( g.isNull() ) )
4060 {
4061 geomV2List.append( g.constGet() );
4062 }
4063 }
4064
4065 QString error;
4066 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
4067 result.mLastError = error;
4068 return result;
4069}
4070
4072{
4073 if ( !d->geometry || !requiresConversionToStraightSegments() )
4074 {
4075 return;
4076 }
4077
4078 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
4079 reset( std::move( straightGeom ) );
4080}
4081
4083{
4084 if ( !d->geometry )
4085 {
4086 return false;
4087 }
4088
4089 return d->geometry->hasCurvedSegments();
4090}
4091
4093{
4094 if ( !d->geometry )
4095 {
4097 }
4098
4099 detach();
4100 d->geometry->transform( ct, direction, transformZ );
4102}
4103
4104Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
4105{
4106 if ( !d->geometry )
4107 {
4109 }
4110
4111 detach();
4112 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
4114}
4115
4117{
4118 if ( d->geometry )
4119 {
4120 detach();
4121 d->geometry->transform( mtp.transform() );
4122 }
4123}
4124
4126{
4127 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
4128 {
4129 return QgsGeometry();
4130 }
4131
4132 QgsGeos geos( d->geometry.get() );
4133 mLastError.clear();
4134 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError, feedback );
4135 if ( !resultGeom )
4136 {
4137 QgsGeometry result;
4138 result.mLastError = mLastError;
4139 return result;
4140 }
4141 return QgsGeometry( std::move( resultGeom ) );
4142}
4143
4144void QgsGeometry::draw( QPainter &p ) const
4145{
4146 if ( d->geometry )
4147 {
4148 d->geometry->draw( p );
4149 }
4150}
4151
4152static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
4153{
4154 if ( vertexIndex < 0 )
4155 return false; // clearly something wrong
4156
4158 {
4159 partIndex = 0;
4160 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
4161 {
4162 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
4163
4164 // count total number of vertices in the part
4165 int numPoints = 0;
4166 for ( int k = 0; k < part->ringCount(); ++k )
4167 numPoints += part->vertexCount( 0, k );
4168
4169 if ( vertexIndex < numPoints )
4170 {
4171 int nothing;
4172 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
4173 }
4174 vertexIndex -= numPoints;
4175 partIndex++;
4176 }
4177 }
4178 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
4179 {
4180 // PolyhedralSurface: patches are the parts
4181 partIndex = 0;
4182 for ( int i = 0; i < polySurface->numPatches(); ++i )
4183 {
4184 const QgsPolygon *patch = polySurface->patchN( i );
4185 // count total number of vertices in the patch
4186 int numPoints = 0;
4187 for ( int k = 0; k < patch->ringCount(); ++k )
4188 numPoints += patch->vertexCount( 0, k );
4189
4190 if ( vertexIndex < numPoints )
4191 {
4192 int nothing;
4193 return vertexIndexInfo( patch, vertexIndex, nothing, ringIndex, vertex );
4194 }
4195 vertexIndex -= numPoints;
4196 partIndex++;
4197 }
4198 }
4199 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4200 {
4201 const QgsCurve *ring = curvePolygon->exteriorRing();
4202 if ( vertexIndex < ring->numPoints() )
4203 {
4204 partIndex = 0;
4205 ringIndex = 0;
4206 vertex = vertexIndex;
4207 return true;
4208 }
4209 vertexIndex -= ring->numPoints();
4210 ringIndex = 1;
4211 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
4212 {
4213 const QgsCurve *ring = curvePolygon->interiorRing( i );
4214 if ( vertexIndex < ring->numPoints() )
4215 {
4216 partIndex = 0;
4217 vertex = vertexIndex;
4218 return true;
4219 }
4220 vertexIndex -= ring->numPoints();
4221 ringIndex += 1;
4222 }
4223 }
4224 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4225 {
4226 if ( vertexIndex < curve->numPoints() )
4227 {
4228 partIndex = 0;
4229 ringIndex = 0;
4230 vertex = vertexIndex;
4231 return true;
4232 }
4233 }
4234 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
4235 {
4236 if ( vertexIndex == 0 )
4237 {
4238 partIndex = 0;
4239 ringIndex = 0;
4240 vertex = 0;
4241 return true;
4242 }
4243 }
4244
4245 return false;
4246}
4247
4249{
4250 if ( !d->geometry )
4251 {
4252 return false;
4253 }
4254
4255 id.type = Qgis::VertexType::Segment;
4256
4257 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
4258 if ( !res )
4259 return false;
4260
4261 // now let's find out if it is a straight or circular segment
4262 const QgsAbstractGeometry *g = d->geometry.get();
4264 {
4265 g = geomCollection->geometryN( id.part );
4266 }
4267 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
4268 {
4269 g = polySurface->patchN( id.part );
4270 }
4271
4272 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4273 {
4274 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
4275 }
4276
4277 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4278 {
4279 QgsPoint p;
4280 res = curve->pointAt( id.vertex, p, id.type );
4281 if ( !res )
4282 return false;
4283 }
4284
4285 return true;
4286}
4287
4289{
4290 if ( !d->geometry )
4291 {
4292 return -1;
4293 }
4294 return d->geometry->vertexNumberFromVertexId( id );
4295}
4296
4298{
4299 return mLastError;
4300}
4301
4302void QgsGeometry::filterVertices( const std::function<bool( const QgsPoint & )> &filter )
4303{
4304 if ( !d->geometry )
4305 return;
4306
4307 detach();
4308
4309 d->geometry->filterVertices( filter );
4310}
4311
4312void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
4313{
4314 if ( !d->geometry )
4315 return;
4316
4317 detach();
4318
4319 d->geometry->transformVertices( transform );
4320}
4321
4322void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
4323{
4324 output.clear();
4325 for ( const QgsPointXY &p : input )
4326 {
4327 output.append( QgsPoint( p ) );
4328 }
4329}
4330
4331void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
4332{
4333 output.clear();
4334 for ( const QgsPoint &p : input )
4335 {
4336 output.append( QgsPointXY( p.x(), p.y() ) );
4337 }
4338}
4339
4340void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
4341{
4342 output.clear();
4343
4344 auto convertRing = []( const QgsCurve *ring ) -> QgsPolylineXY {
4345 QgsPolylineXY res;
4347 std::unique_ptr< QgsLineString > segmentizedLine;
4348 const QgsLineString *line = nullptr;
4349 if ( doSegmentation )
4350 {
4351 segmentizedLine.reset( ring->curveToLine() );
4352 line = segmentizedLine.get();
4353 }
4354 else
4355 {
4357 if ( !line )
4358 {
4359 return res;
4360 }
4361 }
4362
4363 int nVertices = line->numPoints();
4364 res.resize( nVertices );
4365 QgsPointXY *data = res.data();
4366 const double *xData = line->xData();
4367 const double *yData = line->yData();
4368 for ( int i = 0; i < nVertices; ++i )
4369 {
4370 data->setX( *xData++ );
4371 data->setY( *yData++ );
4372 data++;
4373 }
4374 return res;
4375 };
4376
4377 if ( const QgsCurve *exterior = input.exteriorRing() )
4378 {
4379 output.push_back( convertRing( exterior ) );
4380 }
4381
4382 const int interiorRingCount = input.numInteriorRings();
4383 output.reserve( output.size() + interiorRingCount );
4384 for ( int n = 0; n < interiorRingCount; ++n )
4385 {
4386 output.push_back( convertRing( input.interiorRing( n ) ) );
4387 }
4388}
4389
4391{
4392 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
4393}
4394
4395QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
4396{
4397 std::unique_ptr< QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
4398
4399 if ( polygon.isClosed() )
4400 {
4401 auto poly = std::make_unique< QgsPolygon >();
4402 poly->setExteriorRing( ring.release() );
4403 return QgsGeometry( std::move( poly ) );
4404 }
4405 else
4406 {
4407 return QgsGeometry( std::move( ring ) );
4408 }
4409}
4410
4412{
4414 QgsPolygonXY result;
4415 result << createPolylineFromQPolygonF( polygon );
4416 return result;
4418}
4419
4421{
4422 QgsPolylineXY result;
4423 result.reserve( polygon.count() );
4424 for ( const QPointF &p : polygon )
4425 {
4426 result.append( QgsPointXY( p ) );
4427 }
4428 return result;
4429}
4430
4431bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
4432{
4433 if ( p1.count() != p2.count() )
4434 return false;
4435
4436 for ( int i = 0; i < p1.count(); ++i )
4437 {
4438 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
4439 return false;
4440 }
4441 return true;
4442}
4443
4444bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
4445{
4446 if ( p1.count() != p2.count() )
4447 return false;
4448
4449 for ( int i = 0; i < p1.count(); ++i )
4450 {
4451 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4452 return false;
4453 }
4454 return true;
4455}
4456
4457
4458bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
4459{
4460 if ( p1.count() != p2.count() )
4461 return false;
4462
4463 for ( int i = 0; i < p1.count(); ++i )
4464 {
4465 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4466 return false;
4467 }
4468 return true;
4469}
4470
4471QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4472{
4473 if ( !d->geometry || d->geometry->isEmpty() )
4474 return QgsGeometry();
4475
4476 QgsGeometry geom = *this;
4478 geom = QgsGeometry( d->geometry->segmentize() );
4479
4480 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
4481 {
4484 //can't smooth a point based geometry
4485 return geom;
4486
4488 {
4490 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
4491 }
4492
4494 {
4496
4497 auto resultMultiline = std::make_unique< QgsMultiLineString>();
4498 resultMultiline->reserve( inputMultiLine->numGeometries() );
4499 for ( int i = 0; i < inputMultiLine->numGeometries(); ++i )
4500 {
4501 resultMultiline->addGeometry( smoothLine( *( inputMultiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4502 }
4503 return QgsGeometry( std::move( resultMultiline ) );
4504 }
4505
4507 {
4509 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
4510 }
4511
4513 {
4515
4516 auto resultMultiPoly = std::make_unique< QgsMultiPolygon >();
4517 resultMultiPoly->reserve( inputMultiPoly->numGeometries() );
4518 for ( int i = 0; i < inputMultiPoly->numGeometries(); ++i )
4519 {
4520 resultMultiPoly->addGeometry( smoothPolygon( *( inputMultiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4521 }
4522 return QgsGeometry( std::move( resultMultiPoly ) );
4523 }
4524
4526 default:
4527 return QgsGeometry( *this );
4528 }
4529}
4530
4531std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing )
4532{
4533 auto result = std::make_unique< QgsLineString >( line );
4534 QgsPointSequence outputLine;
4535 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
4536 {
4537 outputLine.resize( 0 );
4538 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
4539 bool skipFirst = false;
4540 bool skipLast = false;
4541 if ( isRing )
4542 {
4543 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
4544 QgsPoint p2 = result->pointN( 0 );
4545 QgsPoint p3 = result->pointN( 1 );
4546 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4547 angle = std::fabs( M_PI - angle );
4548 skipFirst = angle > maxAngleRads;
4549 }
4550 for ( int i = 0; i < result->numPoints() - 1; i++ )
4551 {
4552 QgsPoint p1 = result->pointN( i );
4553 QgsPoint p2 = result->pointN( i + 1 );
4554
4555 double angle = M_PI;
4556 if ( i == 0 && isRing )
4557 {
4558 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
4559 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4560 }
4561 else if ( i < result->numPoints() - 2 )
4562 {
4563 QgsPoint p3 = result->pointN( i + 2 );
4564 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4565 }
4566 else if ( i == result->numPoints() - 2 && isRing )
4567 {
4568 QgsPoint p3 = result->pointN( 1 );
4569 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4570 }
4571
4572 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
4573
4574 // don't apply distance threshold to first or last segment
4575 if ( i == 0 || i >= result->numPoints() - 2 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
4576 {
4577 if ( !isRing )
4578 {
4579 if ( !skipFirst )
4580 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
4581 if ( !skipLast )
4582 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
4583 else
4584 outputLine << p2;
4585 }
4586 else
4587 {
4588 // ring
4589 if ( !skipFirst )
4590 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
4591 else if ( i == 0 )
4592 outputLine << p1;
4593 if ( !skipLast )
4594 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
4595 else
4596 outputLine << p2;
4597 }
4598 }
4599 skipFirst = skipLast;
4600 }
4601
4602 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4603 outputLine << outputLine.at( 0 );
4604
4605 result->setPoints( outputLine );
4606 }
4607 return result;
4608}
4609
4610std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4611{
4612 double maxAngleRads = maxAngle * M_PI / 180.0;
4613 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4614 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4615}
4616
4617std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4618{
4619 double maxAngleRads = maxAngle * M_PI / 180.0;
4620 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4621 auto resultPoly = std::make_unique< QgsPolygon >();
4622
4623 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4624
4625 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4626 {
4627 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4628 }
4629 return resultPoly;
4630}
4631
4632QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4633{
4634 switch ( type() )
4635 {
4637 {
4638 bool srcIsMultipart = isMultipart();
4639
4640 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4641 {
4642 // return a copy of the same geom
4643 return QgsGeometry( *this );
4644 }
4645 if ( destMultipart )
4646 {
4647 // layer is multipart => make a multipoint with a single point
4648 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4649 }
4650 else
4651 {
4652 // destination is singlepart => make a single part if possible
4653 QgsMultiPointXY multiPoint = asMultiPoint();
4654 if ( multiPoint.count() == 1 )
4655 {
4656 return fromPointXY( multiPoint[0] );
4657 }
4658 }
4659 return QgsGeometry();
4660 }
4661
4663 {
4664 // only possible if destination is multipart
4665 if ( !destMultipart )
4666 return QgsGeometry();
4667
4668 // input geometry is multipart
4669 if ( isMultipart() )
4670 {
4671 const QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4672 QgsMultiPointXY multiPoint;
4673 for ( const QgsPolylineXY &l : inputMultiLine )
4674 for ( const QgsPointXY &p : l )
4675 multiPoint << p;
4676 return fromMultiPointXY( multiPoint );
4677 }
4678 // input geometry is not multipart: copy directly the line into a multipoint
4679 else
4680 {
4681 QgsPolylineXY line = asPolyline();
4682 if ( !line.isEmpty() )
4683 return fromMultiPointXY( line );
4684 }
4685 return QgsGeometry();
4686 }
4687
4689 {
4690 // can only transform if destination is multipoint
4691 if ( !destMultipart )
4692 return QgsGeometry();
4693
4694 // input geometry is multipart: make a multipoint from multipolygon
4695 if ( isMultipart() )
4696 {
4697 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4698 QgsMultiPointXY multiPoint;
4699 for ( const QgsPolygonXY &poly : multiPolygon )
4700 for ( const QgsPolylineXY &line : poly )
4701 for ( const QgsPointXY &pt : line )
4702 multiPoint << pt;
4703 return fromMultiPointXY( multiPoint );
4704 }
4705 // input geometry is not multipart: make a multipoint from polygon
4706 else
4707 {
4708 const QgsPolygonXY polygon = asPolygon();
4709 QgsMultiPointXY multiPoint;
4710 for ( const QgsPolylineXY &line : polygon )
4711 for ( const QgsPointXY &pt : line )
4712 multiPoint << pt;
4713 return fromMultiPointXY( multiPoint );
4714 }
4715 }
4716
4717 default:
4718 return QgsGeometry();
4719 }
4720}
4721
4722QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4723{
4724 switch ( type() )
4725 {
4727 {
4728 if ( !isMultipart() )
4729 return QgsGeometry();
4730
4731 QgsMultiPointXY multiPoint = asMultiPoint();
4732 if ( multiPoint.count() < 2 )
4733 return QgsGeometry();
4734
4735 if ( destMultipart )
4736 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4737 else
4738 return fromPolylineXY( multiPoint );
4739 }
4740
4742 {
4743 bool srcIsMultipart = isMultipart();
4744
4745 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4746 {
4747 // return a copy of the same geom
4748 return QgsGeometry( *this );
4749 }
4750 if ( destMultipart )
4751 {
4752 // destination is multipart => makes a multipoint with a single line
4753 QgsPolylineXY line = asPolyline();
4754 if ( !line.isEmpty() )
4755 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4756 }
4757 else
4758 {
4759 // destination is singlepart => make a single part if possible
4760 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4761 if ( inputMultiLine.count() == 1 )
4762 return fromPolylineXY( inputMultiLine[0] );
4763 }
4764 return QgsGeometry();
4765 }
4766
4768 {
4769 // input geometry is multipolygon
4770 if ( isMultipart() )
4771 {
4772 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4773 QgsMultiPolylineXY inputMultiLine;
4774 for ( const QgsPolygonXY &poly : multiPolygon )
4775 for ( const QgsPolylineXY &line : poly )
4776 inputMultiLine << line;
4777
4778 if ( destMultipart )
4779 {
4780 // destination is multipart
4781 return fromMultiPolylineXY( inputMultiLine );
4782 }
4783 else if ( inputMultiLine.count() == 1 )
4784 {
4785 // destination is singlepart => make a single part if possible
4786 return fromPolylineXY( inputMultiLine[0] );
4787 }
4788 }
4789 // input geometry is single polygon
4790 else
4791 {
4792 QgsPolygonXY polygon = asPolygon();
4793 // if polygon has rings
4794 if ( polygon.count() > 1 )
4795 {
4796 // cannot fit a polygon with rings in a single line layer
4797 // TODO: would it be better to remove rings?
4798 if ( destMultipart )
4799 {
4800 const QgsPolygonXY polygon = asPolygon();
4801 QgsMultiPolylineXY inputMultiLine;
4802 inputMultiLine.reserve( polygon.count() );
4803 for ( const QgsPolylineXY &line : polygon )
4804 inputMultiLine << line;
4805 return fromMultiPolylineXY( inputMultiLine );
4806 }
4807 }
4808 // no rings
4809 else if ( polygon.count() == 1 )
4810 {
4811 if ( destMultipart )
4812 {
4813 return fromMultiPolylineXY( polygon );
4814 }
4815 else
4816 {
4817 return fromPolylineXY( polygon[0] );
4818 }
4819 }
4820 }
4821 return QgsGeometry();
4822 }
4823
4824 default:
4825 return QgsGeometry();
4826 }
4827}
4828
4829QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4830{
4831 switch ( type() )
4832 {
4834 {
4835 if ( !isMultipart() )
4836 return QgsGeometry();
4837
4838 QgsMultiPointXY multiPoint = asMultiPoint();
4839 if ( multiPoint.count() < 3 )
4840 return QgsGeometry();
4841
4842 if ( multiPoint.last() != multiPoint.first() )
4843 multiPoint << multiPoint.first();
4844
4845 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4846 if ( destMultipart )
4847 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4848 else
4849 return fromPolygonXY( polygon );
4850 }
4851
4853 {
4854 // input geometry is multiline
4855 if ( isMultipart() )
4856 {
4857 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4858 QgsMultiPolygonXY multiPolygon;
4859 for ( QgsMultiPolylineXY::iterator multiLineIt = inputMultiLine.begin(); multiLineIt != inputMultiLine.end(); ++multiLineIt )
4860 {
4861 // do not create polygon for a 1 segment line
4862 if ( ( *multiLineIt ).count() < 3 )
4863 return QgsGeometry();
4864 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4865 return QgsGeometry();
4866
4867 // add closing node
4868 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4869 *multiLineIt << ( *multiLineIt ).first();
4870 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4871 }
4872 // check that polygons were inserted
4873 if ( !multiPolygon.isEmpty() )
4874 {
4875 if ( destMultipart )
4876 {
4877 return fromMultiPolygonXY( multiPolygon );
4878 }
4879 else if ( multiPolygon.count() == 1 )
4880 {
4881 // destination is singlepart => make a single part if possible
4882 return fromPolygonXY( multiPolygon[0] );
4883 }
4884 }
4885 }
4886 // input geometry is single line
4887 else
4888 {
4889 QgsPolylineXY line = asPolyline();
4890
4891 // do not create polygon for a 1 segment line
4892 if ( line.count() < 3 )
4893 return QgsGeometry();
4894 if ( line.count() == 3 && line.first() == line.last() )
4895 return QgsGeometry();
4896
4897 // add closing node
4898 if ( line.first() != line.last() )
4899 line << line.first();
4900
4901 // destination is multipart
4902 if ( destMultipart )
4903 {
4904 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4905 }
4906 else
4907 {
4908 return fromPolygonXY( QgsPolygonXY() << line );
4909 }
4910 }
4911 return QgsGeometry();
4912 }
4913
4915 {
4916 bool srcIsMultipart = isMultipart();
4917
4918 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4919 {
4920 // return a copy of the same geom
4921 return QgsGeometry( *this );
4922 }
4923 if ( destMultipart )
4924 {
4925 // destination is multipart => makes a multipoint with a single polygon
4926 QgsPolygonXY polygon = asPolygon();
4927 if ( !polygon.isEmpty() )
4928 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4929 }
4930 else
4931 {
4932 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4933 if ( multiPolygon.count() == 1 )
4934 {
4935 // destination is singlepart => make a single part if possible
4936 return fromPolygonXY( multiPolygon[0] );
4937 }
4938 }
4939 return QgsGeometry();
4940 }
4941
4942 default:
4943 return QgsGeometry();
4944 }
4945}
4946
4948{
4949 return new QgsGeos( geometry, precision, flags );
4950}
4951
4952QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
4953{
4954 out << geometry.asWkb();
4955 return out;
4956}
4957
4958QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4959{
4960 QByteArray byteArray;
4961 in >> byteArray;
4962 if ( byteArray.isEmpty() )
4963 {
4964 geometry.set( nullptr );
4965 return in;
4966 }
4967
4968 geometry.fromWkb( byteArray );
4969 return in;
4970}
4971
4972
4974{
4975 return mMessage;
4976}
4977
4979{
4980 return mLocation;
4981}
4982
4984{
4985 return mHasLocation;
4986}
4987
4988QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int vertexIndex, double distance1, double distance2, int segments ) const
4989{
4990 QgsDebugMsgLevel( u"%1 starts: %2"_s.arg( qgsEnumValueToKey( op ) ).arg( asWkt( 2 ) ), 3 );
4991 if ( isNull() )
4992 {
4993 mLastError = u"Operation '%1' needs non-null geometry."_s.arg( qgsEnumValueToKey( op ) );
4994 return QgsGeometry();
4995 }
4996
4997 QgsCurve *curve = nullptr;
4998
4999 int modifiedPart = -1;
5000 int modifiedRing = -1;
5001 QgsVertexId vertexId;
5002 if ( !vertexIdFromVertexNr( vertexIndex, vertexId ) )
5003 {
5004 mLastError = u"Invalid vertex index"_s;
5005 return QgsGeometry();
5006 }
5007 int resolvedVertexIndex = vertexId.vertex;
5008 QgsMultiLineString *inputMultiLine = nullptr;
5009 QgsMultiPolygon *inputMultiPoly = nullptr;
5011
5012 if ( geomType == Qgis::GeometryType::Line )
5013 {
5014 if ( isMultipart() )
5015 {
5016 modifiedPart = vertexId.part;
5017
5018 inputMultiLine = qgsgeometry_cast<QgsMultiLineString *>( d->geometry.get() );
5019 curve = dynamic_cast<QgsCurve *>( inputMultiLine->lineStringN( modifiedPart ) );
5020 }
5021 else
5022 {
5023 curve = dynamic_cast<QgsCurve *>( d->geometry.get() );
5024 }
5025 }
5026 else if ( geomType == Qgis::GeometryType::Polygon )
5027 {
5028 QgsPolygon *poly = nullptr;
5029 if ( isMultipart() )
5030 {
5031 modifiedPart = vertexId.part;
5032 // get part, get ring
5033 inputMultiPoly = qgsgeometry_cast<QgsMultiPolygon *>( d->geometry.get() );
5034 poly = inputMultiPoly->polygonN( modifiedPart );
5035 }
5036 else
5037 {
5038 poly = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
5039 }
5040 if ( !poly )
5041 {
5042 mLastError = u"Could not get polygon geometry."_s;
5043 return QgsGeometry();
5044 }
5045
5046 // if has rings
5047 modifiedRing = vertexId.ring;
5048 if ( modifiedRing == 0 )
5049 curve = qgsgeometry_cast<QgsCurve *>( poly->exteriorRing() );
5050 else
5051 curve = qgsgeometry_cast<QgsCurve *>( poly->interiorRing( modifiedRing - 1 ) );
5052 }
5053 else
5054 curve = nullptr;
5055
5056 if ( !curve )
5057 {
5058 mLastError = u"Operation '%1' needs curve geometry."_s.arg( qgsEnumValueToKey( op ) );
5059 return QgsGeometry();
5060 }
5061
5062 std::unique_ptr<QgsAbstractGeometry> result;
5063 try
5064 {
5066 result = QgsGeometryUtils::chamferVertex( curve, resolvedVertexIndex, distance1, distance2 );
5067 else
5068 result = QgsGeometryUtils::filletVertex( curve, resolvedVertexIndex, distance1, segments );
5069 }
5070 catch ( QgsInvalidArgumentException &e )
5071 {
5072 mLastError = u"%1 Requested vertex: %2 was resolved as: [part: %3, ring: %4, vertex: %5]"_s //
5073 .arg( e.what() )
5074 .arg( vertexIndex )
5075 .arg( modifiedPart )
5076 .arg( modifiedRing )
5077 .arg( resolvedVertexIndex );
5078 return QgsGeometry();
5079 }
5080
5081 if ( !result )
5082 {
5083 mLastError = u"Operation '%1' generates a null geometry."_s.arg( qgsEnumValueToKey( op ) );
5084 return QgsGeometry();
5085 }
5086
5087 if ( result->isEmpty() )
5088 return QgsGeometry( std::move( result ) );
5089
5090 // insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon
5091 auto updatePolygon = []( const QgsPolygon *inputPoly, QgsAbstractGeometry *result, int modifiedRing ) -> std::unique_ptr<QgsPolygon> {
5092 auto newPoly = std::make_unique<QgsPolygon>();
5093 for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex )
5094 {
5095 if ( ringIndex == modifiedRing )
5096 {
5097 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
5098 {
5099 if ( ringIndex == 0 && resPartIte == result->parts_begin() )
5100 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
5101 else
5102 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
5103 }
5104 }
5105 else
5106 {
5107 if ( ringIndex == 0 )
5108 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->exteriorRing()->clone() ) );
5109 else
5110 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->interiorRing( ringIndex - 1 )->clone() ) );
5111 }
5112 }
5113 return newPoly;
5114 };
5115
5116 std::unique_ptr<QgsAbstractGeometry> finalGeom;
5117 if ( geomType == Qgis::GeometryType::Line )
5118 {
5119 if ( modifiedPart >= 0 )
5120 {
5121 auto newMultiLine = std::make_unique<QgsMultiLineString>();
5122 int partIndex = 0;
5123 for ( QgsMultiLineString::part_iterator partIte = inputMultiLine->parts_begin(); partIte != inputMultiLine->parts_end(); ++partIte )
5124 {
5125 if ( partIndex == modifiedPart )
5126 {
5127 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
5128 {
5129 newMultiLine->addGeometry( ( *resPartIte )->clone() );
5130 }
5131 }
5132 else
5133 {
5134 newMultiLine->addGeometry( ( *partIte )->clone() );
5135 }
5136 partIndex++;
5137 }
5138 finalGeom = std::move( newMultiLine );
5139 }
5140 else
5141 {
5142 // resultGeom is already the correct result!
5143 finalGeom = std::move( result );
5144 }
5145 }
5146 else
5147 {
5148 // geomType == Qgis::GeometryType::Polygon
5149 if ( modifiedPart >= 0 )
5150 {
5151 auto newMultiPoly = std::make_unique<QgsMultiPolygon>();
5152 int partIndex = 0;
5153 for ( QgsAbstractGeometry::part_iterator partIte = inputMultiPoly->parts_begin(); partIte != inputMultiPoly->parts_end(); ++partIte )
5154 {
5155 if ( partIndex == modifiedPart )
5156 {
5157 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( *partIte ), result.get(), modifiedRing );
5158 newMultiPoly->addGeometry( newPoly.release() );
5159 }
5160 else
5161 {
5162 newMultiPoly->addGeometry( ( *partIte )->clone() );
5163 }
5164 partIndex++;
5165 }
5166 finalGeom.reset( dynamic_cast<QgsAbstractGeometry *>( newMultiPoly.release() ) );
5167 }
5168 else
5169 {
5170 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( d->geometry.get() ), result.get(), modifiedRing );
5171 finalGeom = std::move( newPoly );
5172 }
5173 }
5174
5175 QgsGeometry finalResult( std::move( finalGeom ) );
5176
5177 QgsDebugMsgLevel( u"Final result Wkt: %1"_s.arg( finalResult.asWkt( 2 ) ), 3 );
5178
5179 return finalResult;
5180}
5181
5182
5183QgsGeometry QgsGeometry::chamfer( int vertexIndex, double distance1, double distance2 ) const
5184{
5185 return doChamferFillet( ChamferFilletOperationType::Chamfer, vertexIndex, distance1, distance2, 0 );
5186}
5187
5188QgsGeometry QgsGeometry::fillet( int vertexIndex, double radius, int segments ) const
5189{
5190 return doChamferFillet( ChamferFilletOperationType::Fillet, vertexIndex, radius, 0.0, segments );
5191}
5192
5193QgsGeometry QgsGeometry::chamfer( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2 )
5194{
5195 std::unique_ptr<QgsLineString> result( QgsGeometryUtils::createChamferGeometry( segment1Start, segment1End, segment2Start, segment2End, distance1, distance2 ) );
5196
5197 if ( !result )
5198 {
5199 return QgsGeometry();
5200 }
5201
5202 return QgsGeometry( std::move( result ) );
5203}
5204
5205QgsGeometry QgsGeometry::fillet( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments )
5206{
5207 std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::createFilletGeometry( segment1Start, segment1End, segment2Start, segment2End, radius, segments ) );
5208
5209 if ( !result )
5210 {
5211 return QgsGeometry();
5212 }
5213
5214 return QgsGeometry( std::move( result ) );
5215}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2299
@ GEOS
Use GEOS implementation.
Definition qgis.h:2301
@ QGIS
Use internal implementation.
Definition qgis.h:2300
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2222
BufferSide
Side of line to buffer.
Definition qgis.h:2248
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3503
AngularDirection
Angular directions.
Definition qgis.h:3644
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3647
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2192
@ AddPartSelectedGeometryNotFound
The selected geometry cannot be found.
Definition qgis.h:2202
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Definition qgis.h:2196
@ Success
Operation succeeded.
Definition qgis.h:2193
@ SelectionIsEmpty
No features were selected.
Definition qgis.h:2197
@ GeometryTypeHasChanged
Operation has changed geometry type.
Definition qgis.h:2211
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
Definition qgis.h:2208
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint).
Definition qgis.h:2207
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition qgis.h:2203
@ AddRingNotClosed
The input ring is not closed.
Definition qgis.h:2205
@ SelectionIsGreaterThanOne
More than one features were selected.
Definition qgis.h:2198
@ SplitCannotSplitPoint
Cannot split points.
Definition qgis.h:2210
@ GeometryEngineError
Geometry engine misses a method implemented or an error occurred in the geometry engine.
Definition qgis.h:2199
@ NothingHappened
Nothing happened, without any error.
Definition qgis.h:2194
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition qgis.h:2195
@ LayerNotEditable
Cannot edit layer.
Definition qgis.h:2200
@ AddRingNotValid
The input ring is not valid.
Definition qgis.h:2206
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2226
@ Segment
The actual start or end point of a segment.
Definition qgis.h:3278
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:2235
@ QgisInternal
Use internal QgsGeometryValidator method.
Definition qgis.h:2236
@ Sfcgal
Use SFCGAL validation methods. Only available for QGIS builds with SFCGAL support enabled.
Definition qgis.h:2238
@ Geos
Use GEOS validation methods.
Definition qgis.h:2237
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
Definition qgis.h:2322
GeoJsonProfile
GeoJson export Profile according to OGC Features and Geometries JSON - Part 1: Core https://docs....
Definition qgis.h:5062
@ Rfc7946
GeoJson profile compliant with RFC7946 standard "http://www.opengis.net/def/profile/OGC/0/rfc7946".
Definition qgis.h:5064
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
JoinStyle
Join styles for buffers.
Definition qgis.h:2273
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2260
CoverageValidityResult
Coverage validity results.
Definition qgis.h:2331
@ Error
An exception occurred while determining validity.
Definition qgis.h:2334
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3488
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2344
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ CompoundCurve
CompoundCurve.
Definition qgis.h:305
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ TIN
TIN.
Definition qgis.h:310
@ MultiPoint
MultiPoint.
Definition qgis.h:300
@ Polygon
Polygon.
Definition qgis.h:298
@ MultiPolygon
MultiPolygon.
Definition qgis.h:302
@ Triangle
Triangle.
Definition qgis.h:299
@ NoGeometry
No geometry.
Definition qgis.h:312
@ MultiLineString
MultiLineString.
Definition qgis.h:301
@ Unknown
Unknown.
Definition qgis.h:295
@ CircularString
CircularString.
Definition qgis.h:304
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ MultiCurve
MultiCurve.
Definition qgis.h:307
@ CurvePolygon
CurvePolygon.
Definition qgis.h:306
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:309
@ MultiSurface
MultiSurface.
Definition qgis.h:308
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2862
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.
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 deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
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 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.
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.
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:175
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:587
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Buffers the geometry.
Definition qgsgeos.cpp:2106
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:796
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:844
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:772
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:820
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:3414
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:749
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:112
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:8016
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7654
#define BUILTIN_UNREACHABLE
Definition qgis.h:8052
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8015
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7418
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)
QDataStream & operator<<(QDataStream &out, const QgsGeometry &geometry)
Writes the geometry to stream out. QGIS version compatibility is not guaranteed.
std::unique_ptr< QgsLineString > smoothCurve(const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing)
QDataStream & operator>>(QDataStream &in, QgsGeometry &geometry)
Reads a geometry from stream in into geometry. QGIS version compatibility is not guaranteed.
QCache< QString, QgsGeometry > WktCache
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item.
Definition qgsgeometry.h:92
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