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