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