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