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