QGIS API Documentation 3.43.0-Master (3ee7834ace6)
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 <limits>
17#include <cstdarg>
18#include <cstdio>
19#include <cmath>
20#include <nlohmann/json.hpp>
21#include <QCache>
22
23#include "qgis.h"
24#include "qgsgeometry.h"
25#include "moc_qgsgeometry.cpp"
26#include "qgsabstractgeometry.h"
28#include "qgsgeometryfactory.h"
29
30#include <geos_c.h>
31
32#include "qgsgeometryutils.h"
34#include "qgsgeos.h"
35#include "qgsmaptopixel.h"
36#include "qgspointxy.h"
37#include "qgsrectangle.h"
38
39#include "qgsvectorlayer.h"
41
42#include "qgsmultilinestring.h"
43#include "qgsmultipoint.h"
44#include "qgsmultipolygon.h"
45#include "qgspoint.h"
46#include "qgspolygon.h"
47#include "qgslinestring.h"
48#include "qgscircle.h"
49#include "qgscurve.h"
51#include "qgstriangle.h"
52
54{
56 QgsGeometryPrivate( std::unique_ptr< QgsAbstractGeometry > geometry ): ref( 1 ), geometry( std::move( geometry ) ) {}
57 QAtomicInt ref;
58 std::unique_ptr< QgsAbstractGeometry > geometry;
59};
60
65
67{
68 if ( !d->ref.deref() )
69 delete d;
70}
71
73 : d( new QgsGeometryPrivate() )
74{
75 d->geometry.reset( geom );
76}
77
78QgsGeometry::QgsGeometry( std::unique_ptr<QgsAbstractGeometry> geom )
79 : d( new QgsGeometryPrivate( std::move( geom ) ) )
80{
81}
82
84 : d( other.d )
85{
86 mLastError = other.mLastError;
87 d->ref.ref();
88}
89
91{
92 if ( this != &other )
93 {
94 if ( !d->ref.deref() )
95 {
96 delete d;
97 }
98
99 mLastError = other.mLastError;
100 d = other.d;
101 d->ref.ref();
102 }
103 return *this;
104}
105
106void QgsGeometry::detach()
107{
108 if ( d->ref <= 1 )
109 return;
110
111 std::unique_ptr< QgsAbstractGeometry > cGeom;
112 if ( d->geometry )
113 cGeom.reset( d->geometry->clone() );
114
115 reset( std::move( cGeom ) );
116}
117
118void QgsGeometry::reset( std::unique_ptr<QgsAbstractGeometry> newGeometry )
119{
120 if ( d->ref > 1 )
121 {
122 ( void )d->ref.deref();
123 d = new QgsGeometryPrivate();
124 }
125 d->geometry = std::move( newGeometry );
126}
127
129{
130 return d->geometry.get();
131}
132
134{
135 detach();
136 return d->geometry.get();
137}
138
140{
141 if ( d->geometry.get() == geometry )
142 {
143 return;
144 }
145
146 reset( std::unique_ptr< QgsAbstractGeometry >( geometry ) );
147}
148
150{
151 return !d->geometry;
152}
153
154typedef QCache< QString, QgsGeometry > WktCache;
155Q_GLOBAL_STATIC_WITH_ARGS( WktCache, sWktCache, ( 2000 ) ) // store up to 2000 geometries
156Q_GLOBAL_STATIC( QMutex, sWktMutex )
157
158QgsGeometry QgsGeometry::fromWkt( const QString &wkt )
159{
160 QMutexLocker lock( sWktMutex() );
161 if ( const QgsGeometry *cached = sWktCache()->object( wkt ) )
162 return *cached;
163 const QgsGeometry result( QgsGeometryFactory::geomFromWkt( wkt ) );
164 sWktCache()->insert( wkt, new QgsGeometry( result ), 1 );
165 return result;
166}
167
169{
170 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::fromPointXY( point ) );
171 if ( geom )
172 {
173 return QgsGeometry( geom.release() );
174 }
175 return QgsGeometry();
176}
177
179{
180 return QgsGeometry( point.clone() );
181}
182
184{
185 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolylineXY( polyline );
186 if ( geom )
187 {
188 return QgsGeometry( std::move( geom ) );
189 }
190 return QgsGeometry();
191}
192
194{
195 return QgsGeometry( std::make_unique< QgsLineString >( polyline ) );
196}
197
199{
200 std::unique_ptr< QgsPolygon > geom = QgsGeometryFactory::fromPolygonXY( polygon );
201 if ( geom )
202 {
203 return QgsGeometry( std::move( geom ) );
204 }
205 return QgsGeometry();
206}
207
209{
210 std::unique_ptr< QgsMultiPoint > geom = QgsGeometryFactory::fromMultiPointXY( multipoint );
211 if ( geom )
212 {
213 return QgsGeometry( std::move( geom ) );
214 }
215 return QgsGeometry();
216}
217
219{
220 std::unique_ptr< QgsMultiLineString > geom = QgsGeometryFactory::fromMultiPolylineXY( multiline );
221 if ( geom )
222 {
223 return QgsGeometry( std::move( geom ) );
224 }
225 return QgsGeometry();
226}
227
229{
230 std::unique_ptr< QgsMultiPolygon > geom = QgsGeometryFactory::fromMultiPolygonXY( multipoly );
231 if ( geom )
232 {
233 return QgsGeometry( std::move( geom ) );
234 }
235 return QgsGeometry();
236}
237
239{
240 if ( rect.isNull() )
241 return QgsGeometry();
242
243 auto ext = std::make_unique< QgsLineString >(
244 QVector< double >() << rect.xMinimum()
245 << rect.xMaximum()
246 << rect.xMaximum()
247 << rect.xMinimum()
248 << rect.xMinimum(),
249 QVector< double >() << rect.yMinimum()
250 << rect.yMinimum()
251 << rect.yMaximum()
252 << rect.yMaximum()
253 << rect.yMinimum() );
254 auto polygon = std::make_unique< QgsPolygon >();
255 polygon->setExteriorRing( ext.release() );
256 return QgsGeometry( std::move( polygon ) );
257}
258
260{
261 if ( box.is2d() )
262 {
263 return fromRect( box.toRectangle() );
264 }
265
266 auto polyhedralSurface = std::make_unique< QgsPolyhedralSurface >();
267
268 auto ext1 = std::make_unique< QgsLineString >(
269 QVector< double >() << box.xMinimum()
270 << box.xMinimum()
271 << box.xMaximum()
272 << box.xMaximum()
273 << box.xMinimum(),
274 QVector< double >() << box.yMinimum()
275 << box.yMaximum()
276 << box.yMaximum()
277 << box.yMinimum()
278 << box.yMinimum(),
279 QVector< double >() << box.zMinimum()
280 << box.zMinimum()
281 << box.zMinimum()
282 << box.zMinimum()
283 << box.zMinimum() );
284 auto polygon1 = std::make_unique< QgsPolygon >( ext1.release() );
285 polyhedralSurface->addPatch( polygon1.release() );
286
287 auto ext2 = std::make_unique< QgsLineString >(
288 QVector< double >() << box.xMinimum()
289 << box.xMinimum()
290 << box.xMinimum()
291 << box.xMinimum()
292 << box.xMinimum(),
293 QVector< double >() << box.yMinimum()
294 << box.yMaximum()
295 << box.yMaximum()
296 << box.yMinimum()
297 << box.yMinimum(),
298 QVector< double >() << box.zMinimum()
299 << box.zMinimum()
300 << box.zMaximum()
301 << box.zMaximum()
302 << box.zMinimum() );
303 auto polygon2 = std::make_unique< QgsPolygon >( ext2.release() );
304 polyhedralSurface->addPatch( polygon2.release() );
305
306 auto ext3 = std::make_unique< QgsLineString >(
307 QVector< double >() << box.xMinimum()
308 << box.xMaximum()
309 << box.xMaximum()
310 << box.xMinimum()
311 << box.xMinimum(),
312 QVector< double >() << box.yMinimum()
313 << box.yMinimum()
314 << box.yMinimum()
315 << box.yMinimum()
316 << box.yMinimum(),
317 QVector< double >() << box.zMinimum()
318 << box.zMinimum()
319 << box.zMaximum()
320 << box.zMaximum()
321 << box.zMinimum() );
322 auto polygon3 = std::make_unique< QgsPolygon >( ext3.release() );
323 polyhedralSurface->addPatch( polygon3.release() );
324
325 auto ext4 = std::make_unique< QgsLineString >(
326 QVector< double >() << box.xMaximum()
327 << box.xMaximum()
328 << box.xMinimum()
329 << box.xMinimum()
330 << box.xMaximum(),
331 QVector< double >() << box.yMaximum()
332 << box.yMinimum()
333 << box.yMinimum()
334 << box.yMaximum()
335 << box.yMaximum(),
336 QVector< double >() << box.zMaximum()
337 << box.zMaximum()
338 << box.zMaximum()
339 << box.zMaximum()
340 << box.zMaximum() );
341 auto polygon4 = std::make_unique< QgsPolygon >( ext4.release() );
342 polyhedralSurface->addPatch( polygon4.release() );
343
344 auto ext5 = std::make_unique< QgsLineString >(
345 QVector< double >() << box.xMaximum()
346 << box.xMaximum()
347 << box.xMaximum()
348 << box.xMaximum()
349 << box.xMaximum(),
350 QVector< double >() << box.yMaximum()
351 << box.yMinimum()
352 << box.yMinimum()
353 << box.yMaximum()
354 << box.yMaximum(),
355 QVector< double >() << box.zMaximum()
356 << box.zMaximum()
357 << box.zMinimum()
358 << box.zMinimum()
359 << box.zMaximum() );
360 auto polygon5 = std::make_unique< QgsPolygon >( ext5.release() );
361 polyhedralSurface->addPatch( polygon5.release() );
362
363 auto ext6 = std::make_unique< QgsLineString >(
364 QVector< double >() << box.xMaximum()
365 << box.xMaximum()
366 << box.xMinimum()
367 << box.xMinimum()
368 << box.xMaximum(),
369 QVector< double >() << box.yMaximum()
370 << box.yMaximum()
371 << box.yMaximum()
372 << box.yMaximum()
373 << box.yMaximum(),
374 QVector< double >() << box.zMaximum()
375 << box.zMinimum()
376 << box.zMinimum()
377 << box.zMaximum()
378 << box.zMaximum() );
379 auto polygon6 = std::make_unique< QgsPolygon >( ext6.release() );
380 polyhedralSurface->addPatch( polygon6.release() );
381
382 return QgsGeometry( std::move( polyhedralSurface ) );
383}
384
385QgsGeometry QgsGeometry::collectGeometry( const QVector< QgsGeometry > &geometries )
386{
387 QgsGeometry collected;
388
389 for ( const QgsGeometry &g : geometries )
390 {
391 if ( collected.isNull() )
392 {
393 collected = g;
394 collected.convertToMultiType();
395 }
396 else
397 {
398 if ( g.isMultipart() )
399 {
400 for ( auto p = g.const_parts_begin(); p != g.const_parts_end(); ++p )
401 {
402 collected.addPartV2( ( *p )->clone() );
403 }
404 }
405 else
406 {
407 collected.addPart( g );
408 }
409 }
410 }
411 return collected;
412}
413
414QgsGeometry QgsGeometry::createWedgeBuffer( const QgsPoint &center, const double azimuth, const double angularWidth, const double outerRadius, const double innerRadius )
415{
416 const double startAngle = azimuth - angularWidth * 0.5;
417 const double endAngle = azimuth + angularWidth * 0.5;
418
419 return createWedgeBufferFromAngles( center, startAngle, endAngle, outerRadius, innerRadius );
420}
421
422QgsGeometry QgsGeometry::createWedgeBufferFromAngles( const QgsPoint &center, double startAngle, double endAngle, double outerRadius, double innerRadius )
423{
424 auto wedge = std::make_unique< QgsCompoundCurve >();
425
426 const double DEG_TO_RAD = M_PI / 180.0;
427 const double RAD_TO_DEG = 180.0 / M_PI;
428
429 const double angularWidth = endAngle - startAngle;
430 const bool useShortestArc = QgsGeometryUtilsBase::normalizedAngle( angularWidth * DEG_TO_RAD ) * RAD_TO_DEG <= 180.0;
431
432 if ( std::abs( angularWidth ) >= 360.0 )
433 {
434 auto outerCc = std::make_unique< QgsCompoundCurve >();
435
436 QgsCircle outerCircle = QgsCircle( center, outerRadius );
437 outerCc->addCurve( outerCircle.toCircularString() );
438
439 auto cp = std::make_unique< QgsCurvePolygon >();
440 cp->setExteriorRing( outerCc.release() );
441
442 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
443 {
444 auto innerCc = std::make_unique< QgsCompoundCurve >();
445
446 QgsCircle innerCircle = QgsCircle( center, innerRadius );
447 innerCc->addCurve( innerCircle.toCircularString() );
448
449 cp->setInteriorRings( { innerCc.release() } );
450 }
451
452 return QgsGeometry( std::move( cp ) );
453 }
454
455 const QgsPoint outerP1 = center.project( outerRadius, startAngle );
456 const QgsPoint outerP2 = center.project( outerRadius, endAngle );
457
458 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( outerP1, outerP2, center, useShortestArc ) ) );
459
460 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
461 {
462 const QgsPoint innerP1 = center.project( innerRadius, startAngle );
463 const QgsPoint innerP2 = center.project( innerRadius, endAngle );
464 wedge->addCurve( new QgsLineString( outerP2, innerP2 ) );
465 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( innerP2, innerP1, center, useShortestArc ) ) );
466 wedge->addCurve( new QgsLineString( innerP1, outerP1 ) );
467 }
468 else
469 {
470 wedge->addCurve( new QgsLineString( outerP2, center ) );
471 wedge->addCurve( new QgsLineString( center, outerP1 ) );
472 }
473
474 auto cp = std::make_unique< QgsCurvePolygon >();
475 cp->setExteriorRing( wedge.release() );
476 return QgsGeometry( std::move( cp ) );
477}
478
479void QgsGeometry::fromWkb( unsigned char *wkb, int length )
480{
481 QgsConstWkbPtr ptr( wkb, length );
482 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
483 delete [] wkb;
484}
485
486void QgsGeometry::fromWkb( const QByteArray &wkb )
487{
488 QgsConstWkbPtr ptr( wkb );
489 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
490}
491
493{
494 if ( !d->geometry )
495 {
497 }
498 else
499 {
500 return d->geometry->wkbType();
501 }
502}
503
505{
506 if ( !d->geometry )
507 {
509 }
510 return QgsWkbTypes::geometryType( d->geometry->wkbType() );
511}
512
514{
515 if ( !d->geometry )
516 {
517 return true;
518 }
519
520 return d->geometry->isEmpty();
521}
522
524{
525 if ( !d->geometry )
526 {
527 return false;
528 }
529 return QgsWkbTypes::isMultiType( d->geometry->wkbType() );
530}
531QgsPointXY QgsGeometry::closestVertex( const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist ) const
532{
533 if ( !d->geometry )
534 {
535 sqrDist = -1;
536 return QgsPointXY();
537 }
538
539 QgsPoint pt( point );
540 QgsVertexId id;
541
542 QgsPoint vp = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, id );
543 if ( !id.isValid() )
544 {
545 sqrDist = -1;
546 return QgsPointXY();
547 }
548 sqrDist = QgsGeometryUtils::sqrDistance2D( pt, vp );
549
550 QgsVertexId prevVertex;
551 QgsVertexId nextVertex;
552 d->geometry->adjacentVertices( id, prevVertex, nextVertex );
553 closestVertexIndex = vertexNrFromVertexId( id );
554 previousVertexIndex = vertexNrFromVertexId( prevVertex );
555 nextVertexIndex = vertexNrFromVertexId( nextVertex );
556 return QgsPointXY( vp.x(), vp.y() );
557}
558
559double QgsGeometry::distanceToVertex( int vertex ) const
560{
561 if ( !d->geometry )
562 {
563 return -1;
564 }
565
566 QgsVertexId id;
567 if ( !vertexIdFromVertexNr( vertex, id ) )
568 {
569 return -1;
570 }
571
572 return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id );
573}
574
575double QgsGeometry::angleAtVertex( int vertex ) const
576{
577 if ( !d->geometry )
578 {
579 return 0;
580 }
581
582 QgsVertexId v2;
583 if ( !vertexIdFromVertexNr( vertex, v2 ) )
584 {
585 return 0;
586 }
587
588 return d->geometry->vertexAngle( v2 );
589}
590
591void QgsGeometry::adjacentVertices( int atVertex, int &beforeVertex, int &afterVertex ) const
592{
593 if ( !d->geometry )
594 {
595 return;
596 }
597
598 QgsVertexId id;
599 if ( !vertexIdFromVertexNr( atVertex, id ) )
600 {
601 beforeVertex = -1;
602 afterVertex = -1;
603 return;
604 }
605
606 QgsVertexId beforeVertexId, afterVertexId;
607 d->geometry->adjacentVertices( id, beforeVertexId, afterVertexId );
608 beforeVertex = vertexNrFromVertexId( beforeVertexId );
609 afterVertex = vertexNrFromVertexId( afterVertexId );
610}
611
612bool QgsGeometry::moveVertex( double x, double y, int atVertex )
613{
614 if ( !d->geometry )
615 {
616 return false;
617 }
618
619 QgsVertexId id;
620 if ( !vertexIdFromVertexNr( atVertex, id ) )
621 {
622 return false;
623 }
624
625 detach();
626
627 return d->geometry->moveVertex( id, QgsPoint( x, y ) );
628}
629
630bool QgsGeometry::moveVertex( const QgsPoint &p, int atVertex )
631{
632 if ( !d->geometry )
633 {
634 return false;
635 }
636
637 QgsVertexId id;
638 if ( !vertexIdFromVertexNr( atVertex, id ) )
639 {
640 return false;
641 }
642
643 detach();
644
645 return d->geometry->moveVertex( id, p );
646}
647
648bool QgsGeometry::deleteVertex( int atVertex )
649{
650 if ( !d->geometry )
651 {
652 return false;
653 }
654
655 //maintain compatibility with < 2.10 API
657 {
658 detach();
659 //delete geometry instead of point
660 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->removeGeometry( atVertex );
661 }
662
663 //if it is a point, set the geometry to nullptr
664 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
665 {
666 reset( nullptr );
667 return true;
668 }
669
670 QgsVertexId id;
671 if ( !vertexIdFromVertexNr( atVertex, id ) )
672 {
673 return false;
674 }
675
676 detach();
677
678 return d->geometry->deleteVertex( id );
679}
680
682{
683
684 if ( !d->geometry )
685 return false;
686
687 QgsVertexId id;
688 if ( !vertexIdFromVertexNr( atVertex, id ) )
689 return false;
690
691 detach();
692
693 QgsAbstractGeometry *geom = d->geometry.get();
694
695 // If the geom is a collection, we get the concerned part, otherwise, the part is just the whole geom
696 QgsAbstractGeometry *part = nullptr;
697 QgsGeometryCollection *owningCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
698 if ( owningCollection )
699 part = owningCollection->geometryN( id.part );
700 else
701 part = geom;
702
703 // If the part is a polygon, we get the concerned ring, otherwise, the ring is just the whole part
704 QgsAbstractGeometry *ring = nullptr;
705 QgsCurvePolygon *owningPolygon = qgsgeometry_cast<QgsCurvePolygon *>( part );
706 if ( owningPolygon )
707 ring = ( id.ring == 0 ) ? owningPolygon->exteriorRing() : owningPolygon->interiorRing( id.ring - 1 );
708 else
709 ring = part;
710
711 // If the ring is not a curve, we're probably on a point geometry
712 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( ring );
713 if ( !curve )
714 return false;
715
716 bool success = false;
717 QgsCompoundCurve *cpdCurve = qgsgeometry_cast<QgsCompoundCurve *>( curve );
718 if ( cpdCurve )
719 {
720 // If the geom is a already compound curve, we convert inplace, and we're done
721 success = cpdCurve->toggleCircularAtVertex( id );
722 }
723 else
724 {
725 // TODO : move this block before the above, so we call toggleCircularAtVertex only in one place
726 // If the geom is a linestring or cirularstring, we create a compound curve
727 auto cpdCurve = std::make_unique<QgsCompoundCurve>();
728 cpdCurve->addCurve( curve->clone() );
729 success = cpdCurve->toggleCircularAtVertex( QgsVertexId( -1, -1, id.vertex ) );
730
731 // In that case, we must also reassign the instances
732 if ( success )
733 {
734 if ( !owningPolygon && !owningCollection )
735 {
736 // Standalone linestring
737 reset( std::make_unique<QgsCompoundCurve>( *cpdCurve ) ); // <- REVIEW PLZ
738 }
739 else if ( owningPolygon )
740 {
741 // Replace the ring in the owning polygon
742 if ( id.ring == 0 )
743 {
744 owningPolygon->setExteriorRing( cpdCurve.release() );
745 }
746 else
747 {
748 owningPolygon->removeInteriorRing( id.ring - 1 );
749 owningPolygon->addInteriorRing( cpdCurve.release() );
750 }
751 }
752 else if ( owningCollection )
753 {
754 // Replace the curve in the owning collection
755 owningCollection->removeGeometry( id.part );
756 owningCollection->insertGeometry( cpdCurve.release(), id.part );
757 }
758 }
759 }
760
761 return success;
762}
763
764bool QgsGeometry::insertVertex( double x, double y, int beforeVertex )
765{
766 if ( !d->geometry )
767 {
768 return false;
769 }
770
771 //maintain compatibility with < 2.10 API
773 {
774 detach();
775 //insert geometry instead of point
776 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( x, y ), beforeVertex );
777 }
778
779 QgsVertexId id;
780 if ( !vertexIdFromVertexNr( beforeVertex, id ) )
781 {
782 return false;
783 }
784
785 detach();
786
787 return d->geometry->insertVertex( id, QgsPoint( x, y ) );
788}
789
790bool QgsGeometry::insertVertex( const QgsPoint &point, int beforeVertex )
791{
792 if ( !d->geometry )
793 {
794 return false;
795 }
796
797 //maintain compatibility with < 2.10 API
799 {
800 detach();
801 //insert geometry instead of point
802 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->insertGeometry( new QgsPoint( point ), beforeVertex );
803 }
804
805 QgsVertexId id;
806 if ( !vertexIdFromVertexNr( beforeVertex, id ) )
807 {
808 return false;
809 }
810
811 detach();
812
813 return d->geometry->insertVertex( id, point );
814}
815
816bool QgsGeometry::addTopologicalPoint( const QgsPoint &point, double snappingTolerance, double segmentSearchEpsilon )
817{
818 if ( !d->geometry )
819 {
820 return false;
821 }
822
823 const double sqrSnappingTolerance = snappingTolerance * snappingTolerance;
824 int segmentAfterVertex;
825 QgsPointXY snappedPoint;
826 const double sqrDistSegmentSnap = closestSegmentWithContext( point, snappedPoint, segmentAfterVertex, nullptr, segmentSearchEpsilon );
827
828 if ( sqrDistSegmentSnap > sqrSnappingTolerance )
829 return false;
830
831 int atVertex, beforeVertex, afterVertex;
832 double sqrDistVertexSnap;
833 closestVertex( point, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap );
834
835 if ( sqrDistVertexSnap < sqrSnappingTolerance )
836 return false; // the vertex already exists - do not insert it
837
838 if ( !insertVertex( point, segmentAfterVertex ) )
839 {
840 QgsDebugError( QStringLiteral( "failed to insert topo point" ) );
841 return false;
842 }
843
844 return true;
845}
846
847QgsPoint QgsGeometry::vertexAt( int atVertex ) const
848{
849 if ( !d->geometry )
850 {
851 return QgsPoint();
852 }
853
854 QgsVertexId vId;
855 ( void )vertexIdFromVertexNr( atVertex, vId );
856 if ( vId.vertex < 0 )
857 {
858 return QgsPoint();
859 }
860 return d->geometry->vertexAt( vId );
861}
862
863double QgsGeometry::sqrDistToVertexAt( QgsPointXY &point, int atVertex ) const
864{
865 QgsPointXY vertexPoint = vertexAt( atVertex );
866 return QgsGeometryUtils::sqrDistance2D( QgsPoint( vertexPoint ), QgsPoint( point ) );
867}
868
870{
871 // avoid calling geos for trivial point calculations
872 if ( d->geometry && QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
873 {
874 return QgsGeometry( qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->clone() );
875 }
876
877 QgsGeos geos( d->geometry.get() );
878 mLastError.clear();
879 QgsGeometry result = QgsGeometry( geos.closestPoint( other ) );
880 result.mLastError = mLastError;
881 return result;
882}
883
885{
886 // avoid calling geos for trivial point-to-point line calculations
888 {
889 return QgsGeometry( std::make_unique< QgsLineString >( *qgsgeometry_cast< const QgsPoint * >( d->geometry.get() ), *qgsgeometry_cast< const QgsPoint * >( other.constGet() ) ) );
890 }
891
892 QgsGeos geos( d->geometry.get() );
893 mLastError.clear();
894 QgsGeometry result = QgsGeometry( geos.shortestLine( other, &mLastError ) );
895 result.mLastError = mLastError;
896 return result;
897}
898
899double QgsGeometry::closestVertexWithContext( const QgsPointXY &point, int &atVertex ) const
900{
901 if ( !d->geometry )
902 {
903 return -1;
904 }
905
906 QgsVertexId vId;
907 QgsPoint pt( point );
908 QgsPoint closestPoint = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, vId );
909 if ( !vId.isValid() )
910 return -1;
911 atVertex = vertexNrFromVertexId( vId );
912 return QgsGeometryUtils::sqrDistance2D( closestPoint, pt );
913}
914
916 QgsPointXY &minDistPoint,
917 int &nextVertexIndex,
918 int *leftOrRightOfSegment,
919 double epsilon ) const
920{
921 if ( !d->geometry )
922 {
923 return -1;
924 }
925
926 QgsPoint segmentPt;
927 QgsVertexId vertexAfter;
928
929 double sqrDist = d->geometry->closestSegment( QgsPoint( point ), segmentPt, vertexAfter, leftOrRightOfSegment, epsilon );
930 if ( sqrDist < 0 )
931 return -1;
932
933 minDistPoint.setX( segmentPt.x() );
934 minDistPoint.setY( segmentPt.y() );
935 nextVertexIndex = vertexNrFromVertexId( vertexAfter );
936 return sqrDist;
937}
938
939Qgis::GeometryOperationResult QgsGeometry::addRing( const QVector<QgsPointXY> &ring )
940{
941 auto ringLine = std::make_unique< QgsLineString >( ring );
942 return addRing( ringLine.release() );
943}
944
946{
947 std::unique_ptr< QgsCurve > r( ring );
948 if ( !d->geometry )
949 {
951 }
952
953 detach();
954
955 return QgsGeometryEditUtils::addRing( d->geometry.get(), std::move( r ) );
956}
957
958Qgis::GeometryOperationResult QgsGeometry::addPart( const QVector<QgsPointXY> &points, Qgis::GeometryType geomType )
959{
961 convertPointList( points, l );
963 return addPart( l, geomType );
965}
966
967Qgis::GeometryOperationResult QgsGeometry::addPartV2( const QVector<QgsPointXY> &points, Qgis::WkbType wkbType )
968{
970 convertPointList( points, l );
971 return addPartV2( l, wkbType );
972}
973
975{
976 std::unique_ptr< QgsAbstractGeometry > partGeom;
977 if ( points.size() == 1 )
978 {
979 partGeom = std::make_unique< QgsPoint >( points[0] );
980 }
981 else if ( points.size() > 1 )
982 {
983 auto ringLine = std::make_unique< QgsLineString >();
984 ringLine->setPoints( points );
985 partGeom = std::move( ringLine );
986 }
988 return addPart( partGeom.release(), geomType );
990}
991
993{
994 std::unique_ptr< QgsAbstractGeometry > partGeom;
995 if ( points.size() == 1 )
996 {
997 partGeom = std::make_unique< QgsPoint >( points[0] );
998 }
999 else if ( points.size() > 1 )
1000 {
1001 auto ringLine = std::make_unique< QgsLineString >();
1002 ringLine->setPoints( points );
1003 partGeom = std::move( ringLine );
1004 }
1005 return addPartV2( partGeom.release(), wkbType );
1006}
1007
1009{
1010 std::unique_ptr< QgsAbstractGeometry > p( part );
1011 if ( !d->geometry )
1012 {
1013 switch ( geomType )
1014 {
1016 reset( std::make_unique< QgsMultiPoint >() );
1017 break;
1019 reset( std::make_unique< QgsMultiLineString >() );
1020 break;
1022 reset( std::make_unique< QgsMultiPolygon >() );
1023 break;
1024 default:
1025 reset( nullptr );
1027 }
1028 }
1029 else
1030 {
1031 detach();
1032 }
1033
1035 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
1036}
1037
1039{
1040 std::unique_ptr< QgsAbstractGeometry > p( part );
1041 if ( !d->geometry )
1042 {
1044 {
1046 reset( std::make_unique< QgsMultiPoint >() );
1047 break;
1049 reset( std::make_unique< QgsMultiLineString >() );
1050 break;
1053 reset( std::make_unique< QgsMultiPolygon >() );
1054 break;
1056 reset( std::make_unique< QgsMultiSurface >() );
1057 break;
1060 reset( std::make_unique< QgsMultiCurve >() );
1061 break;
1062 default:
1063 reset( nullptr );
1065 }
1066 }
1067 else
1068 {
1069 detach();
1071 }
1072
1073 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
1074}
1075
1077{
1078 if ( !d->geometry )
1079 {
1081 }
1082 if ( newPart.isNull() || !newPart.d->geometry )
1083 {
1085 }
1086
1087 return addPartV2( newPart.d->geometry->clone() );
1088}
1089
1090QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
1091{
1092 if ( !d->geometry || type() != Qgis::GeometryType::Polygon )
1093 {
1094 return QgsGeometry();
1095 }
1096
1097 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
1098 {
1099 const QVector<QgsGeometry> parts = asGeometryCollection();
1100 QVector<QgsGeometry> results;
1101 results.reserve( parts.count() );
1102 for ( const QgsGeometry &part : parts )
1103 {
1104 QgsGeometry result = part.removeInteriorRings( minimumRingArea );
1105 if ( !result.isNull() )
1106 results << result;
1107 }
1108 if ( results.isEmpty() )
1109 return QgsGeometry();
1110
1111 QgsGeometry first = results.takeAt( 0 );
1112 for ( const QgsGeometry &result : std::as_const( results ) )
1113 {
1114 first.addPart( result );
1115 }
1116 return first;
1117 }
1118 else
1119 {
1120 std::unique_ptr< QgsCurvePolygon > newPoly( static_cast< QgsCurvePolygon * >( d->geometry->clone() ) );
1121 newPoly->removeInteriorRings( minimumRingArea );
1122 return QgsGeometry( std::move( newPoly ) );
1123 }
1124}
1125
1126Qgis::GeometryOperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
1127{
1128 if ( !d->geometry )
1129 {
1131 }
1132
1133 detach();
1134
1135 d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
1137}
1138
1140{
1141 if ( !d->geometry )
1142 {
1144 }
1145
1146 detach();
1147
1148 QTransform t = QTransform::fromTranslate( center.x(), center.y() );
1149 t.rotate( -rotation );
1150 t.translate( -center.x(), -center.y() );
1151 d->geometry->transform( t );
1153}
1154
1155Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature )
1156{
1157 QgsPointSequence split, topology;
1158 convertPointList( splitLine, split );
1159 convertPointList( topologyTestPoints, topology );
1160 Qgis::GeometryOperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
1161 convertPointList( topology, topologyTestPoints );
1162 return result;
1163}
1164Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest )
1165{
1166 if ( !d->geometry )
1167 {
1169 }
1170
1171 // We're trying adding the split line's vertices to the geometry so that
1172 // snap to segment always produces a valid split (see https://github.com/qgis/QGIS/issues/29270)
1173 QgsGeometry tmpGeom( *this );
1174 for ( const QgsPoint &v : splitLine )
1175 {
1176 tmpGeom.addTopologicalPoint( v );
1177 }
1178
1179 QVector<QgsGeometry > newGeoms;
1180 QgsLineString splitLineString( splitLine );
1181
1182 QgsGeos geos( tmpGeom.get() );
1183 mLastError.clear();
1184 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
1185
1186 if ( result == QgsGeometryEngine::Success )
1187 {
1188 if ( splitFeature )
1189 *this = newGeoms.takeAt( 0 );
1190 newGeometries = newGeoms;
1191 }
1192
1193 switch ( result )
1194 {
1209 //default: do not implement default to handle properly all cases
1210 }
1211
1212 // this should never be reached
1213 Q_ASSERT( false );
1215}
1216
1217Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature )
1218{
1219 std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
1220 QgsPointSequence points;
1221 segmentizedLine->points( points );
1222 Qgis::GeometryOperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
1223
1225 {
1226 if ( preserveCircular )
1227 {
1228 for ( int i = 0; i < newGeometries.count(); ++i )
1229 newGeometries[i] = newGeometries[i].convertToCurves();
1230 *this = convertToCurves();
1231 }
1232 }
1233
1234 return result;
1235}
1236
1238{
1239 if ( !d->geometry )
1240 {
1242 }
1243
1244 QgsGeos geos( d->geometry.get() );
1246 mLastError.clear();
1247 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1248 if ( errorCode == QgsGeometryEngine::Success && geom )
1249 {
1250 reset( std::move( geom ) );
1252 }
1253
1254 switch ( errorCode )
1255 {
1266 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1270 }
1271
1272 // should not be reached
1274}
1275
1277{
1278 if ( !d->geometry || !other.d->geometry )
1279 {
1280 return 0;
1281 }
1282
1283 QgsGeos geos( d->geometry.get() );
1284
1285 mLastError.clear();
1286 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1287 if ( !diffGeom )
1288 {
1289 return 1;
1290 }
1291
1292 reset( std::move( diffGeom ) );
1293 return 0;
1294}
1295
1297{
1298 if ( !d->geometry || other.isNull() )
1299 {
1300 return QgsGeometry();
1301 }
1302
1303 QgsGeos geos( d->geometry.get() );
1304
1305 mLastError.clear();
1306 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1307 if ( !diffGeom )
1308 {
1309 QgsGeometry result;
1310 result.mLastError = mLastError;
1311 return result;
1312 }
1313
1314 return QgsGeometry( diffGeom.release() );
1315}
1316
1318{
1319 if ( d->geometry )
1320 {
1321 return d->geometry->boundingBox();
1322 }
1323 return QgsRectangle();
1324}
1325
1327{
1328 if ( d->geometry )
1329 {
1330 return d->geometry->boundingBox3D();
1331 }
1332 return QgsBox3D();
1333}
1334
1335
1336QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1337{
1338 mLastError.clear();
1339 QgsInternalGeometryEngine engine( *this );
1340 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1341 if ( res.isNull() )
1342 mLastError = engine.lastError();
1343 return res;
1344}
1345
1347{
1348 double area, angle, width, height;
1349 return orientedMinimumBoundingBox( area, angle, width, height );
1350}
1351
1352static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1353{
1354 auto l_boundary = boundary.length();
1355 QgsCircle circ_mec;
1356 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1357 {
1358 switch ( l_boundary )
1359 {
1360 case 0:
1361 circ_mec = QgsCircle();
1362 break;
1363 case 1:
1364 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1365 boundary.pop_back();
1366 break;
1367 case 2:
1368 {
1369 QgsPointXY p1 = boundary.last();
1370 boundary.pop_back();
1371 QgsPointXY p2 = boundary.last();
1372 boundary.pop_back();
1373 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1374 }
1375 break;
1376 default:
1377 QgsPoint p1( boundary.at( 0 ) );
1378 QgsPoint p2( boundary.at( 1 ) );
1379 QgsPoint p3( boundary.at( 2 ) );
1380 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1381 break;
1382 }
1383 return circ_mec;
1384 }
1385 else
1386 {
1387 QgsPointXY pxy = points.last();
1388 points.pop_back();
1389 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1390 QgsPoint p( pxy );
1391 if ( !circ_mec.contains( p ) )
1392 {
1393 boundary.append( pxy );
1394 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1395 }
1396 }
1397 return circ_mec;
1398}
1399
1400QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1401{
1402 center = QgsPointXY();
1403 radius = 0;
1404
1405 if ( isEmpty() )
1406 {
1407 return QgsGeometry();
1408 }
1409
1410 /* optimization */
1411 QgsGeometry hull = convexHull();
1412 if ( hull.isNull() )
1413 return QgsGeometry();
1414
1415 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1417
1418 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1419 center = QgsPointXY( circ.center() );
1420 radius = circ.radius();
1421 QgsGeometry geom;
1422 geom.set( circ.toPolygon( segments ) );
1423 return geom;
1424
1425}
1426
1428{
1429 QgsPointXY center;
1430 double radius;
1431 return minimalEnclosingCircle( center, radius, segments );
1432
1433}
1434
1435QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1436{
1437 QgsInternalGeometryEngine engine( *this );
1438
1439 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1440}
1441
1442QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1443{
1444 QgsInternalGeometryEngine engine( *this );
1445 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1446}
1447
1448QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1449{
1450 QgsInternalGeometryEngine engine( *this );
1451 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1452}
1453
1454QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1455{
1456 QgsInternalGeometryEngine engine( *this );
1457 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1458}
1459
1460QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1461{
1462 QgsInternalGeometryEngine engine( *this );
1463 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1464}
1465
1466QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1467{
1468 QgsInternalGeometryEngine engine( *this );
1469 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1470}
1471
1472QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1473{
1474 QgsInternalGeometryEngine engine( *this );
1475 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1476}
1477
1478QgsGeometry QgsGeometry::applyDashPattern( const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset ) const
1479{
1480 QgsInternalGeometryEngine engine( *this );
1481 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1482}
1483
1484QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1485{
1486 if ( !d->geometry )
1487 {
1488 return QgsGeometry();
1489 }
1490 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1491}
1492
1493bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1494{
1495 if ( !d->geometry )
1496 return false;
1497
1498 detach();
1499 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1500}
1501
1503{
1504 // fast case, check bounding boxes
1505 if ( !boundingBoxIntersects( r ) )
1506 return false;
1507
1508 const Qgis::WkbType flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1509 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1510 if ( flatType == Qgis::WkbType::Point )
1511 {
1512 return true;
1513 }
1514
1515 // Workaround for issue issue GH #51429
1516 // in case of multi polygon, intersection with an empty rect fails
1517 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1518 {
1519 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1520 return contains( QgsGeometry::fromPointXY( center ) );
1521 }
1522
1523 QgsGeometry g = fromRect( r );
1524 return intersects( g );
1525}
1526
1527bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1528{
1529 if ( !d->geometry || geometry.isNull() )
1530 {
1531 return false;
1532 }
1533
1534 QgsGeos geos( d->geometry.get() );
1535 mLastError.clear();
1536 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1537}
1538
1540{
1541 if ( !d->geometry )
1542 {
1543 return false;
1544 }
1545
1546 return d->geometry->boundingBoxIntersects( rectangle );
1547}
1548
1550{
1551 if ( !d->geometry || geometry.isNull() )
1552 {
1553 return false;
1554 }
1555
1556 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1557}
1558
1559bool QgsGeometry::contains( const QgsPointXY *p ) const
1560{
1561 if ( !d->geometry || !p )
1562 {
1563 return false;
1564 }
1565
1566 QgsGeos geos( d->geometry.get() );
1567 mLastError.clear();
1568 return geos.contains( p->x(), p->y(), &mLastError );
1569}
1570
1571bool QgsGeometry::contains( double x, double y ) const
1572{
1573 if ( !d->geometry )
1574 {
1575 return false;
1576 }
1577
1578 QgsGeos geos( d->geometry.get() );
1579 mLastError.clear();
1580 return geos.contains( x, y, &mLastError );
1581}
1582
1583bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1584{
1585 if ( !d->geometry || geometry.isNull() )
1586 {
1587 return false;
1588 }
1589
1590 QgsGeos geos( d->geometry.get() );
1591 mLastError.clear();
1592 return geos.contains( geometry.d->geometry.get(), &mLastError );
1593}
1594
1595bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1596{
1597 if ( !d->geometry || geometry.isNull() )
1598 {
1599 return false;
1600 }
1601
1602 QgsGeos geos( d->geometry.get() );
1603 mLastError.clear();
1604 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1605}
1606
1607bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1608{
1609 if ( !d->geometry || geometry.isNull() )
1610 {
1611 return false;
1612 }
1613
1614 // fast check - are they shared copies of the same underlying geometry?
1615 if ( d == geometry.d )
1616 return true;
1617
1618 // fast check - distinct geometry types?
1619 if ( type() != geometry.type() )
1620 return false;
1621
1622 // slower check - actually test the geometries
1623 return *d->geometry == *geometry.d->geometry;
1624}
1625
1626bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1627{
1628 if ( !d->geometry || geometry.isNull() )
1629 {
1630 return false;
1631 }
1632
1633 QgsGeos geos( d->geometry.get() );
1634 mLastError.clear();
1635 return geos.touches( geometry.d->geometry.get(), &mLastError );
1636}
1637
1638bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1639{
1640 if ( !d->geometry || geometry.isNull() )
1641 {
1642 return false;
1643 }
1644
1645 QgsGeos geos( d->geometry.get() );
1646 mLastError.clear();
1647 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1648}
1649
1650bool QgsGeometry::within( const QgsGeometry &geometry ) const
1651{
1652 if ( !d->geometry || geometry.isNull() )
1653 {
1654 return false;
1655 }
1656
1657 QgsGeos geos( d->geometry.get() );
1658 mLastError.clear();
1659 return geos.within( geometry.d->geometry.get(), &mLastError );
1660}
1661
1662bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1663{
1664 if ( !d->geometry || geometry.isNull() )
1665 {
1666 return false;
1667 }
1668
1669 QgsGeos geos( d->geometry.get() );
1670 mLastError.clear();
1671 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1672}
1673
1674QString QgsGeometry::asWkt( int precision ) const
1675{
1676 if ( !d->geometry )
1677 {
1678 return QString();
1679 }
1680 return d->geometry->asWkt( precision );
1681}
1682
1683QString QgsGeometry::asJson( int precision ) const
1684{
1685 return QString::fromStdString( asJsonObject( precision ).dump() );
1686}
1687
1689{
1690 if ( !d->geometry )
1691 {
1692 return nullptr;
1693 }
1694 return d->geometry->asJsonObject( precision );
1695
1696}
1697
1698QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM ) const
1699{
1700 QVector< QgsGeometry > res;
1701 if ( isNull() )
1702 return res;
1703
1704 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1705 {
1706 res << *this;
1707 return res;
1708 }
1709
1711 {
1712 return res;
1713 }
1714
1715 QgsGeometry newGeom = *this;
1716
1717 // Curved -> straight
1719 {
1720 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1721 }
1722
1723 // polygon -> line
1725 newGeom.type() == Qgis::GeometryType::Polygon )
1726 {
1727 // boundary gives us a (multi)line string of exterior + interior rings
1728 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1729 }
1730 // line -> polygon
1732 newGeom.type() == Qgis::GeometryType::Line )
1733 {
1734 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1735 const QgsGeometry source = newGeom;
1736 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1737 {
1738 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1739 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1740 {
1742 {
1743 auto cp = std::make_unique< QgsCurvePolygon >();
1744 cp->setExteriorRing( curve );
1745 ( void )exterior.release();
1746 gc->addGeometry( cp.release() );
1747 }
1748 else
1749 {
1750 auto p = std::make_unique< QgsPolygon >();
1751 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1752 ( void )exterior.release();
1753 gc->addGeometry( p.release() );
1754 }
1755 }
1756 }
1757 newGeom = QgsGeometry( std::move( gc ) );
1758 }
1759
1760 // line/polygon -> points
1762 ( newGeom.type() == Qgis::GeometryType::Line ||
1763 newGeom.type() == Qgis::GeometryType::Polygon ) )
1764 {
1765 // lines/polygons to a point layer, extract all vertices
1766 auto mp = std::make_unique< QgsMultiPoint >();
1767 const QgsGeometry source = newGeom;
1768 QSet< QgsPoint > added;
1769 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1770 {
1771 if ( added.contains( *vertex ) )
1772 continue; // avoid duplicate points, e.g. start/end of rings
1773 mp->addGeometry( ( *vertex ).clone() );
1774 added.insert( *vertex );
1775 }
1776 newGeom = QgsGeometry( std::move( mp ) );
1777 }
1778
1779 //(Multi)Polygon to PolyhedralSurface
1782 {
1783 auto polySurface = std::make_unique< QgsPolyhedralSurface >();
1784 const QgsGeometry source = newGeom;
1785 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1786 {
1787 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1788 {
1789 polySurface->addPatch( polygon->clone() );
1790 }
1791 }
1792 newGeom = QgsGeometry( std::move( polySurface ) );
1793 }
1794
1795 // Polygon -> Triangle
1798 {
1799 auto triangle = std::make_unique< QgsTriangle >();
1800 const QgsGeometry source = newGeom;
1801 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( newGeom.constGet() ) )
1802 {
1803 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
1804 }
1805 newGeom = QgsGeometry( std::move( triangle ) );
1806 }
1807
1808
1809 // Single -> multi
1810 if ( QgsWkbTypes::isMultiType( type ) && ! newGeom.isMultipart( ) )
1811 {
1812 newGeom.convertToMultiType();
1813 }
1814 // Drop Z/M
1815 if ( newGeom.constGet()->is3D() && ! QgsWkbTypes::hasZ( type ) )
1816 {
1817 newGeom.get()->dropZValue();
1818 }
1819 if ( newGeom.constGet()->isMeasure() && ! QgsWkbTypes::hasM( type ) )
1820 {
1821 newGeom.get()->dropMValue();
1822 }
1823 // Add Z/M back, set to 0
1824 if ( ! newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
1825 {
1826 newGeom.get()->addZValue( defaultZ );
1827 }
1828 if ( ! newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
1829 {
1830 newGeom.get()->addMValue( defaultM );
1831 }
1832
1833 // Straight -> curve
1835 {
1836 newGeom.convertToCurvedMultiType();
1837 }
1838
1839 // Multi -> single
1840 if ( ! QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart( ) )
1841 {
1842 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1843 res.reserve( parts->partCount() );
1844 for ( int i = 0; i < parts->partCount( ); i++ )
1845 {
1846 res << QgsGeometry( parts->geometryN( i )->clone() );
1847 }
1848 }
1849 else
1850 {
1851 res << newGeom;
1852 }
1853 return res;
1854}
1855
1856QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
1857{
1858 switch ( destType )
1859 {
1861 return convertToPoint( destMultipart );
1862
1864 return convertToLine( destMultipart );
1865
1867 return convertToPolygon( destMultipart );
1868
1869 default:
1870 return QgsGeometry();
1871 }
1872}
1873
1875{
1876 if ( !d->geometry )
1877 {
1878 return false;
1879 }
1880
1881 if ( isMultipart() ) //already multitype, no need to convert
1882 {
1883 return true;
1884 }
1885
1886 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
1887 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1888 if ( !multiGeom )
1889 {
1890 return false;
1891 }
1892
1893 //try to avoid cloning existing geometry whenever we can
1894
1895 //want to see a magic trick?... gather round kiddies...
1896 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
1897 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
1898 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
1899 multiGeom->addGeometry( d->geometry.release() );
1900 // and replace it with the multi geometry.
1901 // TADA! a clone free conversion in some cases
1902 d->geometry = std::move( geom );
1903 return true;
1904}
1905
1907{
1908 if ( !d->geometry )
1909 {
1910 return false;
1911 }
1912
1913 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
1914 {
1919 {
1920 return true;
1921 }
1922 default:
1923 break;
1924 }
1925
1926 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
1927 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1928 if ( !multiGeom )
1929 {
1930 return false;
1931 }
1932
1933 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1934 if ( sourceMultiGeom )
1935 {
1936 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
1937 {
1938 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
1939 return false;
1940 }
1941 }
1942 else
1943 {
1944 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
1945 return false;
1946 }
1947
1948 reset( std::move( geom ) );
1949 return true;
1950}
1951
1953{
1954 if ( !d->geometry )
1955 {
1956 return false;
1957 }
1958
1959 if ( !isMultipart() ) //already single part, no need to convert
1960 {
1961 return true;
1962 }
1963
1964 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1965 if ( !multiGeom || multiGeom->partCount() < 1 )
1966 return false;
1967
1968 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
1969 reset( std::move( firstPart ) );
1970 return true;
1971}
1972
1973
1975{
1976 const QgsGeometryCollection *origGeom = qgsgeometry_cast<const QgsGeometryCollection *>( constGet() );
1977 if ( !origGeom )
1978 return false;
1979
1980 std::unique_ptr<QgsGeometryCollection> resGeom;
1981 switch ( geomType )
1982 {
1984 resGeom = std::make_unique<QgsMultiPoint>();
1985 break;
1987 resGeom = std::make_unique<QgsMultiLineString>();
1988 break;
1990 resGeom = std::make_unique<QgsMultiPolygon>();
1991 break;
1992 default:
1993 break;
1994 }
1995 if ( !resGeom )
1996 return false;
1997
1998 resGeom->reserve( origGeom->numGeometries() );
1999 for ( int i = 0; i < origGeom->numGeometries(); ++i )
2000 {
2001 const QgsAbstractGeometry *g = origGeom->geometryN( i );
2002 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
2003 resGeom->addGeometry( g->clone() );
2004 }
2005
2006 set( resGeom.release() );
2007 return true;
2008}
2009
2010
2012{
2013 if ( !d->geometry )
2014 {
2015 return QgsPointXY();
2016 }
2017 if ( const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
2018 {
2019 return QgsPointXY( pt->x(), pt->y() );
2020 }
2021 else
2022 {
2023 return QgsPointXY();
2024 }
2025}
2026
2028{
2029 QgsPolylineXY polyLine;
2030 if ( !d->geometry )
2031 {
2032 return polyLine;
2033 }
2034
2035 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve
2037 std::unique_ptr< QgsLineString > segmentizedLine;
2038 QgsLineString *line = nullptr;
2039 if ( doSegmentation )
2040 {
2041 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
2042 if ( !curve )
2043 {
2044 return polyLine;
2045 }
2046 segmentizedLine.reset( curve->curveToLine() );
2047 line = segmentizedLine.get();
2048 }
2049 else
2050 {
2051 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
2052 if ( !line )
2053 {
2054 return polyLine;
2055 }
2056 }
2057
2058 int nVertices = line->numPoints();
2059 polyLine.resize( nVertices );
2060 QgsPointXY *data = polyLine.data();
2061 const double *xData = line->xData();
2062 const double *yData = line->yData();
2063 for ( int i = 0; i < nVertices; ++i )
2064 {
2065 data->setX( *xData++ );
2066 data->setY( *yData++ );
2067 data++;
2068 }
2069
2070 return polyLine;
2071}
2072
2074{
2075 if ( !d->geometry )
2076 return QgsPolygonXY();
2077
2078 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
2079
2080 QgsPolygon *p = nullptr;
2081 std::unique_ptr< QgsPolygon > segmentized;
2082 if ( doSegmentation )
2083 {
2084 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
2085 if ( !curvePoly )
2086 {
2087 return QgsPolygonXY();
2088 }
2089 segmentized.reset( curvePoly->toPolygon() );
2090 p = segmentized.get();
2091 }
2092 else
2093 {
2094 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
2095 }
2096
2097 if ( !p )
2098 {
2099 return QgsPolygonXY();
2100 }
2101
2102 QgsPolygonXY polygon;
2103 convertPolygon( *p, polygon );
2104
2105 return polygon;
2106}
2107
2109{
2110 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
2111 {
2112 return QgsMultiPointXY();
2113 }
2114
2115 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
2116 if ( !mp )
2117 {
2118 return QgsMultiPointXY();
2119 }
2120
2121 int nPoints = mp->numGeometries();
2122 QgsMultiPointXY multiPoint( nPoints );
2123 for ( int i = 0; i < nPoints; ++i )
2124 {
2125 const QgsPoint *pt = mp->pointN( i );
2126 multiPoint[i].setX( pt->x() );
2127 multiPoint[i].setY( pt->y() );
2128 }
2129 return multiPoint;
2130}
2131
2133{
2134 if ( !d->geometry )
2135 {
2136 return QgsMultiPolylineXY();
2137 }
2138
2139 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2140 if ( !geomCollection )
2141 {
2142 return QgsMultiPolylineXY();
2143 }
2144
2145 int nLines = geomCollection->numGeometries();
2146 if ( nLines < 1 )
2147 {
2148 return QgsMultiPolylineXY();
2149 }
2150
2152 mpl.reserve( nLines );
2153 for ( int i = 0; i < nLines; ++i )
2154 {
2155 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2156 std::unique_ptr< QgsLineString > segmentized;
2157 if ( !line )
2158 {
2159 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2160 if ( !curve )
2161 {
2162 continue;
2163 }
2164 segmentized.reset( curve->curveToLine() );
2165 line = segmentized.get();
2166 }
2167
2168 QgsPolylineXY polyLine;
2169 int nVertices = line->numPoints();
2170 polyLine.resize( nVertices );
2171 QgsPointXY *data = polyLine.data();
2172 const double *xData = line->xData();
2173 const double *yData = line->yData();
2174 for ( int i = 0; i < nVertices; ++i )
2175 {
2176 data->setX( *xData++ );
2177 data->setY( *yData++ );
2178 data++;
2179 }
2180 mpl.append( polyLine );
2181 }
2182 return mpl;
2183}
2184
2186{
2187 if ( !d->geometry )
2188 {
2189 return QgsMultiPolygonXY();
2190 }
2191
2192 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2193 if ( !geomCollection )
2194 {
2195 return QgsMultiPolygonXY();
2196 }
2197
2198 const int nPolygons = geomCollection->numGeometries();
2199 if ( nPolygons < 1 )
2200 {
2201 return QgsMultiPolygonXY();
2202 }
2203
2205 mp.reserve( nPolygons );
2206 for ( int i = 0; i < nPolygons; ++i )
2207 {
2208 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2209 if ( !polygon )
2210 {
2211 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2212 if ( cPolygon )
2213 {
2214 polygon = cPolygon->toPolygon();
2215 }
2216 else
2217 {
2218 continue;
2219 }
2220 }
2221
2222 QgsPolygonXY poly;
2223 convertPolygon( *polygon, poly );
2224 mp.push_back( poly );
2225 }
2226 return mp;
2227}
2228
2229double QgsGeometry::area() const
2230{
2231 if ( !d->geometry )
2232 {
2233 return -1.0;
2234 }
2235
2236 return d->geometry->area();
2237}
2238
2240{
2241 if ( !d->geometry )
2242 {
2243 return -1.0;
2244 }
2245
2246 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2247 {
2249 return 0.0;
2250
2252 return d->geometry->length();
2253
2255 return d->geometry->perimeter();
2256
2259 return d->geometry->length();
2260 }
2261 return -1;
2262}
2263
2264double QgsGeometry::distance( const QgsGeometry &geom ) const
2265{
2266 if ( !d->geometry || !geom.d->geometry )
2267 {
2268 return -1.0;
2269 }
2270
2271 // avoid calling geos for trivial point-to-point distance calculations
2273 {
2274 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2275 }
2276
2277 QgsGeos g( d->geometry.get() );
2278 mLastError.clear();
2279 return g.distance( geom.d->geometry.get(), &mLastError );
2280}
2281
2283{
2284 if ( !d->geometry || !geom.d->geometry )
2285 {
2286 return -1.0;
2287 }
2288
2289 QgsGeos g( d->geometry.get() );
2290 mLastError.clear();
2291 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2292}
2293
2294double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2295{
2296 if ( !d->geometry || !geom.d->geometry )
2297 {
2298 return -1.0;
2299 }
2300
2301 QgsGeos g( d->geometry.get() );
2302 mLastError.clear();
2303 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2304}
2305
2306
2308{
2309 if ( !d->geometry || !geom.d->geometry )
2310 {
2311 return -1.0;
2312 }
2313
2314 QgsGeos g( d->geometry.get() );
2315 mLastError.clear();
2316 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
2317}
2318
2319double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2320{
2321 if ( !d->geometry || !geom.d->geometry )
2322 {
2323 return -1.0;
2324 }
2325
2326 QgsGeos g( d->geometry.get() );
2327 mLastError.clear();
2328 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2329}
2330
2332{
2333 if ( !d->geometry || d->geometry.get()->isEmpty() )
2335 return d->geometry->vertices_begin();
2336}
2337
2339{
2340 if ( !d->geometry || d->geometry.get()->isEmpty() )
2342 return d->geometry->vertices_end();
2343}
2344
2346{
2347 if ( !d->geometry || d->geometry.get()->isEmpty() )
2348 return QgsVertexIterator();
2349 return QgsVertexIterator( d->geometry.get() );
2350}
2351
2353{
2354 if ( !d->geometry )
2356
2357 detach();
2358 return d->geometry->parts_begin();
2359}
2360
2362{
2363 if ( !d->geometry )
2365 return d->geometry->parts_end();
2366}
2367
2369{
2370 if ( !d->geometry )
2372 return d->geometry->const_parts_begin();
2373}
2374
2376{
2377 if ( !d->geometry )
2379 return d->geometry->const_parts_end();
2380}
2381
2383{
2384 if ( !d->geometry )
2385 return QgsGeometryPartIterator();
2386
2387 detach();
2388 return QgsGeometryPartIterator( d->geometry.get() );
2389}
2390
2392{
2393 if ( !d->geometry )
2395
2396 return QgsGeometryConstPartIterator( d->geometry.get() );
2397}
2398
2399QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
2400{
2401 if ( !d->geometry )
2402 {
2403 return QgsGeometry();
2404 }
2405
2406 QgsGeos g( d->geometry.get() );
2407 mLastError.clear();
2408 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError ) );
2409 if ( !geom )
2410 {
2411 QgsGeometry result;
2412 result.mLastError = mLastError;
2413 return result;
2414 }
2415 return QgsGeometry( std::move( geom ) );
2416}
2417
2418QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit ) const
2419{
2420 if ( !d->geometry )
2421 {
2422 return QgsGeometry();
2423 }
2424
2425 QgsGeos g( d->geometry.get() );
2426 mLastError.clear();
2427 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError );
2428 if ( !geom )
2429 {
2430 QgsGeometry result;
2431 result.mLastError = mLastError;
2432 return result;
2433 }
2434 return QgsGeometry( geom );
2435}
2436
2437QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2438{
2439 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2440 {
2441 return QgsGeometry();
2442 }
2443
2444 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2445 {
2446 const QVector<QgsGeometry> parts = asGeometryCollection();
2447 QVector<QgsGeometry> results;
2448 results.reserve( parts.count() );
2449 for ( const QgsGeometry &part : parts )
2450 {
2451 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2452 if ( !result.isNull() )
2453 results << result;
2454 }
2455 if ( results.isEmpty() )
2456 return QgsGeometry();
2457
2458 QgsGeometry first = results.takeAt( 0 );
2459 for ( const QgsGeometry &result : std::as_const( results ) )
2460 {
2461 first.addPart( result );
2462 }
2463 return first;
2464 }
2465 else
2466 {
2467 QgsGeos geos( d->geometry.get() );
2468 mLastError.clear();
2469
2470 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2471 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2472
2473 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2474 if ( !offsetGeom )
2475 {
2476 QgsGeometry result;
2477 result.mLastError = mLastError;
2478 return result;
2479 }
2480
2481 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2482 {
2483 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2484 if ( newOrientation != prevOrientation )
2485 {
2486 // GEOS has flipped line orientation, flip it back
2487 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2488 offsetGeom = std::move( flipped );
2489 }
2490 }
2491 return QgsGeometry( std::move( offsetGeom ) );
2492 }
2493}
2494
2495QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2496{
2497 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2498 {
2499 return QgsGeometry();
2500 }
2501
2502 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2503 {
2504 const QVector<QgsGeometry> parts = asGeometryCollection();
2505 QVector<QgsGeometry> results;
2506 results.reserve( parts.count() );
2507 for ( const QgsGeometry &part : parts )
2508 {
2509 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2510 if ( !result.isNull() )
2511 results << result;
2512 }
2513 if ( results.isEmpty() )
2514 return QgsGeometry();
2515
2516 QgsGeometry first = results.takeAt( 0 );
2517 for ( const QgsGeometry &result : std::as_const( results ) )
2518 {
2519 first.addPart( result );
2520 }
2521 return first;
2522 }
2523 else
2524 {
2525 QgsGeos geos( d->geometry.get() );
2526 mLastError.clear();
2527 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side,
2528 joinStyle, miterLimit, &mLastError );
2529 if ( !bufferGeom )
2530 {
2531 QgsGeometry result;
2532 result.mLastError = mLastError;
2533 return result;
2534 }
2535 return QgsGeometry( std::move( bufferGeom ) );
2536 }
2537}
2538
2539QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2540{
2541 QgsInternalGeometryEngine engine( *this );
2542
2543 return engine.taperedBuffer( startWidth, endWidth, segments );
2544}
2545
2547{
2548 QgsInternalGeometryEngine engine( *this );
2549
2550 return engine.variableWidthBufferByM( segments );
2551}
2552
2553QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2554{
2555 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2556 {
2557 return QgsGeometry();
2558 }
2559
2560 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2561 {
2562 const QVector<QgsGeometry> parts = asGeometryCollection();
2563 QVector<QgsGeometry> results;
2564 results.reserve( parts.count() );
2565 for ( const QgsGeometry &part : parts )
2566 {
2567 QgsGeometry result = part.extendLine( startDistance, endDistance );
2568 if ( !result.isNull() )
2569 results << result;
2570 }
2571 if ( results.isEmpty() )
2572 return QgsGeometry();
2573
2574 QgsGeometry first = results.takeAt( 0 );
2575 for ( const QgsGeometry &result : std::as_const( results ) )
2576 {
2577 first.addPart( result );
2578 }
2579 return first;
2580 }
2581 else
2582 {
2583 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2584 if ( !line )
2585 return QgsGeometry();
2586
2587 std::unique_ptr< QgsLineString > newLine( line->clone() );
2588 newLine->extend( startDistance, endDistance );
2589 return QgsGeometry( std::move( newLine ) );
2590 }
2591}
2592
2593QgsGeometry QgsGeometry::simplify( double tolerance ) const
2594{
2595 if ( !d->geometry )
2596 {
2597 return QgsGeometry();
2598 }
2599
2600 QgsGeos geos( d->geometry.get() );
2601 mLastError.clear();
2602 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError ) );
2603 if ( !simplifiedGeom )
2604 {
2605 QgsGeometry result;
2606 result.mLastError = mLastError;
2607 return result;
2608 }
2609 return QgsGeometry( std::move( simplifiedGeom ) );
2610}
2611
2612QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2613{
2614 QgsInternalGeometryEngine engine( *this );
2615
2616 return engine.densifyByCount( extraNodesPerSegment );
2617}
2618
2620{
2621 QgsInternalGeometryEngine engine( *this );
2622
2623 return engine.densifyByDistance( distance );
2624}
2625
2626QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2627{
2628 QgsInternalGeometryEngine engine( *this );
2629
2630 return engine.convertToCurves( distanceTolerance, angleTolerance );
2631}
2632
2634{
2635 if ( !d->geometry )
2636 {
2637 return QgsGeometry();
2638 }
2639
2640 // avoid calling geos for trivial point centroids
2641 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
2642 {
2643 QgsGeometry c = *this;
2644 c.get()->dropZValue();
2645 c.get()->dropMValue();
2646 return c;
2647 }
2648
2649 QgsGeos geos( d->geometry.get() );
2650
2651 mLastError.clear();
2652 QgsGeometry result( geos.centroid( &mLastError ) );
2653 result.mLastError = mLastError;
2654 return result;
2655}
2656
2658{
2659 if ( !d->geometry )
2660 {
2661 return QgsGeometry();
2662 }
2663
2664 QgsGeos geos( d->geometry.get() );
2665
2666 mLastError.clear();
2667 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2668 result.mLastError = mLastError;
2669 return result;
2670}
2671
2672QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2673{
2674 QgsInternalGeometryEngine engine( *this );
2675
2676 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2677}
2678
2679QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2680{
2681 if ( !d->geometry )
2682 {
2683 return QgsGeometry();
2684 }
2685
2686 QgsGeos geos( d->geometry.get() );
2687
2688 mLastError.clear();
2689 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2690 result.mLastError = mLastError;
2691 return result;
2692}
2693
2695{
2696 if ( !d->geometry )
2697 {
2698 return QgsGeometry();
2699 }
2700
2701 QgsGeos geos( d->geometry.get() );
2702
2703 mLastError.clear();
2704 QgsGeometry result( geos.minimumWidth( &mLastError ) );
2705 result.mLastError = mLastError;
2706 return result;
2707}
2708
2710{
2711 if ( !d->geometry )
2712 {
2713 return std::numeric_limits< double >::quiet_NaN();
2714 }
2715
2716 QgsGeos geos( d->geometry.get() );
2717
2718 mLastError.clear();
2719 return geos.minimumClearance( &mLastError );
2720}
2721
2723{
2724 if ( !d->geometry )
2725 {
2726 return QgsGeometry();
2727 }
2728
2729 QgsGeos geos( d->geometry.get() );
2730
2731 mLastError.clear();
2732 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
2733 result.mLastError = mLastError;
2734 return result;
2735}
2736
2738{
2739 if ( !d->geometry )
2740 {
2741 return QgsGeometry();
2742 }
2743 QgsGeos geos( d->geometry.get() );
2744 mLastError.clear();
2745 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
2746 if ( !cHull )
2747 {
2748 QgsGeometry geom;
2749 geom.mLastError = mLastError;
2750 return geom;
2751 }
2752 return QgsGeometry( std::move( cHull ) );
2753}
2754
2755QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles ) const
2756{
2757 if ( !d->geometry )
2758 {
2759 return QgsGeometry();
2760 }
2761 QgsGeos geos( d->geometry.get() );
2762 mLastError.clear();
2763 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError ) );
2764 if ( !concaveHull )
2765 {
2766 QgsGeometry geom;
2767 geom.mLastError = mLastError;
2768 return geom;
2769 }
2770 return QgsGeometry( std::move( concaveHull ) );
2771}
2772
2773QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
2774{
2775 if ( !d->geometry )
2776 {
2777 return QgsGeometry();
2778 }
2779
2780 QgsGeos geos( d->geometry.get() );
2781 mLastError.clear();
2782 QgsGeometry result = QgsGeometry( geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError ) );
2783 result.mLastError = mLastError;
2784 return result;
2785}
2786
2787QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
2788{
2789 if ( !d->geometry )
2790 {
2791 return QgsGeometry();
2792 }
2793
2794 QgsGeos geos( d->geometry.get() );
2795 mLastError.clear();
2796 QgsGeometry result = QgsGeometry( geos.delaunayTriangulation( tolerance, edgesOnly ) );
2797 result.mLastError = mLastError;
2798 return result;
2799}
2800
2802{
2803 if ( !d->geometry )
2804 {
2805 return QgsGeometry();
2806 }
2807
2808 QgsGeos geos( d->geometry.get() );
2809 mLastError.clear();
2810 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
2811 result.mLastError = mLastError;
2812 return result;
2813}
2814
2816{
2817 if ( !d->geometry )
2818 {
2819 return QgsGeometry();
2820 }
2821
2825 return QgsGeometry();
2826
2827 QgsGeos geos( d->geometry.get() );
2828 mLastError.clear();
2829 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
2830 result.mLastError = mLastError;
2831 return result;
2832}
2833
2835{
2836 if ( !d->geometry )
2837 {
2839 }
2840
2841 QgsGeos geos( d->geometry.get() );
2842 mLastError.clear();
2843 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
2844
2845 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
2846
2847 if ( invalidEdges && invalidEdgesGeom )
2848 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
2849
2850 return result;
2851}
2852
2853QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
2854{
2855 if ( !d->geometry )
2856 {
2857 return QgsGeometry();
2858 }
2859
2860 QgsGeos geos( d->geometry.get() );
2861 mLastError.clear();
2862 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
2863 result.mLastError = mLastError;
2864 return result;
2865}
2866
2868{
2869 if ( !d->geometry )
2870 {
2871 return QgsGeometry();
2872 }
2873
2874 QgsGeos geos( d->geometry.get() );
2875 mLastError.clear();
2876 QgsGeometry result( geos.node( &mLastError ) );
2877 result.mLastError = mLastError;
2878 return result;
2879}
2880
2882{
2883 if ( !d->geometry )
2884 {
2885 return QgsGeometry();
2886 }
2887
2888 QgsGeos geos( d->geometry.get() );
2889 mLastError.clear();
2890 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
2891 result.mLastError = mLastError;
2892 return result;
2893}
2894
2895QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters ) const
2896{
2897 if ( !d->geometry )
2898 {
2899 return QgsGeometry();
2900 }
2901
2902 const QgsAbstractGeometry *geom = d->geometry.get();
2903 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
2904 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
2905 {
2906 segmentizedCopy.reset( d->geometry->segmentize() );
2907 geom = segmentizedCopy.get();
2908 }
2909
2910 QgsGeos geos( geom );
2911 mLastError.clear();
2912 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters ) );
2913 if ( !result )
2914 {
2915 QgsGeometry geom;
2916 geom.mLastError = mLastError;
2917 return geom;
2918 }
2919 return QgsGeometry( std::move( result ) );
2920}
2921
2923{
2924 if ( !d->geometry )
2925 {
2926 return QgsGeometry();
2927 }
2928
2929 QgsGeometry line = *this;
2931 return QgsGeometry();
2932 else if ( type() == Qgis::GeometryType::Polygon )
2933 {
2934 line = QgsGeometry( d->geometry->boundary() );
2935 }
2936
2937 const QgsCurve *curve = nullptr;
2938 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( line.constGet() ) )
2939 {
2940 // if multi part, iterate through parts to find target part
2941 for ( int part = 0; part < collection->numGeometries(); ++part )
2942 {
2943 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
2944 if ( !candidate )
2945 continue;
2946 const double candidateLength = candidate->length();
2947 if ( candidateLength >= distance )
2948 {
2949 curve = candidate;
2950 break;
2951 }
2952
2953 distance -= candidateLength;
2954 }
2955 }
2956 else
2957 {
2958 curve = qgsgeometry_cast< const QgsCurve * >( line.constGet() );
2959 }
2960 if ( !curve )
2961 return QgsGeometry();
2962
2963 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
2964 if ( !result )
2965 {
2966 return QgsGeometry();
2967 }
2968 return QgsGeometry( std::move( result ) );
2969}
2970
2971double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
2972{
2973 if ( type() != Qgis::GeometryType::Line )
2974 return -1;
2975
2977 return -1;
2978
2979 QgsGeometry segmentized = *this;
2981 {
2982 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
2983 }
2984
2985 QgsGeos geos( d->geometry.get() );
2986 mLastError.clear();
2987 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
2988}
2989
2990double QgsGeometry::interpolateAngle( double distance ) const
2991{
2992 if ( !d->geometry || d->geometry->isEmpty() )
2993 return 0.0;
2994
2995 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
2997 return 0.0;
2998
2999 // always operate on segmentized geometries
3000 QgsGeometry segmentized = *this;
3001 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
3002 {
3003 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
3004 }
3005
3006 QgsVertexId previous;
3007 QgsVertexId next;
3008 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
3009 return 0.0;
3010
3011 if ( previous == next )
3012 {
3013 // distance coincided exactly with a vertex
3014 QgsVertexId v2 = previous;
3015 QgsVertexId v1;
3016 QgsVertexId v3;
3017 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
3018 if ( v1.isValid() && v3.isValid() )
3019 {
3020 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3021 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3022 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
3023 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3024 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
3025 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
3026 }
3027 else if ( v3.isValid() )
3028 {
3029 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
3030 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
3031 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3032 }
3033 else
3034 {
3035 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3036 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3037 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3038 }
3039 }
3040 else
3041 {
3042 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
3043 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
3044 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3045 }
3046}
3047
3049{
3050 if ( !d->geometry || geometry.isNull() )
3051 {
3052 return QgsGeometry();
3053 }
3054
3055 QgsGeos geos( d->geometry.get() );
3056
3057 mLastError.clear();
3058 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters ) );
3059
3060 if ( !resultGeom )
3061 {
3062 QgsGeometry geom;
3063 geom.mLastError = mLastError;
3064 return geom;
3065 }
3066
3067 return QgsGeometry( std::move( resultGeom ) );
3068}
3069
3070QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters ) const
3071{
3072 if ( !d->geometry || geometry.isNull() )
3073 {
3074 return QgsGeometry();
3075 }
3076
3077 QgsGeos geos( d->geometry.get() );
3078 mLastError.clear();
3079 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters ) );
3080 if ( !resultGeom )
3081 {
3082 QgsGeometry geom;
3083 geom.mLastError = mLastError;
3084 return geom;
3085 }
3086 return QgsGeometry( std::move( resultGeom ) );
3087}
3088
3090{
3091 if ( !d->geometry )
3092 {
3093 return QgsGeometry();
3094 }
3095
3097 {
3098 // special case - a single linestring was passed
3099 return QgsGeometry( *this );
3100 }
3101
3102 QgsGeos geos( d->geometry.get() );
3103 mLastError.clear();
3104 QgsGeometry result( geos.mergeLines( &mLastError ) );
3105 result.mLastError = mLastError;
3106 return result;
3107}
3108
3110{
3111 if ( !d->geometry || geometry.isNull() )
3112 {
3113 return QgsGeometry();
3114 }
3115
3116 QgsGeos geos( d->geometry.get() );
3117
3118 mLastError.clear();
3119 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters ) );
3120 if ( !resultGeom )
3121 {
3122 QgsGeometry geom;
3123 geom.mLastError = mLastError;
3124 return geom;
3125 }
3126 return QgsGeometry( std::move( resultGeom ) );
3127}
3128
3130{
3131 if ( !d->geometry || geometry.isNull() )
3132 {
3133 return QgsGeometry();
3134 }
3135
3136 QgsGeos geos( d->geometry.get() );
3137
3138 mLastError.clear();
3139 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters ) );
3140 if ( !resultGeom )
3141 {
3142 QgsGeometry geom;
3143 geom.mLastError = mLastError;
3144 return geom;
3145 }
3146 return QgsGeometry( std::move( resultGeom ) );
3147}
3148
3150{
3151 QgsInternalGeometryEngine engine( *this );
3152
3153 return engine.extrude( x, y );
3154}
3155
3157
3158QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3159{
3161 return QVector< QgsPointXY >();
3162
3163 QgsInternalGeometryEngine engine( *this );
3164 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3165 mLastError = engine.lastError();
3166 return res;
3167}
3168
3169QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3170{
3172 return QVector< QgsPointXY >();
3173
3174 QgsInternalGeometryEngine engine( *this );
3175 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3176 mLastError = engine.lastError();
3177 return res;
3178}
3180
3182{
3183 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3184}
3185
3187{
3188 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3189}
3190
3191QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3192{
3193 QVector<QgsGeometry> geometryList;
3194 if ( !d->geometry )
3195 {
3196 return geometryList;
3197 }
3198
3199 QgsGeometryCollection *gc = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
3200 if ( gc )
3201 {
3202 int numGeom = gc->numGeometries();
3203 geometryList.reserve( numGeom );
3204 for ( int i = 0; i < numGeom; ++i )
3205 {
3206 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3207 }
3208 }
3209 else //a singlepart geometry
3210 {
3211 geometryList.append( *this );
3212 }
3213
3214 return geometryList;
3215}
3216
3218{
3219 QgsPointXY point = asPoint();
3220 return point.toQPointF();
3221}
3222
3224{
3225 const QgsAbstractGeometry *part = constGet();
3226
3227 // if a geometry collection, get first part only
3228 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( part ) )
3229 {
3230 if ( collection->numGeometries() > 0 )
3231 part = collection->geometryN( 0 );
3232 else
3233 return QPolygonF();
3234 }
3235
3236 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3237 return curve->asQPolygonF();
3238 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3239 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3240 return QPolygonF();
3241}
3242
3243bool QgsGeometry::deleteRing( int ringNum, int partNum )
3244{
3245 if ( !d->geometry )
3246 {
3247 return false;
3248 }
3249
3250 detach();
3251 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3252 return ok;
3253}
3254
3255bool QgsGeometry::deletePart( int partNum )
3256{
3257 if ( !d->geometry )
3258 {
3259 return false;
3260 }
3261
3262 if ( !isMultipart() && partNum < 1 )
3263 {
3264 set( nullptr );
3265 return true;
3266 }
3267
3268 detach();
3269 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3270 return ok;
3271}
3272
3273Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3274{
3275 if ( !d->geometry )
3276 {
3278 }
3279
3280 Qgis::WkbType geomTypeBeforeModification = wkbType();
3281
3282 bool haveInvalidGeometry = false;
3283 bool geomModified = false;
3284
3285 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3286 if ( diffGeom )
3287 {
3288 reset( std::move( diffGeom ) );
3289 geomModified = true;
3290 }
3291
3292 if ( geomTypeBeforeModification != wkbType() )
3294 if ( haveInvalidGeometry )
3296 if ( !geomModified )
3298
3300}
3301
3333
3335{
3336 if ( !d->geometry )
3337 return QgsGeometry();
3338
3339 mLastError.clear();
3340 QgsGeos geos( d->geometry.get() );
3341 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError ) );
3342
3343 QgsGeometry result = QgsGeometry( std::move( g ) );
3344 result.mLastError = mLastError;
3345 return result;
3346}
3347
3352
3354{
3355 if ( !d->geometry )
3356 {
3358 }
3359
3360 if ( isMultipart() )
3361 {
3362 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3363 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3364 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3365 {
3366 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3367 }
3368 }
3369 else
3370 {
3371 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3372 {
3373 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3374 }
3375 }
3376
3378
3379}
3380
3382{
3383 if ( !d->geometry )
3384 return QgsGeometry();
3385
3386 if ( isMultipart() )
3387 {
3388 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3389 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3390 newCollection->reserve( collection->numGeometries() );
3391 for ( int i = 0; i < collection->numGeometries(); ++i )
3392 {
3393 const QgsAbstractGeometry *g = collection->geometryN( i );
3394 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3395 {
3396 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3397 corrected->forceClockwise();
3398 newCollection->addGeometry( corrected.release() );
3399 }
3400 else
3401 {
3402 newCollection->addGeometry( g->clone() );
3403 }
3404 }
3405 return QgsGeometry( std::move( newCollection ) );
3406 }
3407 else
3408 {
3409 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3410 {
3411 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3412 corrected->forceClockwise();
3413 return QgsGeometry( std::move( corrected ) );
3414 }
3415 else
3416 {
3417 // not a curve polygon, so return unchanged
3418 return *this;
3419 }
3420 }
3421}
3422
3424{
3425 if ( !d->geometry )
3426 return QgsGeometry();
3427
3428 if ( isMultipart() )
3429 {
3430 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3431 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3432 newCollection->reserve( collection->numGeometries() );
3433 for ( int i = 0; i < collection->numGeometries(); ++i )
3434 {
3435 const QgsAbstractGeometry *g = collection->geometryN( i );
3436 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3437 {
3438 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3439 corrected->forceCounterClockwise();
3440 newCollection->addGeometry( corrected.release() );
3441 }
3442 else
3443 {
3444 newCollection->addGeometry( g->clone() );
3445 }
3446 }
3447 return QgsGeometry( std::move( newCollection ) );
3448 }
3449 else
3450 {
3451 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3452 {
3453 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3454 corrected->forceCounterClockwise();
3455 return QgsGeometry( std::move( corrected ) );
3456 }
3457 else
3458 {
3459 // not a curve polygon, so return unchanged
3460 return *this;
3461 }
3462 }
3463}
3464
3465
3466void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3467{
3468 errors.clear();
3469 if ( !d->geometry )
3470 return;
3471
3472 // avoid expensive calcs for trivial point geometries
3474 {
3475 return;
3476 }
3477
3478 switch ( method )
3479 {
3481 QgsGeometryValidator::validateGeometry( *this, errors, method );
3482 return;
3483
3485 {
3486 QgsGeos geos( d->geometry.get(), 0, Qgis::GeosCreationFlags() );
3487 QString error;
3488 QgsGeometry errorLoc;
3489 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3490 {
3491 if ( errorLoc.isNull() )
3492 {
3493 errors.append( QgsGeometry::Error( error ) );
3494 }
3495 else
3496 {
3497 const QgsPointXY point = errorLoc.asPoint();
3498 errors.append( QgsGeometry::Error( error, point ) );
3499 }
3500 return;
3501 }
3502 }
3503 }
3504}
3505
3507{
3508 if ( !d->geometry )
3509 {
3510 return;
3511 }
3512
3513 detach();
3514 d->geometry->normalize();
3515}
3516
3518{
3519 if ( !d->geometry )
3520 {
3521 return false;
3522 }
3523
3524 return d->geometry->isValid( mLastError, flags );
3525}
3526
3528{
3529 if ( !d->geometry )
3530 return false;
3531
3532 QgsGeos geos( d->geometry.get() );
3533 mLastError.clear();
3534 return geos.isSimple( &mLastError );
3535}
3536
3537bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3538{
3539 if ( !d->geometry )
3540 return false;
3541
3542 QgsInternalGeometryEngine engine( *this );
3543 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3544}
3545
3547{
3548 if ( !d->geometry || !g.d->geometry )
3549 {
3550 return false;
3551 }
3552
3553 // fast check - are they shared copies of the same underlying geometry?
3554 if ( d == g.d )
3555 return true;
3556
3557 // fast check - distinct geometry types?
3558 if ( type() != g.type() )
3559 return false;
3560
3561 // avoid calling geos for trivial point case
3562 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point
3563 && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3564 {
3565 return equals( g );
3566 }
3567
3568 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3569 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3570 return false;
3571
3572 QgsGeos geos( d->geometry.get() );
3573 mLastError.clear();
3574 return geos.isEqual( g.d->geometry.get(), &mLastError );
3575}
3576
3577QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters )
3578{
3579 QgsGeos geos( nullptr );
3580
3581 QString error;
3582 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters ) );
3583 QgsGeometry result( std::move( geom ) );
3584 result.mLastError = error;
3585 return result;
3586}
3587
3588QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
3589{
3590 QVector<const QgsAbstractGeometry *> geomV2List;
3591 for ( const QgsGeometry &g : geometryList )
3592 {
3593 if ( !( g.isNull() ) )
3594 {
3595 geomV2List.append( g.constGet() );
3596 }
3597 }
3598
3599 QString error;
3600 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
3601 result.mLastError = error;
3602 return result;
3603}
3604
3606{
3608 {
3609 return;
3610 }
3611
3612 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3613 reset( std::move( straightGeom ) );
3614}
3615
3617{
3618 if ( !d->geometry )
3619 {
3620 return false;
3621 }
3622
3623 return d->geometry->hasCurvedSegments();
3624}
3625
3627{
3628 if ( !d->geometry )
3629 {
3631 }
3632
3633 detach();
3634 d->geometry->transform( ct, direction, transformZ );
3636}
3637
3638Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3639{
3640 if ( !d->geometry )
3641 {
3643 }
3644
3645 detach();
3646 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3648}
3649
3651{
3652 if ( d->geometry )
3653 {
3654 detach();
3655 d->geometry->transform( mtp.transform() );
3656 }
3657}
3658
3660{
3661 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
3662 {
3663 return QgsGeometry();
3664 }
3665
3666 QgsGeos geos( d->geometry.get() );
3667 mLastError.clear();
3668 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError );
3669 if ( !resultGeom )
3670 {
3671 QgsGeometry result;
3672 result.mLastError = mLastError;
3673 return result;
3674 }
3675 return QgsGeometry( std::move( resultGeom ) );
3676}
3677
3678void QgsGeometry::draw( QPainter &p ) const
3679{
3680 if ( d->geometry )
3681 {
3682 d->geometry->draw( p );
3683 }
3684}
3685
3686static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
3687{
3688 if ( vertexIndex < 0 )
3689 return false; // clearly something wrong
3690
3691 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3692 {
3693 partIndex = 0;
3694 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3695 {
3696 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3697
3698 // count total number of vertices in the part
3699 int numPoints = 0;
3700 for ( int k = 0; k < part->ringCount(); ++k )
3701 numPoints += part->vertexCount( 0, k );
3702
3703 if ( vertexIndex < numPoints )
3704 {
3705 int nothing;
3706 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3707 }
3708 vertexIndex -= numPoints;
3709 partIndex++;
3710 }
3711 }
3712 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3713 {
3714 const QgsCurve *ring = curvePolygon->exteriorRing();
3715 if ( vertexIndex < ring->numPoints() )
3716 {
3717 partIndex = 0;
3718 ringIndex = 0;
3719 vertex = vertexIndex;
3720 return true;
3721 }
3722 vertexIndex -= ring->numPoints();
3723 ringIndex = 1;
3724 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
3725 {
3726 const QgsCurve *ring = curvePolygon->interiorRing( i );
3727 if ( vertexIndex < ring->numPoints() )
3728 {
3729 partIndex = 0;
3730 vertex = vertexIndex;
3731 return true;
3732 }
3733 vertexIndex -= ring->numPoints();
3734 ringIndex += 1;
3735 }
3736 }
3737 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3738 {
3739 if ( vertexIndex < curve->numPoints() )
3740 {
3741 partIndex = 0;
3742 ringIndex = 0;
3743 vertex = vertexIndex;
3744 return true;
3745 }
3746 }
3747 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
3748 {
3749 if ( vertexIndex == 0 )
3750 {
3751 partIndex = 0;
3752 ringIndex = 0;
3753 vertex = 0;
3754 return true;
3755 }
3756 }
3757
3758 return false;
3759}
3760
3762{
3763 if ( !d->geometry )
3764 {
3765 return false;
3766 }
3767
3768 id.type = Qgis::VertexType::Segment;
3769
3770 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
3771 if ( !res )
3772 return false;
3773
3774 // now let's find out if it is a straight or circular segment
3775 const QgsAbstractGeometry *g = d->geometry.get();
3776 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3777 {
3778 g = geomCollection->geometryN( id.part );
3779 }
3780
3781 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3782 {
3783 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
3784 }
3785
3786 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3787 {
3788 QgsPoint p;
3789 res = curve->pointAt( id.vertex, p, id.type );
3790 if ( !res )
3791 return false;
3792 }
3793
3794 return true;
3795}
3796
3798{
3799 if ( !d->geometry )
3800 {
3801 return -1;
3802 }
3803 return d->geometry->vertexNumberFromVertexId( id );
3804}
3805
3807{
3808 return mLastError;
3809}
3810
3811void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
3812{
3813 if ( !d->geometry )
3814 return;
3815
3816 detach();
3817
3818 d->geometry->filterVertices( filter );
3819}
3820
3821void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
3822{
3823 if ( !d->geometry )
3824 return;
3825
3826 detach();
3827
3828 d->geometry->transformVertices( transform );
3829}
3830
3831void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
3832{
3833 output.clear();
3834 for ( const QgsPointXY &p : input )
3835 {
3836 output.append( QgsPoint( p ) );
3837 }
3838}
3839
3840void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
3841{
3842 output.clear();
3843 for ( const QgsPoint &p : input )
3844 {
3845 output.append( QgsPointXY( p.x(), p.y() ) );
3846 }
3847}
3848
3849void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
3850{
3851 output.clear();
3852
3853 auto convertRing = []( const QgsCurve * ring ) -> QgsPolylineXY
3854 {
3855 QgsPolylineXY res;
3856 bool doSegmentation = ( QgsWkbTypes::flatType( ring->wkbType() ) == Qgis::WkbType::CompoundCurve
3858 std::unique_ptr< QgsLineString > segmentizedLine;
3859 const QgsLineString *line = nullptr;
3860 if ( doSegmentation )
3861 {
3862 segmentizedLine.reset( ring->curveToLine() );
3863 line = segmentizedLine.get();
3864 }
3865 else
3866 {
3867 line = qgsgeometry_cast<const QgsLineString *>( ring );
3868 if ( !line )
3869 {
3870 return res;
3871 }
3872 }
3873
3874 int nVertices = line->numPoints();
3875 res.resize( nVertices );
3876 QgsPointXY *data = res.data();
3877 const double *xData = line->xData();
3878 const double *yData = line->yData();
3879 for ( int i = 0; i < nVertices; ++i )
3880 {
3881 data->setX( *xData++ );
3882 data->setY( *yData++ );
3883 data++;
3884 }
3885 return res;
3886 };
3887
3888 if ( const QgsCurve *exterior = input.exteriorRing() )
3889 {
3890 output.push_back( convertRing( exterior ) );
3891 }
3892
3893 const int interiorRingCount = input.numInteriorRings();
3894 output.reserve( output.size() + interiorRingCount );
3895 for ( int n = 0; n < interiorRingCount; ++n )
3896 {
3897 output.push_back( convertRing( input.interiorRing( n ) ) );
3898 }
3899}
3900
3902{
3903 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
3904}
3905
3906QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
3907{
3908 std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
3909
3910 if ( polygon.isClosed() )
3911 {
3912 auto poly = std::make_unique< QgsPolygon >();
3913 poly->setExteriorRing( ring.release() );
3914 return QgsGeometry( std::move( poly ) );
3915 }
3916 else
3917 {
3918 return QgsGeometry( std::move( ring ) );
3919 }
3920}
3921
3923{
3925 QgsPolygonXY result;
3926 result << createPolylineFromQPolygonF( polygon );
3927 return result;
3929}
3930
3932{
3933 QgsPolylineXY result;
3934 result.reserve( polygon.count() );
3935 for ( const QPointF &p : polygon )
3936 {
3937 result.append( QgsPointXY( p ) );
3938 }
3939 return result;
3940}
3941
3942bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
3943{
3944 if ( p1.count() != p2.count() )
3945 return false;
3946
3947 for ( int i = 0; i < p1.count(); ++i )
3948 {
3949 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
3950 return false;
3951 }
3952 return true;
3953}
3954
3955bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
3956{
3957 if ( p1.count() != p2.count() )
3958 return false;
3959
3960 for ( int i = 0; i < p1.count(); ++i )
3961 {
3962 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3963 return false;
3964 }
3965 return true;
3966}
3967
3968
3969bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
3970{
3971 if ( p1.count() != p2.count() )
3972 return false;
3973
3974 for ( int i = 0; i < p1.count(); ++i )
3975 {
3976 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3977 return false;
3978 }
3979 return true;
3980}
3981
3982QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3983{
3984 if ( !d->geometry || d->geometry->isEmpty() )
3985 return QgsGeometry();
3986
3987 QgsGeometry geom = *this;
3989 geom = QgsGeometry( d->geometry->segmentize() );
3990
3991 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
3992 {
3995 //can't smooth a point based geometry
3996 return geom;
3997
3999 {
4000 const QgsLineString *lineString = qgsgeometry_cast< const QgsLineString * >( geom.constGet() );
4001 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
4002 }
4003
4005 {
4006 const QgsMultiLineString *multiLine = qgsgeometry_cast< const QgsMultiLineString * >( geom.constGet() );
4007
4008 auto resultMultiline = std::make_unique< QgsMultiLineString> ();
4009 resultMultiline->reserve( multiLine->numGeometries() );
4010 for ( int i = 0; i < multiLine->numGeometries(); ++i )
4011 {
4012 resultMultiline->addGeometry( smoothLine( *( multiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4013 }
4014 return QgsGeometry( std::move( resultMultiline ) );
4015 }
4016
4018 {
4019 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( geom.constGet() );
4020 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
4021 }
4022
4024 {
4025 const QgsMultiPolygon *multiPoly = qgsgeometry_cast< const QgsMultiPolygon * >( geom.constGet() );
4026
4027 auto resultMultiPoly = std::make_unique< QgsMultiPolygon >();
4028 resultMultiPoly->reserve( multiPoly->numGeometries() );
4029 for ( int i = 0; i < multiPoly->numGeometries(); ++i )
4030 {
4031 resultMultiPoly->addGeometry( smoothPolygon( *( multiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4032 }
4033 return QgsGeometry( std::move( resultMultiPoly ) );
4034 }
4035
4037 default:
4038 return QgsGeometry( *this );
4039 }
4040}
4041
4042std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations,
4043 const double offset, double squareDistThreshold, double maxAngleRads,
4044 bool isRing )
4045{
4046 auto result = std::make_unique< QgsLineString >( line );
4047 QgsPointSequence outputLine;
4048 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
4049 {
4050 outputLine.resize( 0 );
4051 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
4052 bool skipFirst = false;
4053 bool skipLast = false;
4054 if ( isRing )
4055 {
4056 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
4057 QgsPoint p2 = result->pointN( 0 );
4058 QgsPoint p3 = result->pointN( 1 );
4059 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4060 p3.x(), p3.y() );
4061 angle = std::fabs( M_PI - angle );
4062 skipFirst = angle > maxAngleRads;
4063 }
4064 for ( int i = 0; i < result->numPoints() - 1; i++ )
4065 {
4066 QgsPoint p1 = result->pointN( i );
4067 QgsPoint p2 = result->pointN( i + 1 );
4068
4069 double angle = M_PI;
4070 if ( i == 0 && isRing )
4071 {
4072 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
4073 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4074 p3.x(), p3.y() );
4075 }
4076 else if ( i < result->numPoints() - 2 )
4077 {
4078 QgsPoint p3 = result->pointN( i + 2 );
4079 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4080 p3.x(), p3.y() );
4081 }
4082 else if ( i == result->numPoints() - 2 && isRing )
4083 {
4084 QgsPoint p3 = result->pointN( 1 );
4085 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4086 p3.x(), p3.y() );
4087 }
4088
4089 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
4090
4091 // don't apply distance threshold to first or last segment
4092 if ( i == 0 || i >= result->numPoints() - 2
4093 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
4094 {
4095 if ( !isRing )
4096 {
4097 if ( !skipFirst )
4098 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
4099 if ( !skipLast )
4100 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
4101 else
4102 outputLine << p2;
4103 }
4104 else
4105 {
4106 // ring
4107 if ( !skipFirst )
4108 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
4109 else if ( i == 0 )
4110 outputLine << p1;
4111 if ( !skipLast )
4112 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
4113 else
4114 outputLine << p2;
4115 }
4116 }
4117 skipFirst = skipLast;
4118 }
4119
4120 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4121 outputLine << outputLine.at( 0 );
4122
4123 result->setPoints( outputLine );
4124 }
4125 return result;
4126}
4127
4128std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4129{
4130 double maxAngleRads = maxAngle * M_PI / 180.0;
4131 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4132 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4133}
4134
4135std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4136{
4137 double maxAngleRads = maxAngle * M_PI / 180.0;
4138 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4139 auto resultPoly = std::make_unique< QgsPolygon >();
4140
4141 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset,
4142 squareDistThreshold, maxAngleRads, true ).release() );
4143
4144 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4145 {
4146 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset,
4147 squareDistThreshold, maxAngleRads, true ).release() );
4148 }
4149 return resultPoly;
4150}
4151
4152QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4153{
4154 switch ( type() )
4155 {
4157 {
4158 bool srcIsMultipart = isMultipart();
4159
4160 if ( ( destMultipart && srcIsMultipart ) ||
4161 ( !destMultipart && !srcIsMultipart ) )
4162 {
4163 // return a copy of the same geom
4164 return QgsGeometry( *this );
4165 }
4166 if ( destMultipart )
4167 {
4168 // layer is multipart => make a multipoint with a single point
4169 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4170 }
4171 else
4172 {
4173 // destination is singlepart => make a single part if possible
4174 QgsMultiPointXY multiPoint = asMultiPoint();
4175 if ( multiPoint.count() == 1 )
4176 {
4177 return fromPointXY( multiPoint[0] );
4178 }
4179 }
4180 return QgsGeometry();
4181 }
4182
4184 {
4185 // only possible if destination is multipart
4186 if ( !destMultipart )
4187 return QgsGeometry();
4188
4189 // input geometry is multipart
4190 if ( isMultipart() )
4191 {
4192 const QgsMultiPolylineXY multiLine = asMultiPolyline();
4193 QgsMultiPointXY multiPoint;
4194 for ( const QgsPolylineXY &l : multiLine )
4195 for ( const QgsPointXY &p : l )
4196 multiPoint << p;
4197 return fromMultiPointXY( multiPoint );
4198 }
4199 // input geometry is not multipart: copy directly the line into a multipoint
4200 else
4201 {
4202 QgsPolylineXY line = asPolyline();
4203 if ( !line.isEmpty() )
4204 return fromMultiPointXY( line );
4205 }
4206 return QgsGeometry();
4207 }
4208
4210 {
4211 // can only transform if destination is multipoint
4212 if ( !destMultipart )
4213 return QgsGeometry();
4214
4215 // input geometry is multipart: make a multipoint from multipolygon
4216 if ( isMultipart() )
4217 {
4218 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4219 QgsMultiPointXY multiPoint;
4220 for ( const QgsPolygonXY &poly : multiPolygon )
4221 for ( const QgsPolylineXY &line : poly )
4222 for ( const QgsPointXY &pt : line )
4223 multiPoint << pt;
4224 return fromMultiPointXY( multiPoint );
4225 }
4226 // input geometry is not multipart: make a multipoint from polygon
4227 else
4228 {
4229 const QgsPolygonXY polygon = asPolygon();
4230 QgsMultiPointXY multiPoint;
4231 for ( const QgsPolylineXY &line : polygon )
4232 for ( const QgsPointXY &pt : line )
4233 multiPoint << pt;
4234 return fromMultiPointXY( multiPoint );
4235 }
4236 }
4237
4238 default:
4239 return QgsGeometry();
4240 }
4241}
4242
4243QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4244{
4245 switch ( type() )
4246 {
4248 {
4249 if ( !isMultipart() )
4250 return QgsGeometry();
4251
4252 QgsMultiPointXY multiPoint = asMultiPoint();
4253 if ( multiPoint.count() < 2 )
4254 return QgsGeometry();
4255
4256 if ( destMultipart )
4257 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4258 else
4259 return fromPolylineXY( multiPoint );
4260 }
4261
4263 {
4264 bool srcIsMultipart = isMultipart();
4265
4266 if ( ( destMultipart && srcIsMultipart ) ||
4267 ( !destMultipart && ! srcIsMultipart ) )
4268 {
4269 // return a copy of the same geom
4270 return QgsGeometry( *this );
4271 }
4272 if ( destMultipart )
4273 {
4274 // destination is multipart => makes a multipoint with a single line
4275 QgsPolylineXY line = asPolyline();
4276 if ( !line.isEmpty() )
4277 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4278 }
4279 else
4280 {
4281 // destination is singlepart => make a single part if possible
4282 QgsMultiPolylineXY multiLine = asMultiPolyline();
4283 if ( multiLine.count() == 1 )
4284 return fromPolylineXY( multiLine[0] );
4285 }
4286 return QgsGeometry();
4287 }
4288
4290 {
4291 // input geometry is multipolygon
4292 if ( isMultipart() )
4293 {
4294 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4295 QgsMultiPolylineXY multiLine;
4296 for ( const QgsPolygonXY &poly : multiPolygon )
4297 for ( const QgsPolylineXY &line : poly )
4298 multiLine << line;
4299
4300 if ( destMultipart )
4301 {
4302 // destination is multipart
4303 return fromMultiPolylineXY( multiLine );
4304 }
4305 else if ( multiLine.count() == 1 )
4306 {
4307 // destination is singlepart => make a single part if possible
4308 return fromPolylineXY( multiLine[0] );
4309 }
4310 }
4311 // input geometry is single polygon
4312 else
4313 {
4314 QgsPolygonXY polygon = asPolygon();
4315 // if polygon has rings
4316 if ( polygon.count() > 1 )
4317 {
4318 // cannot fit a polygon with rings in a single line layer
4319 // TODO: would it be better to remove rings?
4320 if ( destMultipart )
4321 {
4322 const QgsPolygonXY polygon = asPolygon();
4323 QgsMultiPolylineXY multiLine;
4324 multiLine.reserve( polygon.count() );
4325 for ( const QgsPolylineXY &line : polygon )
4326 multiLine << line;
4327 return fromMultiPolylineXY( multiLine );
4328 }
4329 }
4330 // no rings
4331 else if ( polygon.count() == 1 )
4332 {
4333 if ( destMultipart )
4334 {
4335 return fromMultiPolylineXY( polygon );
4336 }
4337 else
4338 {
4339 return fromPolylineXY( polygon[0] );
4340 }
4341 }
4342 }
4343 return QgsGeometry();
4344 }
4345
4346 default:
4347 return QgsGeometry();
4348 }
4349}
4350
4351QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4352{
4353 switch ( type() )
4354 {
4356 {
4357 if ( !isMultipart() )
4358 return QgsGeometry();
4359
4360 QgsMultiPointXY multiPoint = asMultiPoint();
4361 if ( multiPoint.count() < 3 )
4362 return QgsGeometry();
4363
4364 if ( multiPoint.last() != multiPoint.first() )
4365 multiPoint << multiPoint.first();
4366
4367 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4368 if ( destMultipart )
4369 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4370 else
4371 return fromPolygonXY( polygon );
4372 }
4373
4375 {
4376 // input geometry is multiline
4377 if ( isMultipart() )
4378 {
4379 QgsMultiPolylineXY multiLine = asMultiPolyline();
4380 QgsMultiPolygonXY multiPolygon;
4381 for ( QgsMultiPolylineXY::iterator multiLineIt = multiLine.begin(); multiLineIt != multiLine.end(); ++multiLineIt )
4382 {
4383 // do not create polygon for a 1 segment line
4384 if ( ( *multiLineIt ).count() < 3 )
4385 return QgsGeometry();
4386 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4387 return QgsGeometry();
4388
4389 // add closing node
4390 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4391 *multiLineIt << ( *multiLineIt ).first();
4392 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4393 }
4394 // check that polygons were inserted
4395 if ( !multiPolygon.isEmpty() )
4396 {
4397 if ( destMultipart )
4398 {
4399 return fromMultiPolygonXY( multiPolygon );
4400 }
4401 else if ( multiPolygon.count() == 1 )
4402 {
4403 // destination is singlepart => make a single part if possible
4404 return fromPolygonXY( multiPolygon[0] );
4405 }
4406 }
4407 }
4408 // input geometry is single line
4409 else
4410 {
4411 QgsPolylineXY line = asPolyline();
4412
4413 // do not create polygon for a 1 segment line
4414 if ( line.count() < 3 )
4415 return QgsGeometry();
4416 if ( line.count() == 3 && line.first() == line.last() )
4417 return QgsGeometry();
4418
4419 // add closing node
4420 if ( line.first() != line.last() )
4421 line << line.first();
4422
4423 // destination is multipart
4424 if ( destMultipart )
4425 {
4426 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4427 }
4428 else
4429 {
4430 return fromPolygonXY( QgsPolygonXY() << line );
4431 }
4432 }
4433 return QgsGeometry();
4434 }
4435
4437 {
4438 bool srcIsMultipart = isMultipart();
4439
4440 if ( ( destMultipart && srcIsMultipart ) ||
4441 ( !destMultipart && ! srcIsMultipart ) )
4442 {
4443 // return a copy of the same geom
4444 return QgsGeometry( *this );
4445 }
4446 if ( destMultipart )
4447 {
4448 // destination is multipart => makes a multipoint with a single polygon
4449 QgsPolygonXY polygon = asPolygon();
4450 if ( !polygon.isEmpty() )
4451 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4452 }
4453 else
4454 {
4455 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4456 if ( multiPolygon.count() == 1 )
4457 {
4458 // destination is singlepart => make a single part if possible
4459 return fromPolygonXY( multiPolygon[0] );
4460 }
4461 }
4462 return QgsGeometry();
4463 }
4464
4465 default:
4466 return QgsGeometry();
4467 }
4468}
4469
4471{
4472 return new QgsGeos( geometry, precision, flags );
4473}
4474
4475QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
4476{
4477 out << geometry.asWkb();
4478 return out;
4479}
4480
4481QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4482{
4483 QByteArray byteArray;
4484 in >> byteArray;
4485 if ( byteArray.isEmpty() )
4486 {
4487 geometry.set( nullptr );
4488 return in;
4489 }
4490
4491 geometry.fromWkb( byteArray );
4492 return in;
4493}
4494
4495
4497{
4498 return mMessage;
4499}
4500
4502{
4503 return mLocation;
4504}
4505
4507{
4508 return mHasLocation;
4509}
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
BufferSide
Side of line to buffer.
Definition qgis.h:2059
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3185
AngularDirection
Angular directions.
Definition qgis.h:3300
@ NoOrientation
Unknown orientation or sentinel value.
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2005
@ AddPartSelectedGeometryNotFound
The selected geometry cannot be found.
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
@ Success
Operation succeeded.
@ SelectionIsEmpty
No features were selected.
@ GeometryTypeHasChanged
Operation has changed geometry type.
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint)
@ AddPartNotMultiGeometry
The source geometry is not multi.
@ AddRingNotClosed
The input ring is not closed.
@ SelectionIsGreaterThanOne
More than one features were selected.
@ SplitCannotSplitPoint
Cannot split points.
@ GeometryEngineError
Geometry engine misses a method implemented or an error occurred in the geometry engine.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
@ LayerNotEditable
Cannot edit layer.
@ AddRingNotValid
The input ring is not valid.
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2038
@ Segment
The actual start or end point of a segment.
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:2047
@ QgisInternal
Use internal QgsGeometryValidator method.
@ Geos
Use GEOS validation methods.
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
Definition qgis.h:2108
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:337
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
JoinStyle
Join styles for buffers.
Definition qgis.h:2084
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2071
CoverageValidityResult
Coverage validity results.
Definition qgis.h:2117
@ Error
An exception occurred while determining validity.
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3170
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2130
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ MultiPoint
MultiPoint.
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
@ Triangle
Triangle.
@ NoGeometry
No geometry.
@ MultiLineString
MultiLineString.
@ Unknown
Unknown.
@ CircularString
CircularString.
@ GeometryCollection
GeometryCollection.
@ MultiCurve
MultiCurve.
@ CurvePolygon
CurvePolygon.
@ PolyhedralSurface
PolyhedralSurface.
@ MultiSurface
MultiSurface.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2619
The part_iterator class provides STL-style iterator for const references to geometry parts.
The part_iterator class provides STL-style iterator for geometry parts.
The vertex_iterator class provides 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 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 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.
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.
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:43
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:231
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:196
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:259
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:203
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:379
bool is2d() const
Returns true if the box can be considered a 2-dimensional box, i.e.
Definition qgsbox3d.cpp:134
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:252
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:224
Circle geometry type.
Definition qgscircle.h:45
static QgsCircle from2Points(const QgsPoint &pt1, const QgsPoint &pt2)
Constructs a circle by 2 points on the circle.
Definition qgscircle.cpp:36
double radius() const
Returns the radius of the circle.
Definition qgscircle.h:300
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:138
Class for doing transforms between two map 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.
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)
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
Abstract base class for curved geometry type.
Definition qgscurve.h:35
virtual int numPoints() const =0
Returns the number of points in the curve.
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsPoint * interpolatePoint(double distance) const =0
Returns an interpolated point on the curve at the specified distance.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
QgsPoint center() const
Returns the center point.
Definition qgsellipse.h:120
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 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 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 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.
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...
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...
QVector< QgsGeometry > coerceToType(Qgis::WkbType type, double defaultZ=0, double defaultM=0) const
Attempts to coerce this geometry into the specified destination type.
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.
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...
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 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.
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.
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.
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.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
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...
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...
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
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:139
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:826
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:780
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition qgsgeos.cpp:2079
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:757
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:581
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:3227
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:803
This class 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.
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.
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.
A class to represent a 2D point.
Definition qgspointxy.h:60
void setY(double y)
Sets the y value of the point.
Definition qgspointxy.h:129
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
void setX(double x)
Sets the x value of the point.
Definition qgspointxy.h:119
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:165
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
void setX(double x)
Sets the point's x-coordinate.
Definition qgspoint.h:326
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:105
double x
Definition qgspoint.h:52
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:704
double y
Definition qgspoint.h:53
Polygon geometry type.
Definition qgspolygon.h:33
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
Represents a vector layer which manages a vector based data sets.
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 bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition qgswkbtypes.h:53
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
static bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB 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:75
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:6796
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6795
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6219
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:74
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition qgsgeometry.h:84
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:80
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:62
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition qgsgeometry.h:91
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition qgsgeometry.h:70
#define QgsDebugError(str)
Definition qgslogger.h:40
int precision
std::unique_ptr< QgsAbstractGeometry > geometry
QgsGeometryPrivate(std::unique_ptr< QgsAbstractGeometry > geometry)
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30
int vertex
Vertex number.
Definition qgsvertexid.h:94
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:45