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