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