QGIS API Documentation 3.99.0-Master (d270888f95f)
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 {
1102 // NOLINTBEGIN(bugprone-branch-clone)
1104 {
1106 reset( std::make_unique< QgsMultiPoint >() );
1107 break;
1109 reset( std::make_unique< QgsMultiLineString >() );
1110 break;
1113 reset( std::make_unique< QgsMultiPolygon >() );
1114 break;
1116 reset( std::make_unique< QgsMultiSurface >() );
1117 break;
1120 reset( std::make_unique< QgsMultiCurve >() );
1121 break;
1123 reset( std::make_unique< QgsPolyhedralSurface >() );
1124 break;
1125 case Qgis::WkbType::TIN:
1126 reset( std::make_unique< QgsTriangulatedSurface >() );
1127 break;
1128 default:
1129 reset( nullptr );
1131 // NOLINTEND(bugprone-branch-clone)
1132 }
1133 }
1134 else
1135 {
1136 detach();
1137 // For TIN and PolyhedralSurface, they already support multiple patches, no conversion needed
1138 const Qgis::WkbType flatType = QgsWkbTypes::flatType( d->geometry->wkbType() );
1139 if ( flatType != Qgis::WkbType::TIN && flatType != Qgis::WkbType::PolyhedralSurface )
1140 {
1142 }
1143 }
1144
1145 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
1146}
1147
1149{
1150 if ( !d->geometry )
1151 {
1153 }
1154 if ( newPart.isNull() || !newPart.d->geometry )
1155 {
1157 }
1158
1159 return addPartV2( newPart.d->geometry->clone() );
1160}
1161
1162QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
1163{
1164 if ( !d->geometry || type() != Qgis::GeometryType::Polygon )
1165 {
1166 return QgsGeometry();
1167 }
1168
1169 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
1170 {
1171 const QVector<QgsGeometry> parts = asGeometryCollection();
1172 QVector<QgsGeometry> results;
1173 results.reserve( parts.count() );
1174 for ( const QgsGeometry &part : parts )
1175 {
1176 QgsGeometry result = part.removeInteriorRings( minimumRingArea );
1177 if ( !result.isNull() )
1178 results << result;
1179 }
1180 if ( results.isEmpty() )
1181 return QgsGeometry();
1182
1183 QgsGeometry first = results.takeAt( 0 );
1184 for ( const QgsGeometry &result : std::as_const( results ) )
1185 {
1186 first.addPart( result );
1187 }
1188 return first;
1189 }
1190 else
1191 {
1192 std::unique_ptr< QgsCurvePolygon > newPoly( static_cast< QgsCurvePolygon * >( d->geometry->clone() ) );
1193 newPoly->removeInteriorRings( minimumRingArea );
1194 return QgsGeometry( std::move( newPoly ) );
1195 }
1196}
1197
1198Qgis::GeometryOperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
1199{
1200 if ( !d->geometry )
1201 {
1203 }
1204
1205 detach();
1206
1207 d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
1209}
1210
1212{
1213 if ( !d->geometry )
1214 {
1216 }
1217
1218 detach();
1219
1220 QTransform t = QTransform::fromTranslate( center.x(), center.y() );
1221 t.rotate( -rotation );
1222 t.translate( -center.x(), -center.y() );
1223 d->geometry->transform( t );
1225}
1226
1227Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature )
1228{
1229 QgsPointSequence split, topology;
1230 convertPointList( splitLine, split );
1231 convertPointList( topologyTestPoints, topology );
1232 Qgis::GeometryOperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
1233 convertPointList( topology, topologyTestPoints );
1234 return result;
1235}
1236Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest )
1237{
1238 if ( !d->geometry )
1239 {
1241 }
1242
1243 // We're trying adding the split line's vertices to the geometry so that
1244 // snap to segment always produces a valid split (see https://github.com/qgis/QGIS/issues/29270)
1245 QgsGeometry tmpGeom( *this );
1246 for ( const QgsPoint &v : splitLine )
1247 {
1248 tmpGeom.addTopologicalPoint( v );
1249 }
1250
1251 QVector<QgsGeometry > newGeoms;
1252 QgsLineString splitLineString( splitLine );
1253
1254 QgsGeos geos( tmpGeom.get() );
1255 mLastError.clear();
1256 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
1257
1258 if ( result == QgsGeometryEngine::Success )
1259 {
1260 if ( splitFeature )
1261 *this = newGeoms.takeAt( 0 );
1262 newGeometries = newGeoms;
1263 }
1264
1265 switch ( result )
1266 {
1281 //default: do not implement default to handle properly all cases
1282 }
1283
1284 // this should never be reached
1285 Q_ASSERT( false );
1287}
1288
1289Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature )
1290{
1291 std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
1292 QgsPointSequence points;
1293 segmentizedLine->points( points );
1294 Qgis::GeometryOperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
1295
1297 {
1298 if ( preserveCircular )
1299 {
1300 for ( int i = 0; i < newGeometries.count(); ++i )
1301 newGeometries[i] = newGeometries[i].convertToCurves();
1302 *this = convertToCurves();
1303 }
1304 }
1305
1306 return result;
1307}
1308
1310{
1311 if ( !d->geometry )
1312 {
1314 }
1315
1316 // We're trying adding the reshape line's vertices to the geometry so that
1317 // snap to segment always produces a valid reshape
1318 QgsPointSequence reshapePoints;
1319 reshapeLineString.points( reshapePoints );
1320 QgsGeometry tmpGeom( *this );
1321 for ( const QgsPoint &v : std::as_const( reshapePoints ) )
1322 {
1323 tmpGeom.addTopologicalPoint( v );
1324 }
1325
1326 QgsGeos geos( tmpGeom.get() );
1328 mLastError.clear();
1329 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1330 if ( errorCode == QgsGeometryEngine::Success && geom )
1331 {
1332 reset( std::move( geom ) );
1334 }
1335
1336 switch ( errorCode )
1337 {
1348 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1352 }
1353
1354 // should not be reached
1356}
1357
1359{
1360 if ( !d->geometry || !other.d->geometry )
1361 {
1362 return 0;
1363 }
1364
1365 QgsGeos geos( d->geometry.get() );
1366
1367 mLastError.clear();
1368 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1369 if ( !diffGeom )
1370 {
1371 return 1;
1372 }
1373
1374 reset( std::move( diffGeom ) );
1375 return 0;
1376}
1377
1379{
1380 if ( !d->geometry || other.isNull() )
1381 {
1382 return QgsGeometry();
1383 }
1384
1385 QgsGeos geos( d->geometry.get() );
1386
1387 mLastError.clear();
1388 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1389 if ( !diffGeom )
1390 {
1391 QgsGeometry result;
1392 result.mLastError = mLastError;
1393 return result;
1394 }
1395
1396 return QgsGeometry( diffGeom.release() );
1397}
1398
1400{
1401 if ( d->geometry )
1402 {
1403 return d->geometry->boundingBox();
1404 }
1405 return QgsRectangle();
1406}
1407
1409{
1410 if ( d->geometry )
1411 {
1412 return d->geometry->boundingBox3D();
1413 }
1414 return QgsBox3D();
1415}
1416
1417
1418QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1419{
1420 mLastError.clear();
1421
1422 if ( isNull() )
1423 return QgsGeometry();
1424
1425 if ( type() == Qgis::GeometryType::Point && d->geometry->partCount() == 1 )
1426 {
1427 area = 0;
1428 angle = 0;
1429 width = 0;
1430 height = 0;
1431 return QgsGeometry::fromRect( d->geometry->boundingBox() );
1432 }
1433
1434 QgsInternalGeometryEngine engine( *this );
1435 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1436 if ( res.isNull() )
1437 mLastError = engine.lastError();
1438 return res;
1439}
1440
1442{
1443 double area, angle, width, height;
1444 return orientedMinimumBoundingBox( area, angle, width, height );
1445}
1446
1447static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1448{
1449 auto l_boundary = boundary.length();
1450 QgsCircle circ_mec;
1451 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1452 {
1453 switch ( l_boundary )
1454 {
1455 case 0:
1456 circ_mec = QgsCircle();
1457 break;
1458 case 1:
1459 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1460 boundary.pop_back();
1461 break;
1462 case 2:
1463 {
1464 QgsPointXY p1 = boundary.last();
1465 boundary.pop_back();
1466 QgsPointXY p2 = boundary.last();
1467 boundary.pop_back();
1468 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1469 }
1470 break;
1471 default:
1472 QgsPoint p1( boundary.at( 0 ) );
1473 QgsPoint p2( boundary.at( 1 ) );
1474 QgsPoint p3( boundary.at( 2 ) );
1475 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1476 break;
1477 }
1478 return circ_mec;
1479 }
1480 else
1481 {
1482 QgsPointXY pxy = points.last();
1483 points.pop_back();
1484 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1485 QgsPoint p( pxy );
1486 if ( !circ_mec.contains( p ) )
1487 {
1488 boundary.append( pxy );
1489 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1490 }
1491 }
1492 return circ_mec;
1493}
1494
1495QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1496{
1497 center = QgsPointXY();
1498 radius = 0;
1499
1500 if ( isEmpty() )
1501 {
1502 return QgsGeometry();
1503 }
1504
1505 /* optimization */
1506 QgsGeometry hull = convexHull();
1507 if ( hull.isNull() )
1508 return QgsGeometry();
1509
1510 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1512
1513 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1514 center = QgsPointXY( circ.center() );
1515 radius = circ.radius();
1516 QgsGeometry geom;
1517 geom.set( circ.toPolygon( segments ) );
1518 return geom;
1519
1520}
1521
1523{
1524 QgsPointXY center;
1525 double radius;
1526 return minimalEnclosingCircle( center, radius, segments );
1527
1528}
1529
1530QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1531{
1532 QgsInternalGeometryEngine engine( *this );
1533
1534 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1535}
1536
1537QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1538{
1539 QgsInternalGeometryEngine engine( *this );
1540 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1541}
1542
1543QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1544{
1545 QgsInternalGeometryEngine engine( *this );
1546 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1547}
1548
1549QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1550{
1551 QgsInternalGeometryEngine engine( *this );
1552 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1553}
1554
1555QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1556{
1557 QgsInternalGeometryEngine engine( *this );
1558 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1559}
1560
1561QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1562{
1563 QgsInternalGeometryEngine engine( *this );
1564 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1565}
1566
1567QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1568{
1569 QgsInternalGeometryEngine engine( *this );
1570 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1571}
1572
1573QgsGeometry QgsGeometry::applyDashPattern( const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset ) const
1574{
1575 QgsInternalGeometryEngine engine( *this );
1576 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1577}
1578
1579QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1580{
1581 if ( !d->geometry )
1582 {
1583 return QgsGeometry();
1584 }
1585 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1586}
1587
1588bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1589{
1590 if ( !d->geometry )
1591 return false;
1592
1593 detach();
1594 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1595}
1596
1598{
1599 // fast case, check bounding boxes
1600 if ( !boundingBoxIntersects( r ) )
1601 return false;
1602
1603 const Qgis::WkbType flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1604 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1605 if ( flatType == Qgis::WkbType::Point )
1606 {
1607 return true;
1608 }
1609
1610 // Workaround for issue issue GH #51429
1611 // in case of multi polygon, intersection with an empty rect fails
1612 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1613 {
1614 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1615 return contains( QgsGeometry::fromPointXY( center ) );
1616 }
1617
1618 QgsGeometry g = fromRect( r );
1619 return intersects( g );
1620}
1621
1622bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1623{
1624 if ( !d->geometry || geometry.isNull() )
1625 {
1626 return false;
1627 }
1628
1629 QgsGeos geos( d->geometry.get() );
1630 mLastError.clear();
1631 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1632}
1633
1635{
1636 if ( !d->geometry )
1637 {
1638 return false;
1639 }
1640
1641 return d->geometry->boundingBoxIntersects( rectangle );
1642}
1643
1645{
1646 if ( !d->geometry || geometry.isNull() )
1647 {
1648 return false;
1649 }
1650
1651 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1652}
1653
1654bool QgsGeometry::contains( const QgsPointXY *p ) const
1655{
1656 if ( !d->geometry || !p )
1657 {
1658 return false;
1659 }
1660
1661 QgsGeos geos( d->geometry.get() );
1662 mLastError.clear();
1663 return geos.contains( p->x(), p->y(), &mLastError );
1664}
1665
1666bool QgsGeometry::contains( double x, double y ) const
1667{
1668 if ( !d->geometry )
1669 {
1670 return false;
1671 }
1672
1673 QgsGeos geos( d->geometry.get() );
1674 mLastError.clear();
1675 return geos.contains( x, y, &mLastError );
1676}
1677
1678bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1679{
1680 if ( !d->geometry || geometry.isNull() )
1681 {
1682 return false;
1683 }
1684
1685 QgsGeos geos( d->geometry.get() );
1686 mLastError.clear();
1687 return geos.contains( geometry.d->geometry.get(), &mLastError );
1688}
1689
1690bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1691{
1692 if ( !d->geometry || geometry.isNull() )
1693 {
1694 return false;
1695 }
1696
1697 QgsGeos geos( d->geometry.get() );
1698 mLastError.clear();
1699 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1700}
1701
1702bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1703{
1704 if ( !d->geometry || geometry.isNull() )
1705 {
1706 return false;
1707 }
1708
1709 // fast check - are they shared copies of the same underlying geometry?
1710 if ( d == geometry.d )
1711 return true;
1712
1713 // fast check - distinct geometry types?
1714 if ( type() != geometry.type() )
1715 return false;
1716
1717 // slower check - actually test the geometries
1718 return *d->geometry == *geometry.d->geometry;
1719}
1720
1721bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1722{
1723 if ( !d->geometry || geometry.isNull() )
1724 {
1725 return false;
1726 }
1727
1728 QgsGeos geos( d->geometry.get() );
1729 mLastError.clear();
1730 return geos.touches( geometry.d->geometry.get(), &mLastError );
1731}
1732
1733bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1734{
1735 if ( !d->geometry || geometry.isNull() )
1736 {
1737 return false;
1738 }
1739
1740 QgsGeos geos( d->geometry.get() );
1741 mLastError.clear();
1742 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1743}
1744
1745bool QgsGeometry::within( const QgsGeometry &geometry ) const
1746{
1747 if ( !d->geometry || geometry.isNull() )
1748 {
1749 return false;
1750 }
1751
1752 QgsGeos geos( d->geometry.get() );
1753 mLastError.clear();
1754 return geos.within( geometry.d->geometry.get(), &mLastError );
1755}
1756
1757bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1758{
1759 if ( !d->geometry || geometry.isNull() )
1760 {
1761 return false;
1762 }
1763
1764 QgsGeos geos( d->geometry.get() );
1765 mLastError.clear();
1766 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1767}
1768
1769QString QgsGeometry::asWkt( int precision ) const
1770{
1771 if ( !d->geometry )
1772 {
1773 return QString();
1774 }
1775 return d->geometry->asWkt( precision );
1776}
1777
1778QString QgsGeometry::asJson( int precision ) const
1779{
1780 return QString::fromStdString( asJsonObject( precision ).dump() );
1781}
1782
1783json QgsGeometry::asJsonObject( int precision ) const
1784{
1785 if ( !d->geometry )
1786 {
1787 return nullptr;
1788 }
1789 return d->geometry->asJsonObject( precision );
1790
1791}
1792
1793QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM, bool avoidDuplicates ) const
1794{
1795 mLastError.clear();
1796 QVector< QgsGeometry > res;
1797 if ( isNull() )
1798 return res;
1799
1800 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1801 {
1802 res << *this;
1803 return res;
1804 }
1805
1807 {
1808 return res;
1809 }
1810
1811 QgsGeometry newGeom = *this;
1812
1813 // Curved -> straight
1815 {
1816 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1817 }
1818
1819 // Handle NurbsCurve: if target is curved but NOT NurbsCurve, and source contains NurbsCurve,
1820 // we need to segmentize the NURBS parts first
1822 {
1823 // Check if geometry contains NurbsCurve that needs conversion
1824 bool hasNurbs = false;
1825 if ( QgsWkbTypes::isNurbsType( newGeom.wkbType() ) )
1826 {
1827 hasNurbs = true;
1828 }
1829 else if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( newGeom.constGet() ) )
1830 {
1831 for ( int i = 0; i < collection->numGeometries(); ++i )
1832 {
1833 if ( QgsWkbTypes::isNurbsType( collection->geometryN( i )->wkbType() ) )
1834 {
1835 hasNurbs = true;
1836 break;
1837 }
1838 }
1839 }
1840 else if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( newGeom.constGet() ) )
1841 {
1842 if ( cp->exteriorRing() && QgsWkbTypes::isNurbsType( cp->exteriorRing()->wkbType() ) )
1843 hasNurbs = true;
1844 for ( int i = 0; !hasNurbs && i < cp->numInteriorRings(); ++i )
1845 {
1846 if ( QgsWkbTypes::isNurbsType( cp->interiorRing( i )->wkbType() ) )
1847 hasNurbs = true;
1848 }
1849 }
1851 {
1852 for ( int i = 0; i < cc->nCurves(); ++i )
1853 {
1854 if ( QgsWkbTypes::isNurbsType( cc->curveAt( i )->wkbType() ) )
1855 {
1856 hasNurbs = true;
1857 break;
1858 }
1859 }
1860 }
1861
1862 if ( hasNurbs )
1863 {
1864 // Segmentize to remove NURBS, then we'll convert back to curve type below
1865 newGeom = QgsGeometry( newGeom.constGet()->segmentize() );
1866 }
1867 }
1868
1869 // polygon -> line
1871 newGeom.type() == Qgis::GeometryType::Polygon )
1872 {
1873 // boundary gives us a (multi)line string of exterior + interior rings
1874 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1875 }
1876 // line -> polygon
1878 newGeom.type() == Qgis::GeometryType::Line )
1879 {
1880 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1881 const QgsGeometry source = newGeom;
1882 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1883 {
1884 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1885 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1886 {
1888 {
1889 auto cp = std::make_unique< QgsCurvePolygon >();
1890 cp->setExteriorRing( curve );
1891 ( void )exterior.release();
1892 gc->addGeometry( cp.release() );
1893 }
1894 else
1895 {
1896 auto p = std::make_unique< QgsPolygon >();
1897 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1898 ( void )exterior.release();
1899 gc->addGeometry( p.release() );
1900 }
1901 }
1902 }
1903 newGeom = QgsGeometry( std::move( gc ) );
1904 }
1905
1906 // line/polygon -> points
1908 ( newGeom.type() == Qgis::GeometryType::Line ||
1909 newGeom.type() == Qgis::GeometryType::Polygon ) )
1910 {
1911 // lines/polygons to a point layer, extract all vertices
1912 auto mp = std::make_unique< QgsMultiPoint >();
1913 const QgsGeometry source = newGeom;
1914 QSet< QgsPoint > added;
1915 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1916 {
1917 if ( avoidDuplicates && added.contains( *vertex ) )
1918 continue; // avoid duplicate points, e.g. start/end of rings
1919 mp->addGeometry( ( *vertex ).clone() );
1920 added.insert( *vertex );
1921 }
1922 newGeom = QgsGeometry( std::move( mp ) );
1923 }
1924
1925 //(Multi)Polygon to PolyhedralSurface
1928 {
1929 auto polySurface = std::make_unique< QgsPolyhedralSurface >();
1930 const QgsGeometry source = newGeom;
1931 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1932 {
1933 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1934 {
1935 polySurface->addPatch( polygon->clone() );
1936 }
1937 }
1938 newGeom = QgsGeometry( std::move( polySurface ) );
1939 }
1940
1941 //(Multi)Polygon/Triangle to TIN
1945 {
1946 auto tin = std::make_unique< QgsTriangulatedSurface >();
1947 const QgsGeometry source = newGeom;
1948 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1949 {
1950 if ( const QgsTriangle *triangle = qgsgeometry_cast< const QgsTriangle * >( *part ) )
1951 {
1952 tin->addPatch( triangle->clone() );
1953 }
1954 else if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1955 {
1956 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
1957 if ( polygon->exteriorRing() )
1958 {
1959 const int numPoints = polygon->exteriorRing()->numPoints();
1960 if ( numPoints != 4 )
1961 {
1962 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
1963 return res;
1964 }
1965 auto triangle = std::make_unique< QgsTriangle >();
1966 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
1967 tin->addPatch( triangle.release() );
1968 }
1969 }
1970 }
1971 newGeom = QgsGeometry( std::move( tin ) );
1972 }
1973
1974 // PolyhedralSurface/TIN to (Multi)Polygon
1978 {
1979 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
1981 {
1982 for ( int i = 0; i < polySurface->numPatches(); ++i )
1983 {
1984 const QgsPolygon *patch = polySurface->patchN( i );
1985 auto polygon = std::make_unique< QgsPolygon >();
1986 polygon->setExteriorRing( patch->exteriorRing()->clone() );
1987 for ( int j = 0; j < patch->numInteriorRings(); ++j )
1988 {
1989 polygon->addInteriorRing( patch->interiorRing( j )->clone() );
1990 }
1991 multiPolygon->addGeometry( polygon.release() );
1992 }
1993 }
1994 newGeom = QgsGeometry( std::move( multiPolygon ) );
1995 }
1996
1997 // Polygon -> Triangle
2000 {
2001 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( newGeom.constGet() ) )
2002 {
2003 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
2004 if ( polygon->exteriorRing() )
2005 {
2006 const int numPoints = polygon->exteriorRing()->numPoints();
2007 if ( numPoints != 4 )
2008 {
2009 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
2010 return res;
2011 }
2012 auto triangle = std::make_unique< QgsTriangle >();
2013 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
2014 newGeom = QgsGeometry( std::move( triangle ) );
2015 }
2016 }
2017 }
2018
2019
2020 // Single -> multi
2021 if ( QgsWkbTypes::isMultiType( type ) && ! newGeom.isMultipart( ) )
2022 {
2023 newGeom.convertToMultiType();
2024 }
2025 // Drop Z/M
2026 if ( newGeom.constGet()->is3D() && ! QgsWkbTypes::hasZ( type ) )
2027 {
2028 newGeom.get()->dropZValue();
2029 }
2030 if ( newGeom.constGet()->isMeasure() && ! QgsWkbTypes::hasM( type ) )
2031 {
2032 newGeom.get()->dropMValue();
2033 }
2034 // Add Z/M back, set to 0
2035 if ( ! newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
2036 {
2037 newGeom.get()->addZValue( defaultZ );
2038 }
2039 if ( ! newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
2040 {
2041 newGeom.get()->addMValue( defaultM );
2042 }
2043
2044 // Straight -> curve
2046 {
2047 newGeom.convertToCurvedMultiType();
2048 }
2049
2050 // Multi -> single
2051 if ( ! QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart( ) )
2052 {
2053 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
2054 res.reserve( parts->partCount() );
2055 for ( int i = 0; i < parts->partCount( ); i++ )
2056 {
2057 res << QgsGeometry( parts->geometryN( i )->clone() );
2058 }
2059 }
2060 // GeometryCollection (of Point/LineString/Polygon) -> MultiPoint/MultiLineString/MultiPolygon
2061 else if ( ( type == Qgis::WkbType::MultiPoint ||
2065 {
2067 const QgsGeometryCollection *geomColl( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
2068
2069 bool allExpectedType = true;
2070 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2071 {
2072 if ( geomColl->geometryN( i )->wkbType() != singleType )
2073 {
2074 allExpectedType = false;
2075 break;
2076 }
2077 }
2078 if ( allExpectedType )
2079 {
2080 std::unique_ptr< QgsGeometryCollection > newGeomCol;
2082 {
2083 newGeomCol = std::make_unique< QgsMultiPoint >();
2084 }
2086 {
2087 newGeomCol = std::make_unique< QgsMultiLineString >();
2088 }
2089 else
2090 {
2091 newGeomCol = std::make_unique< QgsMultiPolygon >();
2092 }
2093 newGeomCol->reserve( geomColl->numGeometries() );
2094 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2095 {
2096 newGeomCol->addGeometry( geomColl->geometryN( i )->clone() );
2097 }
2098 res << QgsGeometry( std::move( newGeomCol ) );
2099 }
2100 else
2101 {
2102 res << newGeom;
2103 }
2104 }
2105 else
2106 {
2107 res << newGeom;
2108 }
2109 return res;
2110}
2111
2112QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
2113{
2114 switch ( destType )
2115 {
2117 return convertToPoint( destMultipart );
2118
2120 return convertToLine( destMultipart );
2121
2123 return convertToPolygon( destMultipart );
2124
2125 default:
2126 return QgsGeometry();
2127 }
2128}
2129
2131{
2132 if ( !d->geometry )
2133 {
2134 return false;
2135 }
2136
2137 if ( isMultipart() ) //already multitype, no need to convert
2138 {
2139 return true;
2140 }
2141
2142 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
2144 if ( !multiGeom )
2145 {
2146 return false;
2147 }
2148
2149 //try to avoid cloning existing geometry whenever we can
2150
2151 //want to see a magic trick?... gather round kiddies...
2152 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
2153 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
2154 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
2155 multiGeom->addGeometry( d->geometry.release() );
2156 // and replace it with the multi geometry.
2157 // TADA! a clone free conversion in some cases
2158 d->geometry = std::move( geom );
2159 return true;
2160}
2161
2163{
2164 if ( !d->geometry )
2165 {
2166 return false;
2167 }
2168
2169 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
2170 {
2175 {
2176 return true;
2177 }
2178 default:
2179 break;
2180 }
2181
2182 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
2184 if ( !multiGeom )
2185 {
2186 return false;
2187 }
2188
2189 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2190 if ( sourceMultiGeom )
2191 {
2192 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
2193 {
2194 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
2195 return false;
2196 }
2197 }
2198 else
2199 {
2200 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
2201 return false;
2202 }
2203
2204 reset( std::move( geom ) );
2205 return true;
2206}
2207
2209{
2210 if ( !d->geometry )
2211 {
2212 return false;
2213 }
2214
2215 if ( !isMultipart() ) //already single part, no need to convert
2216 {
2217 return true;
2218 }
2219
2220 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2221 if ( !multiGeom || multiGeom->partCount() < 1 )
2222 return false;
2223
2224 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
2225 reset( std::move( firstPart ) );
2226 return true;
2227}
2228
2229
2231{
2233 if ( !origGeom )
2234 return false;
2235
2236 std::unique_ptr<QgsGeometryCollection> resGeom;
2237 switch ( geomType )
2238 {
2240 resGeom = std::make_unique<QgsMultiPoint>();
2241 break;
2243 resGeom = std::make_unique<QgsMultiLineString>();
2244 break;
2246 resGeom = std::make_unique<QgsMultiPolygon>();
2247 break;
2248 default:
2249 break;
2250 }
2251 if ( !resGeom )
2252 return false;
2253
2254 resGeom->reserve( origGeom->numGeometries() );
2255 for ( int i = 0; i < origGeom->numGeometries(); ++i )
2256 {
2257 const QgsAbstractGeometry *g = origGeom->geometryN( i );
2258 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
2259 resGeom->addGeometry( g->clone() );
2260 }
2261
2262 set( resGeom.release() );
2263 return true;
2264}
2265
2266
2268{
2269 if ( !d->geometry )
2270 {
2271 return QgsPointXY();
2272 }
2273 if ( const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
2274 {
2275 return QgsPointXY( pt->x(), pt->y() );
2276 }
2277 else
2278 {
2279 return QgsPointXY();
2280 }
2281}
2282
2284{
2285 QgsPolylineXY polyLine;
2286 if ( !d->geometry )
2287 {
2288 return polyLine;
2289 }
2290
2291 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve
2292 || QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CircularString );
2293 std::unique_ptr< QgsLineString > segmentizedLine;
2294 QgsLineString *line = nullptr;
2295 if ( doSegmentation )
2296 {
2297 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
2298 if ( !curve )
2299 {
2300 return polyLine;
2301 }
2302 segmentizedLine.reset( curve->curveToLine() );
2303 line = segmentizedLine.get();
2304 }
2305 else
2306 {
2307 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
2308 if ( !line )
2309 {
2310 return polyLine;
2311 }
2312 }
2313
2314 int nVertices = line->numPoints();
2315 polyLine.resize( nVertices );
2316 QgsPointXY *data = polyLine.data();
2317 const double *xData = line->xData();
2318 const double *yData = line->yData();
2319 for ( int i = 0; i < nVertices; ++i )
2320 {
2321 data->setX( *xData++ );
2322 data->setY( *yData++ );
2323 data++;
2324 }
2325
2326 return polyLine;
2327}
2328
2330{
2331 if ( !d->geometry )
2332 return QgsPolygonXY();
2333
2334 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
2335
2336 QgsPolygon *p = nullptr;
2337 std::unique_ptr< QgsPolygon > segmentized;
2338 if ( doSegmentation )
2339 {
2340 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
2341 if ( !curvePoly )
2342 {
2343 return QgsPolygonXY();
2344 }
2345 segmentized.reset( curvePoly->toPolygon() );
2346 p = segmentized.get();
2347 }
2348 else
2349 {
2350 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
2351 }
2352
2353 if ( !p )
2354 {
2355 return QgsPolygonXY();
2356 }
2357
2358 QgsPolygonXY polygon;
2359 convertPolygon( *p, polygon );
2360
2361 return polygon;
2362}
2363
2365{
2366 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
2367 {
2368 return QgsMultiPointXY();
2369 }
2370
2371 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
2372 if ( !mp )
2373 {
2374 return QgsMultiPointXY();
2375 }
2376
2377 int nPoints = mp->numGeometries();
2378 QgsMultiPointXY multiPoint( nPoints );
2379 for ( int i = 0; i < nPoints; ++i )
2380 {
2381 const QgsPoint *pt = mp->pointN( i );
2382 multiPoint[i].setX( pt->x() );
2383 multiPoint[i].setY( pt->y() );
2384 }
2385 return multiPoint;
2386}
2387
2389{
2390 if ( !d->geometry )
2391 {
2392 return QgsMultiPolylineXY();
2393 }
2394
2395 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2396 if ( !geomCollection )
2397 {
2398 return QgsMultiPolylineXY();
2399 }
2400
2401 int nLines = geomCollection->numGeometries();
2402 if ( nLines < 1 )
2403 {
2404 return QgsMultiPolylineXY();
2405 }
2406
2408 mpl.reserve( nLines );
2409 for ( int i = 0; i < nLines; ++i )
2410 {
2411 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2412 std::unique_ptr< QgsLineString > segmentized;
2413 if ( !line )
2414 {
2415 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2416 if ( !curve )
2417 {
2418 continue;
2419 }
2420 segmentized.reset( curve->curveToLine() );
2421 line = segmentized.get();
2422 }
2423
2424 QgsPolylineXY polyLine;
2425 int nVertices = line->numPoints();
2426 polyLine.resize( nVertices );
2427 QgsPointXY *data = polyLine.data();
2428 const double *xData = line->xData();
2429 const double *yData = line->yData();
2430 for ( int i = 0; i < nVertices; ++i )
2431 {
2432 data->setX( *xData++ );
2433 data->setY( *yData++ );
2434 data++;
2435 }
2436 mpl.append( polyLine );
2437 }
2438 return mpl;
2439}
2440
2442{
2443 if ( !d->geometry )
2444 {
2445 return QgsMultiPolygonXY();
2446 }
2447
2448 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2449 if ( !geomCollection )
2450 {
2451 return QgsMultiPolygonXY();
2452 }
2453
2454 const int nPolygons = geomCollection->numGeometries();
2455 if ( nPolygons < 1 )
2456 {
2457 return QgsMultiPolygonXY();
2458 }
2459
2461 mp.reserve( nPolygons );
2462 for ( int i = 0; i < nPolygons; ++i )
2463 {
2464 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2465 if ( !polygon )
2466 {
2467 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2468 if ( cPolygon )
2469 {
2470 polygon = cPolygon->toPolygon();
2471 }
2472 else
2473 {
2474 continue;
2475 }
2476 }
2477
2478 QgsPolygonXY poly;
2479 convertPolygon( *polygon, poly );
2480 mp.push_back( poly );
2481 }
2482 return mp;
2483}
2484
2485double QgsGeometry::area() const
2486{
2487 if ( !d->geometry )
2488 {
2489 return -1.0;
2490 }
2491
2492 return d->geometry->area();
2493}
2494
2496{
2497 if ( !d->geometry )
2498 {
2499 throw QgsInvalidArgumentException( "Cannot compute 3D area: geometry is null." );
2500 }
2501
2502 return d->geometry->area3D();
2503}
2504
2506{
2507 if ( !d->geometry )
2508 {
2509 return -1.0;
2510 }
2511
2512 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2513 {
2515 return 0.0;
2516
2518 return d->geometry->length();
2519
2521 return d->geometry->perimeter();
2522
2525 return d->geometry->length();
2526 }
2527 return -1;
2528}
2529
2530double QgsGeometry::distance( const QgsGeometry &geom ) const
2531{
2532 if ( !d->geometry || !geom.d->geometry )
2533 {
2534 return -1.0;
2535 }
2536
2537 // avoid calling geos for trivial point-to-point distance calculations
2538 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( geom.wkbType() ) == Qgis::WkbType::Point )
2539 {
2540 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2541 }
2542
2543 QgsGeos g( d->geometry.get() );
2544 mLastError.clear();
2545 return g.distance( geom.d->geometry.get(), &mLastError );
2546}
2547
2549{
2550 if ( !d->geometry || !geom.d->geometry )
2551 {
2552 return -1.0;
2553 }
2554
2555 QgsGeos g( d->geometry.get() );
2556 mLastError.clear();
2557 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2558}
2559
2560double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2561{
2562 if ( !d->geometry || !geom.d->geometry )
2563 {
2564 return -1.0;
2565 }
2566
2567 QgsGeos g( d->geometry.get() );
2568 mLastError.clear();
2569 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2570}
2571
2572
2574{
2575 if ( !d->geometry || !geom.d->geometry )
2576 {
2577 return -1.0;
2578 }
2579
2580 QgsGeos g( d->geometry.get() );
2581 mLastError.clear();
2582 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
2583}
2584
2585double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2586{
2587 if ( !d->geometry || !geom.d->geometry )
2588 {
2589 return -1.0;
2590 }
2591
2592 QgsGeos g( d->geometry.get() );
2593 mLastError.clear();
2594 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2595}
2596
2598{
2599 if ( !d->geometry || d->geometry.get()->isEmpty() )
2601 return d->geometry->vertices_begin();
2602}
2603
2605{
2606 if ( !d->geometry || d->geometry.get()->isEmpty() )
2608 return d->geometry->vertices_end();
2609}
2610
2612{
2613 if ( !d->geometry || d->geometry.get()->isEmpty() )
2614 return QgsVertexIterator();
2615 return QgsVertexIterator( d->geometry.get() );
2616}
2617
2619{
2620 if ( !d->geometry )
2622
2623 detach();
2624 return d->geometry->parts_begin();
2625}
2626
2628{
2629 if ( !d->geometry )
2631 return d->geometry->parts_end();
2632}
2633
2635{
2636 if ( !d->geometry )
2638 return d->geometry->const_parts_begin();
2639}
2640
2642{
2643 if ( !d->geometry )
2645 return d->geometry->const_parts_end();
2646}
2647
2649{
2650 if ( !d->geometry )
2651 return QgsGeometryPartIterator();
2652
2653 detach();
2654 return QgsGeometryPartIterator( d->geometry.get() );
2655}
2656
2658{
2659 if ( !d->geometry )
2661
2662 return QgsGeometryConstPartIterator( d->geometry.get() );
2663}
2664
2665QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
2666{
2667 if ( !d->geometry )
2668 {
2669 return QgsGeometry();
2670 }
2671
2672 QgsGeos g( d->geometry.get() );
2673 mLastError.clear();
2674 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError ) );
2675 if ( !geom )
2676 {
2677 QgsGeometry result;
2678 result.mLastError = mLastError;
2679 return result;
2680 }
2681 return QgsGeometry( std::move( geom ) );
2682}
2683
2684QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit ) const
2685{
2686 if ( !d->geometry )
2687 {
2688 return QgsGeometry();
2689 }
2690
2691 QgsGeos g( d->geometry.get() );
2692 mLastError.clear();
2693 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError );
2694 if ( !geom )
2695 {
2696 QgsGeometry result;
2697 result.mLastError = mLastError;
2698 return result;
2699 }
2700 return QgsGeometry( geom );
2701}
2702
2703QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2704{
2705 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2706 {
2707 return QgsGeometry();
2708 }
2709
2710 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2711 {
2712 const QVector<QgsGeometry> parts = asGeometryCollection();
2713 QVector<QgsGeometry> results;
2714 results.reserve( parts.count() );
2715 for ( const QgsGeometry &part : parts )
2716 {
2717 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2718 if ( !result.isNull() )
2719 results << result;
2720 }
2721 if ( results.isEmpty() )
2722 return QgsGeometry();
2723
2724 QgsGeometry first = results.takeAt( 0 );
2725 for ( const QgsGeometry &result : std::as_const( results ) )
2726 {
2727 first.addPart( result );
2728 }
2729 return first;
2730 }
2731 else
2732 {
2733 QgsGeos geos( d->geometry.get() );
2734 mLastError.clear();
2735
2736 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2737 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2738
2739 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2740 if ( !offsetGeom )
2741 {
2742 QgsGeometry result;
2743 result.mLastError = mLastError;
2744 return result;
2745 }
2746
2747 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2748 {
2749 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2750 if ( newOrientation != prevOrientation )
2751 {
2752 // GEOS has flipped line orientation, flip it back
2753 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2754 offsetGeom = std::move( flipped );
2755 }
2756 }
2757 return QgsGeometry( std::move( offsetGeom ) );
2758 }
2759}
2760
2761QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2762{
2763 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2764 {
2765 return QgsGeometry();
2766 }
2767
2768 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2769 {
2770 const QVector<QgsGeometry> parts = asGeometryCollection();
2771 QVector<QgsGeometry> results;
2772 results.reserve( parts.count() );
2773 for ( const QgsGeometry &part : parts )
2774 {
2775 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2776 if ( !result.isNull() )
2777 results << result;
2778 }
2779 if ( results.isEmpty() )
2780 return QgsGeometry();
2781
2782 QgsGeometry first = results.takeAt( 0 );
2783 for ( const QgsGeometry &result : std::as_const( results ) )
2784 {
2785 first.addPart( result );
2786 }
2787 return first;
2788 }
2789 else
2790 {
2791 QgsGeos geos( d->geometry.get() );
2792 mLastError.clear();
2793 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side,
2794 joinStyle, miterLimit, &mLastError );
2795 if ( !bufferGeom )
2796 {
2797 QgsGeometry result;
2798 result.mLastError = mLastError;
2799 return result;
2800 }
2801 return QgsGeometry( std::move( bufferGeom ) );
2802 }
2803}
2804
2805QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2806{
2807 QgsInternalGeometryEngine engine( *this );
2808
2809 return engine.taperedBuffer( startWidth, endWidth, segments );
2810}
2811
2813{
2814 QgsInternalGeometryEngine engine( *this );
2815
2816 return engine.variableWidthBufferByM( segments );
2817}
2818
2819QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2820{
2821 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2822 {
2823 return QgsGeometry();
2824 }
2825
2826 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2827 {
2828 const QVector<QgsGeometry> parts = asGeometryCollection();
2829 QVector<QgsGeometry> results;
2830 results.reserve( parts.count() );
2831 for ( const QgsGeometry &part : parts )
2832 {
2833 QgsGeometry result = part.extendLine( startDistance, endDistance );
2834 if ( !result.isNull() )
2835 results << result;
2836 }
2837 if ( results.isEmpty() )
2838 return QgsGeometry();
2839
2840 QgsGeometry first = results.takeAt( 0 );
2841 for ( const QgsGeometry &result : std::as_const( results ) )
2842 {
2843 first.addPart( result );
2844 }
2845 return first;
2846 }
2847 else
2848 {
2849 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2850 if ( !line )
2851 return QgsGeometry();
2852
2853 std::unique_ptr< QgsLineString > newLine( line->clone() );
2854 newLine->extend( startDistance, endDistance );
2855 return QgsGeometry( std::move( newLine ) );
2856 }
2857}
2858
2859QgsGeometry QgsGeometry::simplify( double tolerance ) const
2860{
2861 if ( !d->geometry )
2862 {
2863 return QgsGeometry();
2864 }
2865
2866 QgsGeos geos( d->geometry.get() );
2867 mLastError.clear();
2868 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError ) );
2869 if ( !simplifiedGeom )
2870 {
2871 QgsGeometry result;
2872 result.mLastError = mLastError;
2873 return result;
2874 }
2875 return QgsGeometry( std::move( simplifiedGeom ) );
2876}
2877
2878QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2879{
2880 QgsInternalGeometryEngine engine( *this );
2881
2882 return engine.densifyByCount( extraNodesPerSegment );
2883}
2884
2886{
2887 QgsInternalGeometryEngine engine( *this );
2888
2889 return engine.densifyByDistance( distance );
2890}
2891
2892QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2893{
2894 QgsInternalGeometryEngine engine( *this );
2895
2896 return engine.convertToCurves( distanceTolerance, angleTolerance );
2897}
2898
2900{
2901 if ( !d->geometry )
2902 {
2903 return QgsGeometry();
2904 }
2905
2906 // avoid calling geos for trivial point centroids
2907 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
2908 {
2909 QgsGeometry c = *this;
2910 c.get()->dropZValue();
2911 c.get()->dropMValue();
2912 return c;
2913 }
2914
2915 QgsGeos geos( d->geometry.get() );
2916
2917 mLastError.clear();
2918 QgsGeometry result( geos.centroid( &mLastError ) );
2919 result.mLastError = mLastError;
2920 return result;
2921}
2922
2924{
2925 if ( !d->geometry )
2926 {
2927 return QgsGeometry();
2928 }
2929
2930 QgsGeos geos( d->geometry.get() );
2931
2932 mLastError.clear();
2933 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2934 result.mLastError = mLastError;
2935 return result;
2936}
2937
2938QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2939{
2940 QgsInternalGeometryEngine engine( *this );
2941
2942 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2943}
2944
2945QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2946{
2947 if ( !d->geometry )
2948 {
2949 return QgsGeometry();
2950 }
2951
2952 QgsGeos geos( d->geometry.get() );
2953
2954 mLastError.clear();
2955 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2956 result.mLastError = mLastError;
2957 return result;
2958}
2959
2961{
2962 if ( !d->geometry )
2963 {
2964 return QgsGeometry();
2965 }
2966
2967 QgsGeos geos( d->geometry.get() );
2968
2969 mLastError.clear();
2970 QgsGeometry result( geos.minimumWidth( &mLastError ) );
2971 result.mLastError = mLastError;
2972 return result;
2973}
2974
2976{
2977 if ( !d->geometry )
2978 {
2979 return std::numeric_limits< double >::quiet_NaN();
2980 }
2981
2982 QgsGeos geos( d->geometry.get() );
2983
2984 mLastError.clear();
2985 return geos.minimumClearance( &mLastError );
2986}
2987
2989{
2990 if ( !d->geometry )
2991 {
2992 return QgsGeometry();
2993 }
2994
2995 QgsGeos geos( d->geometry.get() );
2996
2997 mLastError.clear();
2998 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
2999 result.mLastError = mLastError;
3000 return result;
3001}
3002
3004{
3005 if ( !d->geometry )
3006 {
3007 return QgsGeometry();
3008 }
3009 QgsGeos geos( d->geometry.get() );
3010 mLastError.clear();
3011 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
3012 if ( !cHull )
3013 {
3014 QgsGeometry geom;
3015 geom.mLastError = mLastError;
3016 return geom;
3017 }
3018 return QgsGeometry( std::move( cHull ) );
3019}
3020
3021QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles ) const
3022{
3023 if ( !d->geometry )
3024 {
3025 return QgsGeometry();
3026 }
3027 QgsGeos geos( d->geometry.get() );
3028 mLastError.clear();
3029 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError ) );
3030 if ( !concaveHull )
3031 {
3032 QgsGeometry geom;
3033 geom.mLastError = mLastError;
3034 return geom;
3035 }
3036 return QgsGeometry( std::move( concaveHull ) );
3037}
3038
3039QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
3040{
3041 if ( !d->geometry )
3042 {
3043 return QgsGeometry();
3044 }
3045
3046 QgsGeos geos( d->geometry.get() );
3047 mLastError.clear();
3048 QgsGeometry result = QgsGeometry( geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError ) );
3049 result.mLastError = mLastError;
3050 return result;
3051}
3052
3053QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
3054{
3055 if ( !d->geometry )
3056 {
3057 return QgsGeometry();
3058 }
3059
3060 QgsGeos geos( d->geometry.get() );
3061 mLastError.clear();
3062 QgsGeometry result = QgsGeometry( geos.delaunayTriangulation( tolerance, edgesOnly ) );
3063 result.mLastError = mLastError;
3064 return result;
3065}
3066
3068{
3069 if ( !d->geometry )
3070 {
3071 return QgsGeometry();
3072 }
3073
3074 QgsGeos geos( d->geometry.get() );
3075 mLastError.clear();
3076 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
3077 result.mLastError = mLastError;
3078 return result;
3079}
3080
3082{
3083 if ( !d->geometry )
3084 {
3085 return QgsGeometry();
3086 }
3087
3088 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::GeometryCollection &&
3089 QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPolygon
3090 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::Polygon )
3091 return QgsGeometry();
3092
3093 QgsGeos geos( d->geometry.get() );
3094 mLastError.clear();
3095 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
3096 result.mLastError = mLastError;
3097 return result;
3098}
3099
3101{
3102 if ( !d->geometry )
3103 {
3105 }
3106
3107 QgsGeos geos( d->geometry.get() );
3108 mLastError.clear();
3109 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
3110
3111 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
3112
3113 if ( invalidEdges && invalidEdgesGeom )
3114 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
3115
3116 return result;
3117}
3118
3119QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
3120{
3121 if ( !d->geometry )
3122 {
3123 return QgsGeometry();
3124 }
3125
3126 QgsGeos geos( d->geometry.get() );
3127 mLastError.clear();
3128 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
3129 result.mLastError = mLastError;
3130 return result;
3131}
3132
3134{
3135 if ( !d->geometry )
3136 {
3137 return QgsGeometry();
3138 }
3139
3140 QgsGeos geos( d->geometry.get() );
3141 mLastError.clear();
3142 QgsGeometry result( geos.node( &mLastError ) );
3143 result.mLastError = mLastError;
3144 return result;
3145}
3146
3148{
3149 if ( !d->geometry )
3150 {
3151 return QgsGeometry();
3152 }
3153
3154 QgsGeos geos( d->geometry.get() );
3155 mLastError.clear();
3156 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
3157 result.mLastError = mLastError;
3158 return result;
3159}
3160
3161QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters ) const
3162{
3163 if ( !d->geometry )
3164 {
3165 return QgsGeometry();
3166 }
3167
3168 const QgsAbstractGeometry *geom = d->geometry.get();
3169 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
3170 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
3171 {
3172 segmentizedCopy.reset( d->geometry->segmentize() );
3173 geom = segmentizedCopy.get();
3174 }
3175
3176 QgsGeos geos( geom );
3177 mLastError.clear();
3178 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters ) );
3179 if ( !result )
3180 {
3181 QgsGeometry geom;
3182 geom.mLastError = mLastError;
3183 return geom;
3184 }
3185 return QgsGeometry( std::move( result ) );
3186}
3187
3189{
3190 if ( !d->geometry )
3191 {
3192 return QgsGeometry();
3193 }
3194
3195 QgsGeometry line = *this;
3197 return QgsGeometry();
3198 else if ( type() == Qgis::GeometryType::Polygon )
3199 {
3200 line = QgsGeometry( d->geometry->boundary() );
3201 }
3202
3203 const QgsCurve *curve = nullptr;
3205 {
3206 // if multi part, iterate through parts to find target part
3207 for ( int part = 0; part < collection->numGeometries(); ++part )
3208 {
3209 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
3210 if ( !candidate )
3211 continue;
3212 const double candidateLength = candidate->length();
3213 if ( candidateLength >= distance )
3214 {
3215 curve = candidate;
3216 break;
3217 }
3218
3219 distance -= candidateLength;
3220 }
3221 }
3222 else
3223 {
3225 }
3226 if ( !curve )
3227 return QgsGeometry();
3228
3229 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
3230 if ( !result )
3231 {
3232 return QgsGeometry();
3233 }
3234 return QgsGeometry( std::move( result ) );
3235}
3236
3237double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
3238{
3239 if ( type() != Qgis::GeometryType::Line )
3240 return -1;
3241
3243 return -1;
3244
3245 QgsGeometry segmentized = *this;
3247 {
3248 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
3249 }
3250
3251 QgsGeos geos( d->geometry.get() );
3252 mLastError.clear();
3253 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
3254}
3255
3257{
3258 if ( !d->geometry || d->geometry->isEmpty() )
3259 return 0.0;
3260
3261 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
3263 return 0.0;
3264
3265 // always operate on segmentized geometries
3266 QgsGeometry segmentized = *this;
3267 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
3268 {
3269 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
3270 }
3271
3272 QgsVertexId previous;
3273 QgsVertexId next;
3274 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
3275 return 0.0;
3276
3277 if ( previous == next )
3278 {
3279 // distance coincided exactly with a vertex
3280 QgsVertexId v2 = previous;
3281 QgsVertexId v1;
3282 QgsVertexId v3;
3283 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
3284 if ( v1.isValid() && v3.isValid() )
3285 {
3286 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3287 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3288 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
3289 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3290 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
3291 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
3292 }
3293 else if ( v3.isValid() )
3294 {
3295 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
3296 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
3297 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3298 }
3299 else
3300 {
3301 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3302 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3303 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3304 }
3305 }
3306 else
3307 {
3308 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
3309 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
3310 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3311 }
3312}
3313
3315{
3316 if ( !d->geometry || geometry.isNull() )
3317 {
3318 return QgsGeometry();
3319 }
3320
3321 QgsGeos geos( d->geometry.get() );
3322
3323 mLastError.clear();
3324 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters ) );
3325
3326 if ( !resultGeom )
3327 {
3328 QgsGeometry geom;
3329 geom.mLastError = mLastError;
3330 return geom;
3331 }
3332
3333 return QgsGeometry( std::move( resultGeom ) );
3334}
3335
3336QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters ) const
3337{
3338 if ( !d->geometry || geometry.isNull() )
3339 {
3340 return QgsGeometry();
3341 }
3342
3343 QgsGeos geos( d->geometry.get() );
3344 mLastError.clear();
3345 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters ) );
3346 if ( !resultGeom )
3347 {
3348 QgsGeometry geom;
3349 geom.mLastError = mLastError;
3350 return geom;
3351 }
3352 return QgsGeometry( std::move( resultGeom ) );
3353}
3354
3356{
3357 if ( !d->geometry )
3358 {
3359 return QgsGeometry();
3360 }
3361
3362 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::LineString )
3363 {
3364 // special case - a single linestring was passed
3365 return QgsGeometry( *this );
3366 }
3367
3368 QgsGeos geos( d->geometry.get() );
3369 mLastError.clear();
3370 QgsGeometry result( geos.mergeLines( &mLastError, parameters ) );
3371 result.mLastError = mLastError;
3372 return result;
3373}
3374
3376{
3377 if ( !d->geometry || geometry.isNull() )
3378 {
3379 return QgsGeometry();
3380 }
3381
3382 QgsGeos geos( d->geometry.get() );
3383
3384 mLastError.clear();
3385 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters ) );
3386 if ( !resultGeom )
3387 {
3388 QgsGeometry geom;
3389 geom.mLastError = mLastError;
3390 return geom;
3391 }
3392 return QgsGeometry( std::move( resultGeom ) );
3393}
3394
3396{
3397 if ( !d->geometry || geometry.isNull() )
3398 {
3399 return QgsGeometry();
3400 }
3401
3402 QgsGeos geos( d->geometry.get() );
3403
3404 mLastError.clear();
3405 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters ) );
3406 if ( !resultGeom )
3407 {
3408 QgsGeometry geom;
3409 geom.mLastError = mLastError;
3410 return geom;
3411 }
3412 return QgsGeometry( std::move( resultGeom ) );
3413}
3414
3416{
3417 QgsInternalGeometryEngine engine( *this );
3418
3419 return engine.extrude( x, y );
3420}
3421
3423
3424QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3425{
3427 return QVector< QgsPointXY >();
3428
3429 QgsInternalGeometryEngine engine( *this );
3430 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3431 mLastError = engine.lastError();
3432 return res;
3433}
3434
3435QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3436{
3438 return QVector< QgsPointXY >();
3439
3440 QgsInternalGeometryEngine engine( *this );
3441 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3442 mLastError = engine.lastError();
3443 return res;
3444}
3446
3448{
3449 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3450}
3451
3453{
3454 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3455}
3456
3457QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3458{
3459 QVector<QgsGeometry> geometryList;
3460 if ( !d->geometry )
3461 {
3462 return geometryList;
3463 }
3464
3466 if ( gc )
3467 {
3468 int numGeom = gc->numGeometries();
3469 geometryList.reserve( numGeom );
3470 for ( int i = 0; i < numGeom; ++i )
3471 {
3472 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3473 }
3474 }
3475 else //a singlepart geometry
3476 {
3477 geometryList.append( *this );
3478 }
3479
3480 return geometryList;
3481}
3482
3484{
3485 QgsPointXY point = asPoint();
3486 return point.toQPointF();
3487}
3488
3490{
3491 const QgsAbstractGeometry *part = constGet();
3492
3493 // if a geometry collection, get first part only
3495 {
3496 if ( collection->numGeometries() > 0 )
3497 part = collection->geometryN( 0 );
3498 else
3499 return QPolygonF();
3500 }
3501
3502 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3503 return curve->asQPolygonF();
3504 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3505 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3506 return QPolygonF();
3507}
3508
3509bool QgsGeometry::deleteRing( int ringNum, int partNum )
3510{
3511 if ( !d->geometry )
3512 {
3513 return false;
3514 }
3515
3516 detach();
3517 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3518 return ok;
3519}
3520
3521bool QgsGeometry::deletePart( int partNum )
3522{
3523 if ( !d->geometry )
3524 {
3525 return false;
3526 }
3527
3528 if ( !isMultipart() && partNum < 1 )
3529 {
3530 set( nullptr );
3531 return true;
3532 }
3533
3534 detach();
3535 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3536 return ok;
3537}
3538
3539Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3540{
3541 if ( !d->geometry )
3542 {
3544 }
3545
3546 Qgis::WkbType geomTypeBeforeModification = wkbType();
3547
3548 bool haveInvalidGeometry = false;
3549 bool geomModified = false;
3550
3551 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3552 if ( diffGeom )
3553 {
3554 reset( std::move( diffGeom ) );
3555 geomModified = true;
3556 }
3557
3558 if ( geomTypeBeforeModification != wkbType() )
3560 if ( haveInvalidGeometry )
3562 if ( !geomModified )
3564
3566}
3567
3599
3601{
3602 if ( !d->geometry )
3603 return QgsGeometry();
3604
3605 mLastError.clear();
3606 QgsGeos geos( d->geometry.get() );
3607 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError ) );
3608
3609 QgsGeometry result = QgsGeometry( std::move( g ) );
3610 result.mLastError = mLastError;
3611 return result;
3612}
3613
3618
3620{
3621 if ( !d->geometry )
3622 {
3624 }
3625
3626 if ( isMultipart() )
3627 {
3628 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3629 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3631 {
3632 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3633 }
3634 }
3635 else
3636 {
3637 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3638 {
3639 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3640 }
3641 }
3642
3644
3645}
3646
3648{
3649 if ( !d->geometry )
3650 return QgsGeometry();
3651
3652 if ( isMultipart() )
3653 {
3654 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3655 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3656 newCollection->reserve( collection->numGeometries() );
3657 for ( int i = 0; i < collection->numGeometries(); ++i )
3658 {
3659 const QgsAbstractGeometry *g = collection->geometryN( i );
3661 {
3662 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3663 corrected->forceClockwise();
3664 newCollection->addGeometry( corrected.release() );
3665 }
3666 else
3667 {
3668 newCollection->addGeometry( g->clone() );
3669 }
3670 }
3671 return QgsGeometry( std::move( newCollection ) );
3672 }
3673 else
3674 {
3675 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3676 {
3677 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3678 corrected->forceClockwise();
3679 return QgsGeometry( std::move( corrected ) );
3680 }
3681 else
3682 {
3683 // not a curve polygon, so return unchanged
3684 return *this;
3685 }
3686 }
3687}
3688
3690{
3691 if ( !d->geometry )
3692 return QgsGeometry();
3693
3694 if ( isMultipart() )
3695 {
3696 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3697 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3698 newCollection->reserve( collection->numGeometries() );
3699 for ( int i = 0; i < collection->numGeometries(); ++i )
3700 {
3701 const QgsAbstractGeometry *g = collection->geometryN( i );
3703 {
3704 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3705 corrected->forceCounterClockwise();
3706 newCollection->addGeometry( corrected.release() );
3707 }
3708 else
3709 {
3710 newCollection->addGeometry( g->clone() );
3711 }
3712 }
3713 return QgsGeometry( std::move( newCollection ) );
3714 }
3715 else
3716 {
3717 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3718 {
3719 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3720 corrected->forceCounterClockwise();
3721 return QgsGeometry( std::move( corrected ) );
3722 }
3723 else
3724 {
3725 // not a curve polygon, so return unchanged
3726 return *this;
3727 }
3728 }
3729}
3730
3731
3732void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3733{
3734 errors.clear();
3735 if ( !d->geometry )
3736 return;
3737
3738 // avoid expensive calcs for trivial point geometries
3739 if ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) == Qgis::GeometryType::Point )
3740 {
3741 return;
3742 }
3743
3744 switch ( method )
3745 {
3747 QgsGeometryValidator::validateGeometry( *this, errors, method );
3748 return;
3749
3751 {
3752 QgsGeos geos( d->geometry.get(), 0, Qgis::GeosCreationFlags() );
3753 QString error;
3754 QgsGeometry errorLoc;
3755 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3756 {
3757 if ( errorLoc.isNull() )
3758 {
3759 errors.append( QgsGeometry::Error( error ) );
3760 }
3761 else
3762 {
3763 const QgsPointXY point = errorLoc.asPoint();
3764 errors.append( QgsGeometry::Error( error, point ) );
3765 }
3766 return;
3767 }
3768 }
3769 }
3770}
3771
3773{
3774 if ( !d->geometry )
3775 {
3776 return;
3777 }
3778
3779 detach();
3780 d->geometry->normalize();
3781}
3782
3784{
3785 if ( !d->geometry )
3786 {
3787 return false;
3788 }
3789
3790 return d->geometry->isValid( mLastError, flags );
3791}
3792
3794{
3795 if ( !d->geometry )
3796 return false;
3797
3798 QgsGeos geos( d->geometry.get() );
3799 mLastError.clear();
3800 return geos.isSimple( &mLastError );
3801}
3802
3803bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3804{
3805 if ( !d->geometry )
3806 return false;
3807
3808 QgsInternalGeometryEngine engine( *this );
3809 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3810}
3811
3813{
3814 if ( !d->geometry || !g.d->geometry )
3815 {
3816 return false;
3817 }
3818
3819 // fast check - are they shared copies of the same underlying geometry?
3820 if ( d == g.d )
3821 return true;
3822
3823 // fast check - distinct geometry types?
3824 if ( type() != g.type() )
3825 return false;
3826
3827 // avoid calling geos for trivial point case
3828 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point
3829 && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3830 {
3831 return equals( g );
3832 }
3833
3834 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3835 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3836 return false;
3837
3838 QgsGeos geos( d->geometry.get() );
3839 mLastError.clear();
3840 return geos.isEqual( g.d->geometry.get(), &mLastError );
3841}
3842
3843QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters )
3844{
3845 QgsGeos geos( nullptr );
3846
3847 QString error;
3848 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters ) );
3849 QgsGeometry result( std::move( geom ) );
3850 result.mLastError = error;
3851 return result;
3852}
3853
3854QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
3855{
3856 QVector<const QgsAbstractGeometry *> geomV2List;
3857 for ( const QgsGeometry &g : geometryList )
3858 {
3859 if ( !( g.isNull() ) )
3860 {
3861 geomV2List.append( g.constGet() );
3862 }
3863 }
3864
3865 QString error;
3866 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
3867 result.mLastError = error;
3868 return result;
3869}
3870
3872{
3873 if ( !d->geometry || !requiresConversionToStraightSegments() )
3874 {
3875 return;
3876 }
3877
3878 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3879 reset( std::move( straightGeom ) );
3880}
3881
3883{
3884 if ( !d->geometry )
3885 {
3886 return false;
3887 }
3888
3889 return d->geometry->hasCurvedSegments();
3890}
3891
3893{
3894 if ( !d->geometry )
3895 {
3897 }
3898
3899 detach();
3900 d->geometry->transform( ct, direction, transformZ );
3902}
3903
3904Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3905{
3906 if ( !d->geometry )
3907 {
3909 }
3910
3911 detach();
3912 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3914}
3915
3917{
3918 if ( d->geometry )
3919 {
3920 detach();
3921 d->geometry->transform( mtp.transform() );
3922 }
3923}
3924
3926{
3927 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
3928 {
3929 return QgsGeometry();
3930 }
3931
3932 QgsGeos geos( d->geometry.get() );
3933 mLastError.clear();
3934 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError );
3935 if ( !resultGeom )
3936 {
3937 QgsGeometry result;
3938 result.mLastError = mLastError;
3939 return result;
3940 }
3941 return QgsGeometry( std::move( resultGeom ) );
3942}
3943
3944void QgsGeometry::draw( QPainter &p ) const
3945{
3946 if ( d->geometry )
3947 {
3948 d->geometry->draw( p );
3949 }
3950}
3951
3952static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
3953{
3954 if ( vertexIndex < 0 )
3955 return false; // clearly something wrong
3956
3958 {
3959 partIndex = 0;
3960 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3961 {
3962 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3963
3964 // count total number of vertices in the part
3965 int numPoints = 0;
3966 for ( int k = 0; k < part->ringCount(); ++k )
3967 numPoints += part->vertexCount( 0, k );
3968
3969 if ( vertexIndex < numPoints )
3970 {
3971 int nothing;
3972 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3973 }
3974 vertexIndex -= numPoints;
3975 partIndex++;
3976 }
3977 }
3978 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
3979 {
3980 // PolyhedralSurface: patches are the parts
3981 partIndex = 0;
3982 for ( int i = 0; i < polySurface->numPatches(); ++i )
3983 {
3984 const QgsPolygon *patch = polySurface->patchN( i );
3985 // count total number of vertices in the patch
3986 int numPoints = 0;
3987 for ( int k = 0; k < patch->ringCount(); ++k )
3988 numPoints += patch->vertexCount( 0, k );
3989
3990 if ( vertexIndex < numPoints )
3991 {
3992 int nothing;
3993 return vertexIndexInfo( patch, vertexIndex, nothing, ringIndex, vertex );
3994 }
3995 vertexIndex -= numPoints;
3996 partIndex++;
3997 }
3998 }
3999 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4000 {
4001 const QgsCurve *ring = curvePolygon->exteriorRing();
4002 if ( vertexIndex < ring->numPoints() )
4003 {
4004 partIndex = 0;
4005 ringIndex = 0;
4006 vertex = vertexIndex;
4007 return true;
4008 }
4009 vertexIndex -= ring->numPoints();
4010 ringIndex = 1;
4011 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
4012 {
4013 const QgsCurve *ring = curvePolygon->interiorRing( i );
4014 if ( vertexIndex < ring->numPoints() )
4015 {
4016 partIndex = 0;
4017 vertex = vertexIndex;
4018 return true;
4019 }
4020 vertexIndex -= ring->numPoints();
4021 ringIndex += 1;
4022 }
4023 }
4024 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4025 {
4026 if ( vertexIndex < curve->numPoints() )
4027 {
4028 partIndex = 0;
4029 ringIndex = 0;
4030 vertex = vertexIndex;
4031 return true;
4032 }
4033 }
4034 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
4035 {
4036 if ( vertexIndex == 0 )
4037 {
4038 partIndex = 0;
4039 ringIndex = 0;
4040 vertex = 0;
4041 return true;
4042 }
4043 }
4044
4045 return false;
4046}
4047
4049{
4050 if ( !d->geometry )
4051 {
4052 return false;
4053 }
4054
4055 id.type = Qgis::VertexType::Segment;
4056
4057 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
4058 if ( !res )
4059 return false;
4060
4061 // now let's find out if it is a straight or circular segment
4062 const QgsAbstractGeometry *g = d->geometry.get();
4064 {
4065 g = geomCollection->geometryN( id.part );
4066 }
4067 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
4068 {
4069 g = polySurface->patchN( id.part );
4070 }
4071
4072 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4073 {
4074 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
4075 }
4076
4077 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4078 {
4079 QgsPoint p;
4080 res = curve->pointAt( id.vertex, p, id.type );
4081 if ( !res )
4082 return false;
4083 }
4084
4085 return true;
4086}
4087
4089{
4090 if ( !d->geometry )
4091 {
4092 return -1;
4093 }
4094 return d->geometry->vertexNumberFromVertexId( id );
4095}
4096
4098{
4099 return mLastError;
4100}
4101
4102void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
4103{
4104 if ( !d->geometry )
4105 return;
4106
4107 detach();
4108
4109 d->geometry->filterVertices( filter );
4110}
4111
4112void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
4113{
4114 if ( !d->geometry )
4115 return;
4116
4117 detach();
4118
4119 d->geometry->transformVertices( transform );
4120}
4121
4122void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
4123{
4124 output.clear();
4125 for ( const QgsPointXY &p : input )
4126 {
4127 output.append( QgsPoint( p ) );
4128 }
4129}
4130
4131void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
4132{
4133 output.clear();
4134 for ( const QgsPoint &p : input )
4135 {
4136 output.append( QgsPointXY( p.x(), p.y() ) );
4137 }
4138}
4139
4140void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
4141{
4142 output.clear();
4143
4144 auto convertRing = []( const QgsCurve * ring ) -> QgsPolylineXY
4145 {
4146 QgsPolylineXY res;
4147 bool doSegmentation = ( QgsWkbTypes::flatType( ring->wkbType() ) == Qgis::WkbType::CompoundCurve
4149 std::unique_ptr< QgsLineString > segmentizedLine;
4150 const QgsLineString *line = nullptr;
4151 if ( doSegmentation )
4152 {
4153 segmentizedLine.reset( ring->curveToLine() );
4154 line = segmentizedLine.get();
4155 }
4156 else
4157 {
4159 if ( !line )
4160 {
4161 return res;
4162 }
4163 }
4164
4165 int nVertices = line->numPoints();
4166 res.resize( nVertices );
4167 QgsPointXY *data = res.data();
4168 const double *xData = line->xData();
4169 const double *yData = line->yData();
4170 for ( int i = 0; i < nVertices; ++i )
4171 {
4172 data->setX( *xData++ );
4173 data->setY( *yData++ );
4174 data++;
4175 }
4176 return res;
4177 };
4178
4179 if ( const QgsCurve *exterior = input.exteriorRing() )
4180 {
4181 output.push_back( convertRing( exterior ) );
4182 }
4183
4184 const int interiorRingCount = input.numInteriorRings();
4185 output.reserve( output.size() + interiorRingCount );
4186 for ( int n = 0; n < interiorRingCount; ++n )
4187 {
4188 output.push_back( convertRing( input.interiorRing( n ) ) );
4189 }
4190}
4191
4193{
4194 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
4195}
4196
4197QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
4198{
4199 std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
4200
4201 if ( polygon.isClosed() )
4202 {
4203 auto poly = std::make_unique< QgsPolygon >();
4204 poly->setExteriorRing( ring.release() );
4205 return QgsGeometry( std::move( poly ) );
4206 }
4207 else
4208 {
4209 return QgsGeometry( std::move( ring ) );
4210 }
4211}
4212
4214{
4216 QgsPolygonXY result;
4217 result << createPolylineFromQPolygonF( polygon );
4218 return result;
4220}
4221
4223{
4224 QgsPolylineXY result;
4225 result.reserve( polygon.count() );
4226 for ( const QPointF &p : polygon )
4227 {
4228 result.append( QgsPointXY( p ) );
4229 }
4230 return result;
4231}
4232
4233bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
4234{
4235 if ( p1.count() != p2.count() )
4236 return false;
4237
4238 for ( int i = 0; i < p1.count(); ++i )
4239 {
4240 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
4241 return false;
4242 }
4243 return true;
4244}
4245
4246bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
4247{
4248 if ( p1.count() != p2.count() )
4249 return false;
4250
4251 for ( int i = 0; i < p1.count(); ++i )
4252 {
4253 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4254 return false;
4255 }
4256 return true;
4257}
4258
4259
4260bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
4261{
4262 if ( p1.count() != p2.count() )
4263 return false;
4264
4265 for ( int i = 0; i < p1.count(); ++i )
4266 {
4267 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4268 return false;
4269 }
4270 return true;
4271}
4272
4273QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4274{
4275 if ( !d->geometry || d->geometry->isEmpty() )
4276 return QgsGeometry();
4277
4278 QgsGeometry geom = *this;
4280 geom = QgsGeometry( d->geometry->segmentize() );
4281
4282 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
4283 {
4286 //can't smooth a point based geometry
4287 return geom;
4288
4290 {
4292 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
4293 }
4294
4296 {
4298
4299 auto resultMultiline = std::make_unique< QgsMultiLineString> ();
4300 resultMultiline->reserve( inputMultiLine->numGeometries() );
4301 for ( int i = 0; i < inputMultiLine->numGeometries(); ++i )
4302 {
4303 resultMultiline->addGeometry( smoothLine( *( inputMultiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4304 }
4305 return QgsGeometry( std::move( resultMultiline ) );
4306 }
4307
4309 {
4311 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
4312 }
4313
4315 {
4317
4318 auto resultMultiPoly = std::make_unique< QgsMultiPolygon >();
4319 resultMultiPoly->reserve( inputMultiPoly->numGeometries() );
4320 for ( int i = 0; i < inputMultiPoly->numGeometries(); ++i )
4321 {
4322 resultMultiPoly->addGeometry( smoothPolygon( *( inputMultiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4323 }
4324 return QgsGeometry( std::move( resultMultiPoly ) );
4325 }
4326
4328 default:
4329 return QgsGeometry( *this );
4330 }
4331}
4332
4333std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations,
4334 const double offset, double squareDistThreshold, double maxAngleRads,
4335 bool isRing )
4336{
4337 auto result = std::make_unique< QgsLineString >( line );
4338 QgsPointSequence outputLine;
4339 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
4340 {
4341 outputLine.resize( 0 );
4342 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
4343 bool skipFirst = false;
4344 bool skipLast = false;
4345 if ( isRing )
4346 {
4347 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
4348 QgsPoint p2 = result->pointN( 0 );
4349 QgsPoint p3 = result->pointN( 1 );
4350 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4351 p3.x(), p3.y() );
4352 angle = std::fabs( M_PI - angle );
4353 skipFirst = angle > maxAngleRads;
4354 }
4355 for ( int i = 0; i < result->numPoints() - 1; i++ )
4356 {
4357 QgsPoint p1 = result->pointN( i );
4358 QgsPoint p2 = result->pointN( i + 1 );
4359
4360 double angle = M_PI;
4361 if ( i == 0 && isRing )
4362 {
4363 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
4364 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4365 p3.x(), p3.y() );
4366 }
4367 else if ( i < result->numPoints() - 2 )
4368 {
4369 QgsPoint p3 = result->pointN( i + 2 );
4370 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4371 p3.x(), p3.y() );
4372 }
4373 else if ( i == result->numPoints() - 2 && isRing )
4374 {
4375 QgsPoint p3 = result->pointN( 1 );
4376 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4377 p3.x(), p3.y() );
4378 }
4379
4380 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
4381
4382 // don't apply distance threshold to first or last segment
4383 if ( i == 0 || i >= result->numPoints() - 2
4384 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
4385 {
4386 if ( !isRing )
4387 {
4388 if ( !skipFirst )
4389 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
4390 if ( !skipLast )
4391 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
4392 else
4393 outputLine << p2;
4394 }
4395 else
4396 {
4397 // ring
4398 if ( !skipFirst )
4399 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
4400 else if ( i == 0 )
4401 outputLine << p1;
4402 if ( !skipLast )
4403 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
4404 else
4405 outputLine << p2;
4406 }
4407 }
4408 skipFirst = skipLast;
4409 }
4410
4411 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4412 outputLine << outputLine.at( 0 );
4413
4414 result->setPoints( outputLine );
4415 }
4416 return result;
4417}
4418
4419std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4420{
4421 double maxAngleRads = maxAngle * M_PI / 180.0;
4422 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4423 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4424}
4425
4426std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4427{
4428 double maxAngleRads = maxAngle * M_PI / 180.0;
4429 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4430 auto resultPoly = std::make_unique< QgsPolygon >();
4431
4432 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset,
4433 squareDistThreshold, maxAngleRads, true ).release() );
4434
4435 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4436 {
4437 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset,
4438 squareDistThreshold, maxAngleRads, true ).release() );
4439 }
4440 return resultPoly;
4441}
4442
4443QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4444{
4445 switch ( type() )
4446 {
4448 {
4449 bool srcIsMultipart = isMultipart();
4450
4451 if ( ( destMultipart && srcIsMultipart ) ||
4452 ( !destMultipart && !srcIsMultipart ) )
4453 {
4454 // return a copy of the same geom
4455 return QgsGeometry( *this );
4456 }
4457 if ( destMultipart )
4458 {
4459 // layer is multipart => make a multipoint with a single point
4460 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4461 }
4462 else
4463 {
4464 // destination is singlepart => make a single part if possible
4465 QgsMultiPointXY multiPoint = asMultiPoint();
4466 if ( multiPoint.count() == 1 )
4467 {
4468 return fromPointXY( multiPoint[0] );
4469 }
4470 }
4471 return QgsGeometry();
4472 }
4473
4475 {
4476 // only possible if destination is multipart
4477 if ( !destMultipart )
4478 return QgsGeometry();
4479
4480 // input geometry is multipart
4481 if ( isMultipart() )
4482 {
4483 const QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4484 QgsMultiPointXY multiPoint;
4485 for ( const QgsPolylineXY &l : inputMultiLine )
4486 for ( const QgsPointXY &p : l )
4487 multiPoint << p;
4488 return fromMultiPointXY( multiPoint );
4489 }
4490 // input geometry is not multipart: copy directly the line into a multipoint
4491 else
4492 {
4493 QgsPolylineXY line = asPolyline();
4494 if ( !line.isEmpty() )
4495 return fromMultiPointXY( line );
4496 }
4497 return QgsGeometry();
4498 }
4499
4501 {
4502 // can only transform if destination is multipoint
4503 if ( !destMultipart )
4504 return QgsGeometry();
4505
4506 // input geometry is multipart: make a multipoint from multipolygon
4507 if ( isMultipart() )
4508 {
4509 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4510 QgsMultiPointXY multiPoint;
4511 for ( const QgsPolygonXY &poly : multiPolygon )
4512 for ( const QgsPolylineXY &line : poly )
4513 for ( const QgsPointXY &pt : line )
4514 multiPoint << pt;
4515 return fromMultiPointXY( multiPoint );
4516 }
4517 // input geometry is not multipart: make a multipoint from polygon
4518 else
4519 {
4520 const QgsPolygonXY polygon = asPolygon();
4521 QgsMultiPointXY multiPoint;
4522 for ( const QgsPolylineXY &line : polygon )
4523 for ( const QgsPointXY &pt : line )
4524 multiPoint << pt;
4525 return fromMultiPointXY( multiPoint );
4526 }
4527 }
4528
4529 default:
4530 return QgsGeometry();
4531 }
4532}
4533
4534QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4535{
4536 switch ( type() )
4537 {
4539 {
4540 if ( !isMultipart() )
4541 return QgsGeometry();
4542
4543 QgsMultiPointXY multiPoint = asMultiPoint();
4544 if ( multiPoint.count() < 2 )
4545 return QgsGeometry();
4546
4547 if ( destMultipart )
4548 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4549 else
4550 return fromPolylineXY( multiPoint );
4551 }
4552
4554 {
4555 bool srcIsMultipart = isMultipart();
4556
4557 if ( ( destMultipart && srcIsMultipart ) ||
4558 ( !destMultipart && ! srcIsMultipart ) )
4559 {
4560 // return a copy of the same geom
4561 return QgsGeometry( *this );
4562 }
4563 if ( destMultipart )
4564 {
4565 // destination is multipart => makes a multipoint with a single line
4566 QgsPolylineXY line = asPolyline();
4567 if ( !line.isEmpty() )
4568 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4569 }
4570 else
4571 {
4572 // destination is singlepart => make a single part if possible
4573 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4574 if ( inputMultiLine.count() == 1 )
4575 return fromPolylineXY( inputMultiLine[0] );
4576 }
4577 return QgsGeometry();
4578 }
4579
4581 {
4582 // input geometry is multipolygon
4583 if ( isMultipart() )
4584 {
4585 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4586 QgsMultiPolylineXY inputMultiLine;
4587 for ( const QgsPolygonXY &poly : multiPolygon )
4588 for ( const QgsPolylineXY &line : poly )
4589 inputMultiLine << line;
4590
4591 if ( destMultipart )
4592 {
4593 // destination is multipart
4594 return fromMultiPolylineXY( inputMultiLine );
4595 }
4596 else if ( inputMultiLine.count() == 1 )
4597 {
4598 // destination is singlepart => make a single part if possible
4599 return fromPolylineXY( inputMultiLine[0] );
4600 }
4601 }
4602 // input geometry is single polygon
4603 else
4604 {
4605 QgsPolygonXY polygon = asPolygon();
4606 // if polygon has rings
4607 if ( polygon.count() > 1 )
4608 {
4609 // cannot fit a polygon with rings in a single line layer
4610 // TODO: would it be better to remove rings?
4611 if ( destMultipart )
4612 {
4613 const QgsPolygonXY polygon = asPolygon();
4614 QgsMultiPolylineXY inputMultiLine;
4615 inputMultiLine.reserve( polygon.count() );
4616 for ( const QgsPolylineXY &line : polygon )
4617 inputMultiLine << line;
4618 return fromMultiPolylineXY( inputMultiLine );
4619 }
4620 }
4621 // no rings
4622 else if ( polygon.count() == 1 )
4623 {
4624 if ( destMultipart )
4625 {
4626 return fromMultiPolylineXY( polygon );
4627 }
4628 else
4629 {
4630 return fromPolylineXY( polygon[0] );
4631 }
4632 }
4633 }
4634 return QgsGeometry();
4635 }
4636
4637 default:
4638 return QgsGeometry();
4639 }
4640}
4641
4642QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4643{
4644 switch ( type() )
4645 {
4647 {
4648 if ( !isMultipart() )
4649 return QgsGeometry();
4650
4651 QgsMultiPointXY multiPoint = asMultiPoint();
4652 if ( multiPoint.count() < 3 )
4653 return QgsGeometry();
4654
4655 if ( multiPoint.last() != multiPoint.first() )
4656 multiPoint << multiPoint.first();
4657
4658 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4659 if ( destMultipart )
4660 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4661 else
4662 return fromPolygonXY( polygon );
4663 }
4664
4666 {
4667 // input geometry is multiline
4668 if ( isMultipart() )
4669 {
4670 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4671 QgsMultiPolygonXY multiPolygon;
4672 for ( QgsMultiPolylineXY::iterator multiLineIt = inputMultiLine.begin(); multiLineIt != inputMultiLine.end(); ++multiLineIt )
4673 {
4674 // do not create polygon for a 1 segment line
4675 if ( ( *multiLineIt ).count() < 3 )
4676 return QgsGeometry();
4677 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4678 return QgsGeometry();
4679
4680 // add closing node
4681 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4682 *multiLineIt << ( *multiLineIt ).first();
4683 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4684 }
4685 // check that polygons were inserted
4686 if ( !multiPolygon.isEmpty() )
4687 {
4688 if ( destMultipart )
4689 {
4690 return fromMultiPolygonXY( multiPolygon );
4691 }
4692 else if ( multiPolygon.count() == 1 )
4693 {
4694 // destination is singlepart => make a single part if possible
4695 return fromPolygonXY( multiPolygon[0] );
4696 }
4697 }
4698 }
4699 // input geometry is single line
4700 else
4701 {
4702 QgsPolylineXY line = asPolyline();
4703
4704 // do not create polygon for a 1 segment line
4705 if ( line.count() < 3 )
4706 return QgsGeometry();
4707 if ( line.count() == 3 && line.first() == line.last() )
4708 return QgsGeometry();
4709
4710 // add closing node
4711 if ( line.first() != line.last() )
4712 line << line.first();
4713
4714 // destination is multipart
4715 if ( destMultipart )
4716 {
4717 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4718 }
4719 else
4720 {
4721 return fromPolygonXY( QgsPolygonXY() << line );
4722 }
4723 }
4724 return QgsGeometry();
4725 }
4726
4728 {
4729 bool srcIsMultipart = isMultipart();
4730
4731 if ( ( destMultipart && srcIsMultipart ) ||
4732 ( !destMultipart && ! srcIsMultipart ) )
4733 {
4734 // return a copy of the same geom
4735 return QgsGeometry( *this );
4736 }
4737 if ( destMultipart )
4738 {
4739 // destination is multipart => makes a multipoint with a single polygon
4740 QgsPolygonXY polygon = asPolygon();
4741 if ( !polygon.isEmpty() )
4742 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4743 }
4744 else
4745 {
4746 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4747 if ( multiPolygon.count() == 1 )
4748 {
4749 // destination is singlepart => make a single part if possible
4750 return fromPolygonXY( multiPolygon[0] );
4751 }
4752 }
4753 return QgsGeometry();
4754 }
4755
4756 default:
4757 return QgsGeometry();
4758 }
4759}
4760
4762{
4763 return new QgsGeos( geometry, precision, flags );
4764}
4765
4766QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
4767{
4768 out << geometry.asWkb();
4769 return out;
4770}
4771
4772QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4773{
4774 QByteArray byteArray;
4775 in >> byteArray;
4776 if ( byteArray.isEmpty() )
4777 {
4778 geometry.set( nullptr );
4779 return in;
4780 }
4781
4782 geometry.fromWkb( byteArray );
4783 return in;
4784}
4785
4786
4788{
4789 return mMessage;
4790}
4791
4793{
4794 return mLocation;
4795}
4796
4798{
4799 return mHasLocation;
4800}
4801
4802QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int vertexIndex, double distance1, double distance2, int segments ) const
4803{
4804 QgsDebugMsgLevel( u"%1 starts: %2"_s.arg( qgsEnumValueToKey( op ) ).arg( asWkt( 2 ) ), 3 );
4805 if ( isNull() )
4806 {
4807 mLastError = u"Operation '%1' needs non-null geometry."_s.arg( qgsEnumValueToKey( op ) );
4808 return QgsGeometry();
4809 }
4810
4811 QgsCurve *curve = nullptr;
4812
4813 int modifiedPart = -1;
4814 int modifiedRing = -1;
4815 QgsVertexId vertexId;
4816 if ( !vertexIdFromVertexNr( vertexIndex, vertexId ) )
4817 {
4818 mLastError = u"Invalid vertex index"_s;
4819 return QgsGeometry();
4820 }
4821 int resolvedVertexIndex = vertexId.vertex;
4822 QgsMultiLineString *inputMultiLine = nullptr;
4823 QgsMultiPolygon *inputMultiPoly = nullptr;
4825
4826 if ( geomType == Qgis::GeometryType::Line )
4827 {
4828 if ( isMultipart() )
4829 {
4830 modifiedPart = vertexId.part;
4831
4832 inputMultiLine = qgsgeometry_cast<QgsMultiLineString *>( d->geometry.get() );
4833 curve = dynamic_cast<QgsCurve *>( inputMultiLine->lineStringN( modifiedPart ) );
4834 }
4835 else
4836 {
4837 curve = dynamic_cast<QgsCurve *>( d->geometry.get() );
4838 }
4839 }
4840 else if ( geomType == Qgis::GeometryType::Polygon )
4841 {
4842 QgsPolygon *poly = nullptr;
4843 if ( isMultipart() )
4844 {
4845 modifiedPart = vertexId.part;
4846 // get part, get ring
4847 inputMultiPoly = qgsgeometry_cast<QgsMultiPolygon *>( d->geometry.get() );
4848 poly = inputMultiPoly->polygonN( modifiedPart );
4849 }
4850 else
4851 {
4852 poly = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
4853 }
4854 if ( !poly )
4855 {
4856 mLastError = u"Could not get polygon geometry."_s;
4857 return QgsGeometry();
4858 }
4859
4860 // if has rings
4861 modifiedRing = vertexId.ring;
4862 if ( modifiedRing == 0 )
4863 curve = qgsgeometry_cast<QgsCurve *>( poly->exteriorRing() );
4864 else
4865 curve = qgsgeometry_cast<QgsCurve *>( poly->interiorRing( modifiedRing - 1 ) );
4866 }
4867 else
4868 curve = nullptr;
4869
4870 if ( !curve )
4871 {
4872 mLastError = u"Operation '%1' needs curve geometry."_s.arg( qgsEnumValueToKey( op ) );
4873 return QgsGeometry();
4874 }
4875
4876 std::unique_ptr<QgsAbstractGeometry> result;
4877 try
4878 {
4880 result = QgsGeometryUtils::chamferVertex( curve, resolvedVertexIndex, distance1, distance2 );
4881 else
4882 result = QgsGeometryUtils::filletVertex( curve, resolvedVertexIndex, distance1, segments );
4883 }
4884 catch ( QgsInvalidArgumentException &e )
4885 {
4886 mLastError = u"%1 Requested vertex: %2 was resolved as: [part: %3, ring: %4, vertex: %5]"_s //
4887 .arg( e.what() )
4888 .arg( vertexIndex )
4889 .arg( modifiedPart )
4890 .arg( modifiedRing )
4891 .arg( resolvedVertexIndex );
4892 return QgsGeometry();
4893 }
4894
4895 if ( !result )
4896 {
4897 mLastError = u"Operation '%1' generates a null geometry."_s.arg( qgsEnumValueToKey( op ) );
4898 return QgsGeometry();
4899 }
4900
4901 if ( result->isEmpty() )
4902 return QgsGeometry( std::move( result ) );
4903
4904 // insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon
4905 auto updatePolygon = []( const QgsPolygon * inputPoly, QgsAbstractGeometry * result, int modifiedRing ) -> std::unique_ptr<QgsPolygon>
4906 {
4907 auto newPoly = std::make_unique<QgsPolygon>();
4908 for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex )
4909 {
4910 if ( ringIndex == modifiedRing )
4911 {
4912 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
4913 {
4914 if ( ringIndex == 0 && resPartIte == result->parts_begin() )
4915 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
4916 else
4917 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
4918 }
4919 }
4920 else
4921 {
4922 if ( ringIndex == 0 )
4923 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->exteriorRing()->clone() ) );
4924 else
4925 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->interiorRing( ringIndex - 1 )->clone() ) );
4926 }
4927 }
4928 return newPoly;
4929 };
4930
4931 std::unique_ptr<QgsAbstractGeometry> finalGeom;
4932 if ( geomType == Qgis::GeometryType::Line )
4933 {
4934 if ( modifiedPart >= 0 )
4935 {
4936 auto newMultiLine = std::make_unique<QgsMultiLineString>();
4937 int partIndex = 0;
4938 for ( QgsMultiLineString::part_iterator partIte = inputMultiLine->parts_begin(); partIte != inputMultiLine->parts_end(); ++partIte )
4939 {
4940 if ( partIndex == modifiedPart )
4941 {
4942 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
4943 {
4944 newMultiLine->addGeometry( ( *resPartIte )->clone() );
4945 }
4946 }
4947 else
4948 {
4949 newMultiLine->addGeometry( ( *partIte )->clone() );
4950 }
4951 partIndex++;
4952 }
4953 finalGeom = std::move( newMultiLine );
4954 }
4955 else
4956 {
4957 // resultGeom is already the correct result!
4958 finalGeom = std::move( result );
4959 }
4960 }
4961 else
4962 {
4963 // geomType == Qgis::GeometryType::Polygon
4964 if ( modifiedPart >= 0 )
4965 {
4966 auto newMultiPoly = std::make_unique<QgsMultiPolygon>();
4967 int partIndex = 0;
4968 for ( QgsAbstractGeometry::part_iterator partIte = inputMultiPoly->parts_begin(); partIte != inputMultiPoly->parts_end(); ++partIte )
4969 {
4970 if ( partIndex == modifiedPart )
4971 {
4972 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( *partIte ), result.get(), modifiedRing );
4973 newMultiPoly->addGeometry( newPoly.release() );
4974 }
4975 else
4976 {
4977 newMultiPoly->addGeometry( ( *partIte )->clone() );
4978 }
4979 partIndex++;
4980 }
4981 finalGeom.reset( dynamic_cast<QgsAbstractGeometry *>( newMultiPoly.release() ) );
4982 }
4983 else
4984 {
4985 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( d->geometry.get() ), result.get(), modifiedRing );
4986 finalGeom = std::move( newPoly );
4987 }
4988 }
4989
4990 QgsGeometry finalResult( std::move( finalGeom ) );
4991
4992 QgsDebugMsgLevel( u"Final result Wkt: %1"_s.arg( finalResult.asWkt( 2 ) ), 3 );
4993
4994 return finalResult;
4995}
4996
4997
4998QgsGeometry QgsGeometry::chamfer( int vertexIndex, double distance1, double distance2 ) const
4999{
5000 return doChamferFillet( ChamferFilletOperationType::Chamfer, vertexIndex, distance1, distance2, 0 );
5001}
5002
5003QgsGeometry QgsGeometry::fillet( int vertexIndex, double radius, int segments ) const
5004{
5005 return doChamferFillet( ChamferFilletOperationType::Fillet, vertexIndex, radius, 0.0, segments );
5006}
5007
5008QgsGeometry QgsGeometry::chamfer( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2 )
5009{
5010 std::unique_ptr<QgsLineString> result( QgsGeometryUtils::createChamferGeometry(
5011 segment1Start, segment1End, segment2Start, segment2End, distance1, distance2
5012 ) );
5013
5014 if ( !result )
5015 {
5016 return QgsGeometry();
5017 }
5018
5019 return QgsGeometry( std::move( result ) );
5020}
5021
5022QgsGeometry QgsGeometry::fillet( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments )
5023{
5024 std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::createFilletGeometry(
5025 segment1Start, segment1End, segment2Start, segment2End, radius, segments
5026 ) );
5027
5028 if ( !result )
5029 {
5030 return QgsGeometry();
5031 }
5032
5033 return QgsGeometry( std::move( result ) );
5034}
@ 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:3350
AngularDirection
Angular directions.
Definition qgis.h:3491
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3494
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:3125
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:3335
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:111
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:710
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:7451
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7091
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7450
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
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