QGIS API Documentation 3.99.0-Master (b927df884fe)
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 // We're trying adding the reshape line's vertices to the geometry so that
1245 // snap to segment always produces a valid reshape
1246 QgsPointSequence reshapePoints;
1247 reshapeLineString.points( reshapePoints );
1248 QgsGeometry tmpGeom( *this );
1249 for ( const QgsPoint &v : std::as_const( reshapePoints ) )
1250 {
1251 tmpGeom.addTopologicalPoint( v );
1252 }
1253
1254 QgsGeos geos( tmpGeom.get() );
1256 mLastError.clear();
1257 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1258 if ( errorCode == QgsGeometryEngine::Success && geom )
1259 {
1260 reset( std::move( geom ) );
1262 }
1263
1264 switch ( errorCode )
1265 {
1276 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1280 }
1281
1282 // should not be reached
1284}
1285
1287{
1288 if ( !d->geometry || !other.d->geometry )
1289 {
1290 return 0;
1291 }
1292
1293 QgsGeos geos( d->geometry.get() );
1294
1295 mLastError.clear();
1296 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1297 if ( !diffGeom )
1298 {
1299 return 1;
1300 }
1301
1302 reset( std::move( diffGeom ) );
1303 return 0;
1304}
1305
1307{
1308 if ( !d->geometry || other.isNull() )
1309 {
1310 return QgsGeometry();
1311 }
1312
1313 QgsGeos geos( d->geometry.get() );
1314
1315 mLastError.clear();
1316 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1317 if ( !diffGeom )
1318 {
1319 QgsGeometry result;
1320 result.mLastError = mLastError;
1321 return result;
1322 }
1323
1324 return QgsGeometry( diffGeom.release() );
1325}
1326
1328{
1329 if ( d->geometry )
1330 {
1331 return d->geometry->boundingBox();
1332 }
1333 return QgsRectangle();
1334}
1335
1337{
1338 if ( d->geometry )
1339 {
1340 return d->geometry->boundingBox3D();
1341 }
1342 return QgsBox3D();
1343}
1344
1345
1346QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1347{
1348 mLastError.clear();
1349
1350 if ( isNull() )
1351 return QgsGeometry();
1352
1353 if ( type() == Qgis::GeometryType::Point && d->geometry->partCount() == 1 )
1354 {
1355 area = 0;
1356 angle = 0;
1357 width = 0;
1358 height = 0;
1359 return QgsGeometry::fromRect( d->geometry->boundingBox() );
1360 }
1361
1362 QgsInternalGeometryEngine engine( *this );
1363 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1364 if ( res.isNull() )
1365 mLastError = engine.lastError();
1366 return res;
1367}
1368
1370{
1371 double area, angle, width, height;
1372 return orientedMinimumBoundingBox( area, angle, width, height );
1373}
1374
1375static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1376{
1377 auto l_boundary = boundary.length();
1378 QgsCircle circ_mec;
1379 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1380 {
1381 switch ( l_boundary )
1382 {
1383 case 0:
1384 circ_mec = QgsCircle();
1385 break;
1386 case 1:
1387 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1388 boundary.pop_back();
1389 break;
1390 case 2:
1391 {
1392 QgsPointXY p1 = boundary.last();
1393 boundary.pop_back();
1394 QgsPointXY p2 = boundary.last();
1395 boundary.pop_back();
1396 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1397 }
1398 break;
1399 default:
1400 QgsPoint p1( boundary.at( 0 ) );
1401 QgsPoint p2( boundary.at( 1 ) );
1402 QgsPoint p3( boundary.at( 2 ) );
1403 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1404 break;
1405 }
1406 return circ_mec;
1407 }
1408 else
1409 {
1410 QgsPointXY pxy = points.last();
1411 points.pop_back();
1412 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1413 QgsPoint p( pxy );
1414 if ( !circ_mec.contains( p ) )
1415 {
1416 boundary.append( pxy );
1417 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1418 }
1419 }
1420 return circ_mec;
1421}
1422
1423QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1424{
1425 center = QgsPointXY();
1426 radius = 0;
1427
1428 if ( isEmpty() )
1429 {
1430 return QgsGeometry();
1431 }
1432
1433 /* optimization */
1434 QgsGeometry hull = convexHull();
1435 if ( hull.isNull() )
1436 return QgsGeometry();
1437
1438 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1440
1441 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1442 center = QgsPointXY( circ.center() );
1443 radius = circ.radius();
1444 QgsGeometry geom;
1445 geom.set( circ.toPolygon( segments ) );
1446 return geom;
1447
1448}
1449
1451{
1452 QgsPointXY center;
1453 double radius;
1454 return minimalEnclosingCircle( center, radius, segments );
1455
1456}
1457
1458QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1459{
1460 QgsInternalGeometryEngine engine( *this );
1461
1462 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1463}
1464
1465QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1466{
1467 QgsInternalGeometryEngine engine( *this );
1468 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1469}
1470
1471QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1472{
1473 QgsInternalGeometryEngine engine( *this );
1474 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1475}
1476
1477QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1478{
1479 QgsInternalGeometryEngine engine( *this );
1480 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1481}
1482
1483QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1484{
1485 QgsInternalGeometryEngine engine( *this );
1486 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1487}
1488
1489QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1490{
1491 QgsInternalGeometryEngine engine( *this );
1492 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1493}
1494
1495QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1496{
1497 QgsInternalGeometryEngine engine( *this );
1498 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1499}
1500
1501QgsGeometry QgsGeometry::applyDashPattern( const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset ) const
1502{
1503 QgsInternalGeometryEngine engine( *this );
1504 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1505}
1506
1507QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1508{
1509 if ( !d->geometry )
1510 {
1511 return QgsGeometry();
1512 }
1513 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1514}
1515
1516bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1517{
1518 if ( !d->geometry )
1519 return false;
1520
1521 detach();
1522 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1523}
1524
1526{
1527 // fast case, check bounding boxes
1528 if ( !boundingBoxIntersects( r ) )
1529 return false;
1530
1531 const Qgis::WkbType flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1532 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1533 if ( flatType == Qgis::WkbType::Point )
1534 {
1535 return true;
1536 }
1537
1538 // Workaround for issue issue GH #51429
1539 // in case of multi polygon, intersection with an empty rect fails
1540 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1541 {
1542 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1543 return contains( QgsGeometry::fromPointXY( center ) );
1544 }
1545
1546 QgsGeometry g = fromRect( r );
1547 return intersects( g );
1548}
1549
1550bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1551{
1552 if ( !d->geometry || geometry.isNull() )
1553 {
1554 return false;
1555 }
1556
1557 QgsGeos geos( d->geometry.get() );
1558 mLastError.clear();
1559 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1560}
1561
1563{
1564 if ( !d->geometry )
1565 {
1566 return false;
1567 }
1568
1569 return d->geometry->boundingBoxIntersects( rectangle );
1570}
1571
1573{
1574 if ( !d->geometry || geometry.isNull() )
1575 {
1576 return false;
1577 }
1578
1579 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1580}
1581
1582bool QgsGeometry::contains( const QgsPointXY *p ) const
1583{
1584 if ( !d->geometry || !p )
1585 {
1586 return false;
1587 }
1588
1589 QgsGeos geos( d->geometry.get() );
1590 mLastError.clear();
1591 return geos.contains( p->x(), p->y(), &mLastError );
1592}
1593
1594bool QgsGeometry::contains( double x, double y ) const
1595{
1596 if ( !d->geometry )
1597 {
1598 return false;
1599 }
1600
1601 QgsGeos geos( d->geometry.get() );
1602 mLastError.clear();
1603 return geos.contains( x, y, &mLastError );
1604}
1605
1606bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1607{
1608 if ( !d->geometry || geometry.isNull() )
1609 {
1610 return false;
1611 }
1612
1613 QgsGeos geos( d->geometry.get() );
1614 mLastError.clear();
1615 return geos.contains( geometry.d->geometry.get(), &mLastError );
1616}
1617
1618bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1619{
1620 if ( !d->geometry || geometry.isNull() )
1621 {
1622 return false;
1623 }
1624
1625 QgsGeos geos( d->geometry.get() );
1626 mLastError.clear();
1627 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1628}
1629
1630bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1631{
1632 if ( !d->geometry || geometry.isNull() )
1633 {
1634 return false;
1635 }
1636
1637 // fast check - are they shared copies of the same underlying geometry?
1638 if ( d == geometry.d )
1639 return true;
1640
1641 // fast check - distinct geometry types?
1642 if ( type() != geometry.type() )
1643 return false;
1644
1645 // slower check - actually test the geometries
1646 return *d->geometry == *geometry.d->geometry;
1647}
1648
1649bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1650{
1651 if ( !d->geometry || geometry.isNull() )
1652 {
1653 return false;
1654 }
1655
1656 QgsGeos geos( d->geometry.get() );
1657 mLastError.clear();
1658 return geos.touches( geometry.d->geometry.get(), &mLastError );
1659}
1660
1661bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1662{
1663 if ( !d->geometry || geometry.isNull() )
1664 {
1665 return false;
1666 }
1667
1668 QgsGeos geos( d->geometry.get() );
1669 mLastError.clear();
1670 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1671}
1672
1673bool QgsGeometry::within( const QgsGeometry &geometry ) const
1674{
1675 if ( !d->geometry || geometry.isNull() )
1676 {
1677 return false;
1678 }
1679
1680 QgsGeos geos( d->geometry.get() );
1681 mLastError.clear();
1682 return geos.within( geometry.d->geometry.get(), &mLastError );
1683}
1684
1685bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1686{
1687 if ( !d->geometry || geometry.isNull() )
1688 {
1689 return false;
1690 }
1691
1692 QgsGeos geos( d->geometry.get() );
1693 mLastError.clear();
1694 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1695}
1696
1697QString QgsGeometry::asWkt( int precision ) const
1698{
1699 if ( !d->geometry )
1700 {
1701 return QString();
1702 }
1703 return d->geometry->asWkt( precision );
1704}
1705
1706QString QgsGeometry::asJson( int precision ) const
1707{
1708 return QString::fromStdString( asJsonObject( precision ).dump() );
1709}
1710
1712{
1713 if ( !d->geometry )
1714 {
1715 return nullptr;
1716 }
1717 return d->geometry->asJsonObject( precision );
1718
1719}
1720
1721QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM, bool avoidDuplicates ) const
1722{
1723 QVector< QgsGeometry > res;
1724 if ( isNull() )
1725 return res;
1726
1727 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1728 {
1729 res << *this;
1730 return res;
1731 }
1732
1734 {
1735 return res;
1736 }
1737
1738 QgsGeometry newGeom = *this;
1739
1740 // Curved -> straight
1742 {
1743 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1744 }
1745
1746 // polygon -> line
1748 newGeom.type() == Qgis::GeometryType::Polygon )
1749 {
1750 // boundary gives us a (multi)line string of exterior + interior rings
1751 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1752 }
1753 // line -> polygon
1755 newGeom.type() == Qgis::GeometryType::Line )
1756 {
1757 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1758 const QgsGeometry source = newGeom;
1759 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1760 {
1761 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1762 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1763 {
1765 {
1766 auto cp = std::make_unique< QgsCurvePolygon >();
1767 cp->setExteriorRing( curve );
1768 ( void )exterior.release();
1769 gc->addGeometry( cp.release() );
1770 }
1771 else
1772 {
1773 auto p = std::make_unique< QgsPolygon >();
1774 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1775 ( void )exterior.release();
1776 gc->addGeometry( p.release() );
1777 }
1778 }
1779 }
1780 newGeom = QgsGeometry( std::move( gc ) );
1781 }
1782
1783 // line/polygon -> points
1785 ( newGeom.type() == Qgis::GeometryType::Line ||
1786 newGeom.type() == Qgis::GeometryType::Polygon ) )
1787 {
1788 // lines/polygons to a point layer, extract all vertices
1789 auto mp = std::make_unique< QgsMultiPoint >();
1790 const QgsGeometry source = newGeom;
1791 QSet< QgsPoint > added;
1792 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1793 {
1794 if ( avoidDuplicates && added.contains( *vertex ) )
1795 continue; // avoid duplicate points, e.g. start/end of rings
1796 mp->addGeometry( ( *vertex ).clone() );
1797 added.insert( *vertex );
1798 }
1799 newGeom = QgsGeometry( std::move( mp ) );
1800 }
1801
1802 //(Multi)Polygon to PolyhedralSurface
1805 {
1806 auto polySurface = std::make_unique< QgsPolyhedralSurface >();
1807 const QgsGeometry source = newGeom;
1808 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1809 {
1810 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1811 {
1812 polySurface->addPatch( polygon->clone() );
1813 }
1814 }
1815 newGeom = QgsGeometry( std::move( polySurface ) );
1816 }
1817
1818 // Polygon -> Triangle
1821 {
1822 auto triangle = std::make_unique< QgsTriangle >();
1823 const QgsGeometry source = newGeom;
1824 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( newGeom.constGet() ) )
1825 {
1826 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
1827 }
1828 newGeom = QgsGeometry( std::move( triangle ) );
1829 }
1830
1831
1832 // Single -> multi
1833 if ( QgsWkbTypes::isMultiType( type ) && ! newGeom.isMultipart( ) )
1834 {
1835 newGeom.convertToMultiType();
1836 }
1837 // Drop Z/M
1838 if ( newGeom.constGet()->is3D() && ! QgsWkbTypes::hasZ( type ) )
1839 {
1840 newGeom.get()->dropZValue();
1841 }
1842 if ( newGeom.constGet()->isMeasure() && ! QgsWkbTypes::hasM( type ) )
1843 {
1844 newGeom.get()->dropMValue();
1845 }
1846 // Add Z/M back, set to 0
1847 if ( ! newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
1848 {
1849 newGeom.get()->addZValue( defaultZ );
1850 }
1851 if ( ! newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
1852 {
1853 newGeom.get()->addMValue( defaultM );
1854 }
1855
1856 // Straight -> curve
1858 {
1859 newGeom.convertToCurvedMultiType();
1860 }
1861
1862 // Multi -> single
1863 if ( ! QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart( ) )
1864 {
1865 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1866 res.reserve( parts->partCount() );
1867 for ( int i = 0; i < parts->partCount( ); i++ )
1868 {
1869 res << QgsGeometry( parts->geometryN( i )->clone() );
1870 }
1871 }
1872 else
1873 {
1874 res << newGeom;
1875 }
1876 return res;
1877}
1878
1879QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
1880{
1881 switch ( destType )
1882 {
1884 return convertToPoint( destMultipart );
1885
1887 return convertToLine( destMultipart );
1888
1890 return convertToPolygon( destMultipart );
1891
1892 default:
1893 return QgsGeometry();
1894 }
1895}
1896
1898{
1899 if ( !d->geometry )
1900 {
1901 return false;
1902 }
1903
1904 if ( isMultipart() ) //already multitype, no need to convert
1905 {
1906 return true;
1907 }
1908
1909 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
1910 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1911 if ( !multiGeom )
1912 {
1913 return false;
1914 }
1915
1916 //try to avoid cloning existing geometry whenever we can
1917
1918 //want to see a magic trick?... gather round kiddies...
1919 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
1920 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
1921 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
1922 multiGeom->addGeometry( d->geometry.release() );
1923 // and replace it with the multi geometry.
1924 // TADA! a clone free conversion in some cases
1925 d->geometry = std::move( geom );
1926 return true;
1927}
1928
1930{
1931 if ( !d->geometry )
1932 {
1933 return false;
1934 }
1935
1936 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
1937 {
1942 {
1943 return true;
1944 }
1945 default:
1946 break;
1947 }
1948
1949 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
1950 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1951 if ( !multiGeom )
1952 {
1953 return false;
1954 }
1955
1956 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1957 if ( sourceMultiGeom )
1958 {
1959 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
1960 {
1961 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
1962 return false;
1963 }
1964 }
1965 else
1966 {
1967 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
1968 return false;
1969 }
1970
1971 reset( std::move( geom ) );
1972 return true;
1973}
1974
1976{
1977 if ( !d->geometry )
1978 {
1979 return false;
1980 }
1981
1982 if ( !isMultipart() ) //already single part, no need to convert
1983 {
1984 return true;
1985 }
1986
1987 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1988 if ( !multiGeom || multiGeom->partCount() < 1 )
1989 return false;
1990
1991 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
1992 reset( std::move( firstPart ) );
1993 return true;
1994}
1995
1996
1998{
1999 const QgsGeometryCollection *origGeom = qgsgeometry_cast<const QgsGeometryCollection *>( constGet() );
2000 if ( !origGeom )
2001 return false;
2002
2003 std::unique_ptr<QgsGeometryCollection> resGeom;
2004 switch ( geomType )
2005 {
2007 resGeom = std::make_unique<QgsMultiPoint>();
2008 break;
2010 resGeom = std::make_unique<QgsMultiLineString>();
2011 break;
2013 resGeom = std::make_unique<QgsMultiPolygon>();
2014 break;
2015 default:
2016 break;
2017 }
2018 if ( !resGeom )
2019 return false;
2020
2021 resGeom->reserve( origGeom->numGeometries() );
2022 for ( int i = 0; i < origGeom->numGeometries(); ++i )
2023 {
2024 const QgsAbstractGeometry *g = origGeom->geometryN( i );
2025 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
2026 resGeom->addGeometry( g->clone() );
2027 }
2028
2029 set( resGeom.release() );
2030 return true;
2031}
2032
2033
2035{
2036 if ( !d->geometry )
2037 {
2038 return QgsPointXY();
2039 }
2040 if ( const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
2041 {
2042 return QgsPointXY( pt->x(), pt->y() );
2043 }
2044 else
2045 {
2046 return QgsPointXY();
2047 }
2048}
2049
2051{
2052 QgsPolylineXY polyLine;
2053 if ( !d->geometry )
2054 {
2055 return polyLine;
2056 }
2057
2058 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve
2060 std::unique_ptr< QgsLineString > segmentizedLine;
2061 QgsLineString *line = nullptr;
2062 if ( doSegmentation )
2063 {
2064 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
2065 if ( !curve )
2066 {
2067 return polyLine;
2068 }
2069 segmentizedLine.reset( curve->curveToLine() );
2070 line = segmentizedLine.get();
2071 }
2072 else
2073 {
2074 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
2075 if ( !line )
2076 {
2077 return polyLine;
2078 }
2079 }
2080
2081 int nVertices = line->numPoints();
2082 polyLine.resize( nVertices );
2083 QgsPointXY *data = polyLine.data();
2084 const double *xData = line->xData();
2085 const double *yData = line->yData();
2086 for ( int i = 0; i < nVertices; ++i )
2087 {
2088 data->setX( *xData++ );
2089 data->setY( *yData++ );
2090 data++;
2091 }
2092
2093 return polyLine;
2094}
2095
2097{
2098 if ( !d->geometry )
2099 return QgsPolygonXY();
2100
2101 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
2102
2103 QgsPolygon *p = nullptr;
2104 std::unique_ptr< QgsPolygon > segmentized;
2105 if ( doSegmentation )
2106 {
2107 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
2108 if ( !curvePoly )
2109 {
2110 return QgsPolygonXY();
2111 }
2112 segmentized.reset( curvePoly->toPolygon() );
2113 p = segmentized.get();
2114 }
2115 else
2116 {
2117 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
2118 }
2119
2120 if ( !p )
2121 {
2122 return QgsPolygonXY();
2123 }
2124
2125 QgsPolygonXY polygon;
2126 convertPolygon( *p, polygon );
2127
2128 return polygon;
2129}
2130
2132{
2133 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
2134 {
2135 return QgsMultiPointXY();
2136 }
2137
2138 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
2139 if ( !mp )
2140 {
2141 return QgsMultiPointXY();
2142 }
2143
2144 int nPoints = mp->numGeometries();
2145 QgsMultiPointXY multiPoint( nPoints );
2146 for ( int i = 0; i < nPoints; ++i )
2147 {
2148 const QgsPoint *pt = mp->pointN( i );
2149 multiPoint[i].setX( pt->x() );
2150 multiPoint[i].setY( pt->y() );
2151 }
2152 return multiPoint;
2153}
2154
2156{
2157 if ( !d->geometry )
2158 {
2159 return QgsMultiPolylineXY();
2160 }
2161
2162 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2163 if ( !geomCollection )
2164 {
2165 return QgsMultiPolylineXY();
2166 }
2167
2168 int nLines = geomCollection->numGeometries();
2169 if ( nLines < 1 )
2170 {
2171 return QgsMultiPolylineXY();
2172 }
2173
2175 mpl.reserve( nLines );
2176 for ( int i = 0; i < nLines; ++i )
2177 {
2178 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2179 std::unique_ptr< QgsLineString > segmentized;
2180 if ( !line )
2181 {
2182 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2183 if ( !curve )
2184 {
2185 continue;
2186 }
2187 segmentized.reset( curve->curveToLine() );
2188 line = segmentized.get();
2189 }
2190
2191 QgsPolylineXY polyLine;
2192 int nVertices = line->numPoints();
2193 polyLine.resize( nVertices );
2194 QgsPointXY *data = polyLine.data();
2195 const double *xData = line->xData();
2196 const double *yData = line->yData();
2197 for ( int i = 0; i < nVertices; ++i )
2198 {
2199 data->setX( *xData++ );
2200 data->setY( *yData++ );
2201 data++;
2202 }
2203 mpl.append( polyLine );
2204 }
2205 return mpl;
2206}
2207
2209{
2210 if ( !d->geometry )
2211 {
2212 return QgsMultiPolygonXY();
2213 }
2214
2215 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2216 if ( !geomCollection )
2217 {
2218 return QgsMultiPolygonXY();
2219 }
2220
2221 const int nPolygons = geomCollection->numGeometries();
2222 if ( nPolygons < 1 )
2223 {
2224 return QgsMultiPolygonXY();
2225 }
2226
2228 mp.reserve( nPolygons );
2229 for ( int i = 0; i < nPolygons; ++i )
2230 {
2231 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2232 if ( !polygon )
2233 {
2234 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2235 if ( cPolygon )
2236 {
2237 polygon = cPolygon->toPolygon();
2238 }
2239 else
2240 {
2241 continue;
2242 }
2243 }
2244
2245 QgsPolygonXY poly;
2246 convertPolygon( *polygon, poly );
2247 mp.push_back( poly );
2248 }
2249 return mp;
2250}
2251
2252double QgsGeometry::area() const
2253{
2254 if ( !d->geometry )
2255 {
2256 return -1.0;
2257 }
2258
2259 return d->geometry->area();
2260}
2261
2263{
2264 if ( !d->geometry )
2265 {
2266 return -1.0;
2267 }
2268
2269 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2270 {
2272 return 0.0;
2273
2275 return d->geometry->length();
2276
2278 return d->geometry->perimeter();
2279
2282 return d->geometry->length();
2283 }
2284 return -1;
2285}
2286
2287double QgsGeometry::distance( const QgsGeometry &geom ) const
2288{
2289 if ( !d->geometry || !geom.d->geometry )
2290 {
2291 return -1.0;
2292 }
2293
2294 // avoid calling geos for trivial point-to-point distance calculations
2296 {
2297 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2298 }
2299
2300 QgsGeos g( d->geometry.get() );
2301 mLastError.clear();
2302 return g.distance( geom.d->geometry.get(), &mLastError );
2303}
2304
2306{
2307 if ( !d->geometry || !geom.d->geometry )
2308 {
2309 return -1.0;
2310 }
2311
2312 QgsGeos g( d->geometry.get() );
2313 mLastError.clear();
2314 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2315}
2316
2317double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2318{
2319 if ( !d->geometry || !geom.d->geometry )
2320 {
2321 return -1.0;
2322 }
2323
2324 QgsGeos g( d->geometry.get() );
2325 mLastError.clear();
2326 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2327}
2328
2329
2331{
2332 if ( !d->geometry || !geom.d->geometry )
2333 {
2334 return -1.0;
2335 }
2336
2337 QgsGeos g( d->geometry.get() );
2338 mLastError.clear();
2339 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
2340}
2341
2342double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2343{
2344 if ( !d->geometry || !geom.d->geometry )
2345 {
2346 return -1.0;
2347 }
2348
2349 QgsGeos g( d->geometry.get() );
2350 mLastError.clear();
2351 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2352}
2353
2355{
2356 if ( !d->geometry || d->geometry.get()->isEmpty() )
2358 return d->geometry->vertices_begin();
2359}
2360
2362{
2363 if ( !d->geometry || d->geometry.get()->isEmpty() )
2365 return d->geometry->vertices_end();
2366}
2367
2369{
2370 if ( !d->geometry || d->geometry.get()->isEmpty() )
2371 return QgsVertexIterator();
2372 return QgsVertexIterator( d->geometry.get() );
2373}
2374
2376{
2377 if ( !d->geometry )
2379
2380 detach();
2381 return d->geometry->parts_begin();
2382}
2383
2385{
2386 if ( !d->geometry )
2388 return d->geometry->parts_end();
2389}
2390
2392{
2393 if ( !d->geometry )
2395 return d->geometry->const_parts_begin();
2396}
2397
2399{
2400 if ( !d->geometry )
2402 return d->geometry->const_parts_end();
2403}
2404
2406{
2407 if ( !d->geometry )
2408 return QgsGeometryPartIterator();
2409
2410 detach();
2411 return QgsGeometryPartIterator( d->geometry.get() );
2412}
2413
2415{
2416 if ( !d->geometry )
2418
2419 return QgsGeometryConstPartIterator( d->geometry.get() );
2420}
2421
2422QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
2423{
2424 if ( !d->geometry )
2425 {
2426 return QgsGeometry();
2427 }
2428
2429 QgsGeos g( d->geometry.get() );
2430 mLastError.clear();
2431 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError ) );
2432 if ( !geom )
2433 {
2434 QgsGeometry result;
2435 result.mLastError = mLastError;
2436 return result;
2437 }
2438 return QgsGeometry( std::move( geom ) );
2439}
2440
2441QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit ) const
2442{
2443 if ( !d->geometry )
2444 {
2445 return QgsGeometry();
2446 }
2447
2448 QgsGeos g( d->geometry.get() );
2449 mLastError.clear();
2450 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError );
2451 if ( !geom )
2452 {
2453 QgsGeometry result;
2454 result.mLastError = mLastError;
2455 return result;
2456 }
2457 return QgsGeometry( geom );
2458}
2459
2460QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2461{
2462 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2463 {
2464 return QgsGeometry();
2465 }
2466
2467 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2468 {
2469 const QVector<QgsGeometry> parts = asGeometryCollection();
2470 QVector<QgsGeometry> results;
2471 results.reserve( parts.count() );
2472 for ( const QgsGeometry &part : parts )
2473 {
2474 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2475 if ( !result.isNull() )
2476 results << result;
2477 }
2478 if ( results.isEmpty() )
2479 return QgsGeometry();
2480
2481 QgsGeometry first = results.takeAt( 0 );
2482 for ( const QgsGeometry &result : std::as_const( results ) )
2483 {
2484 first.addPart( result );
2485 }
2486 return first;
2487 }
2488 else
2489 {
2490 QgsGeos geos( d->geometry.get() );
2491 mLastError.clear();
2492
2493 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2494 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2495
2496 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2497 if ( !offsetGeom )
2498 {
2499 QgsGeometry result;
2500 result.mLastError = mLastError;
2501 return result;
2502 }
2503
2504 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2505 {
2506 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2507 if ( newOrientation != prevOrientation )
2508 {
2509 // GEOS has flipped line orientation, flip it back
2510 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2511 offsetGeom = std::move( flipped );
2512 }
2513 }
2514 return QgsGeometry( std::move( offsetGeom ) );
2515 }
2516}
2517
2518QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2519{
2520 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2521 {
2522 return QgsGeometry();
2523 }
2524
2525 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2526 {
2527 const QVector<QgsGeometry> parts = asGeometryCollection();
2528 QVector<QgsGeometry> results;
2529 results.reserve( parts.count() );
2530 for ( const QgsGeometry &part : parts )
2531 {
2532 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2533 if ( !result.isNull() )
2534 results << result;
2535 }
2536 if ( results.isEmpty() )
2537 return QgsGeometry();
2538
2539 QgsGeometry first = results.takeAt( 0 );
2540 for ( const QgsGeometry &result : std::as_const( results ) )
2541 {
2542 first.addPart( result );
2543 }
2544 return first;
2545 }
2546 else
2547 {
2548 QgsGeos geos( d->geometry.get() );
2549 mLastError.clear();
2550 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side,
2551 joinStyle, miterLimit, &mLastError );
2552 if ( !bufferGeom )
2553 {
2554 QgsGeometry result;
2555 result.mLastError = mLastError;
2556 return result;
2557 }
2558 return QgsGeometry( std::move( bufferGeom ) );
2559 }
2560}
2561
2562QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2563{
2564 QgsInternalGeometryEngine engine( *this );
2565
2566 return engine.taperedBuffer( startWidth, endWidth, segments );
2567}
2568
2570{
2571 QgsInternalGeometryEngine engine( *this );
2572
2573 return engine.variableWidthBufferByM( segments );
2574}
2575
2576QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2577{
2578 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2579 {
2580 return QgsGeometry();
2581 }
2582
2583 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2584 {
2585 const QVector<QgsGeometry> parts = asGeometryCollection();
2586 QVector<QgsGeometry> results;
2587 results.reserve( parts.count() );
2588 for ( const QgsGeometry &part : parts )
2589 {
2590 QgsGeometry result = part.extendLine( startDistance, endDistance );
2591 if ( !result.isNull() )
2592 results << result;
2593 }
2594 if ( results.isEmpty() )
2595 return QgsGeometry();
2596
2597 QgsGeometry first = results.takeAt( 0 );
2598 for ( const QgsGeometry &result : std::as_const( results ) )
2599 {
2600 first.addPart( result );
2601 }
2602 return first;
2603 }
2604 else
2605 {
2606 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2607 if ( !line )
2608 return QgsGeometry();
2609
2610 std::unique_ptr< QgsLineString > newLine( line->clone() );
2611 newLine->extend( startDistance, endDistance );
2612 return QgsGeometry( std::move( newLine ) );
2613 }
2614}
2615
2616QgsGeometry QgsGeometry::simplify( double tolerance ) const
2617{
2618 if ( !d->geometry )
2619 {
2620 return QgsGeometry();
2621 }
2622
2623 QgsGeos geos( d->geometry.get() );
2624 mLastError.clear();
2625 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError ) );
2626 if ( !simplifiedGeom )
2627 {
2628 QgsGeometry result;
2629 result.mLastError = mLastError;
2630 return result;
2631 }
2632 return QgsGeometry( std::move( simplifiedGeom ) );
2633}
2634
2635QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2636{
2637 QgsInternalGeometryEngine engine( *this );
2638
2639 return engine.densifyByCount( extraNodesPerSegment );
2640}
2641
2643{
2644 QgsInternalGeometryEngine engine( *this );
2645
2646 return engine.densifyByDistance( distance );
2647}
2648
2649QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2650{
2651 QgsInternalGeometryEngine engine( *this );
2652
2653 return engine.convertToCurves( distanceTolerance, angleTolerance );
2654}
2655
2657{
2658 if ( !d->geometry )
2659 {
2660 return QgsGeometry();
2661 }
2662
2663 // avoid calling geos for trivial point centroids
2664 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
2665 {
2666 QgsGeometry c = *this;
2667 c.get()->dropZValue();
2668 c.get()->dropMValue();
2669 return c;
2670 }
2671
2672 QgsGeos geos( d->geometry.get() );
2673
2674 mLastError.clear();
2675 QgsGeometry result( geos.centroid( &mLastError ) );
2676 result.mLastError = mLastError;
2677 return result;
2678}
2679
2681{
2682 if ( !d->geometry )
2683 {
2684 return QgsGeometry();
2685 }
2686
2687 QgsGeos geos( d->geometry.get() );
2688
2689 mLastError.clear();
2690 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2691 result.mLastError = mLastError;
2692 return result;
2693}
2694
2695QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2696{
2697 QgsInternalGeometryEngine engine( *this );
2698
2699 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2700}
2701
2702QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2703{
2704 if ( !d->geometry )
2705 {
2706 return QgsGeometry();
2707 }
2708
2709 QgsGeos geos( d->geometry.get() );
2710
2711 mLastError.clear();
2712 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2713 result.mLastError = mLastError;
2714 return result;
2715}
2716
2718{
2719 if ( !d->geometry )
2720 {
2721 return QgsGeometry();
2722 }
2723
2724 QgsGeos geos( d->geometry.get() );
2725
2726 mLastError.clear();
2727 QgsGeometry result( geos.minimumWidth( &mLastError ) );
2728 result.mLastError = mLastError;
2729 return result;
2730}
2731
2733{
2734 if ( !d->geometry )
2735 {
2736 return std::numeric_limits< double >::quiet_NaN();
2737 }
2738
2739 QgsGeos geos( d->geometry.get() );
2740
2741 mLastError.clear();
2742 return geos.minimumClearance( &mLastError );
2743}
2744
2746{
2747 if ( !d->geometry )
2748 {
2749 return QgsGeometry();
2750 }
2751
2752 QgsGeos geos( d->geometry.get() );
2753
2754 mLastError.clear();
2755 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
2756 result.mLastError = mLastError;
2757 return result;
2758}
2759
2761{
2762 if ( !d->geometry )
2763 {
2764 return QgsGeometry();
2765 }
2766 QgsGeos geos( d->geometry.get() );
2767 mLastError.clear();
2768 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
2769 if ( !cHull )
2770 {
2771 QgsGeometry geom;
2772 geom.mLastError = mLastError;
2773 return geom;
2774 }
2775 return QgsGeometry( std::move( cHull ) );
2776}
2777
2778QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles ) const
2779{
2780 if ( !d->geometry )
2781 {
2782 return QgsGeometry();
2783 }
2784 QgsGeos geos( d->geometry.get() );
2785 mLastError.clear();
2786 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError ) );
2787 if ( !concaveHull )
2788 {
2789 QgsGeometry geom;
2790 geom.mLastError = mLastError;
2791 return geom;
2792 }
2793 return QgsGeometry( std::move( concaveHull ) );
2794}
2795
2796QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
2797{
2798 if ( !d->geometry )
2799 {
2800 return QgsGeometry();
2801 }
2802
2803 QgsGeos geos( d->geometry.get() );
2804 mLastError.clear();
2805 QgsGeometry result = QgsGeometry( geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError ) );
2806 result.mLastError = mLastError;
2807 return result;
2808}
2809
2810QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
2811{
2812 if ( !d->geometry )
2813 {
2814 return QgsGeometry();
2815 }
2816
2817 QgsGeos geos( d->geometry.get() );
2818 mLastError.clear();
2819 QgsGeometry result = QgsGeometry( geos.delaunayTriangulation( tolerance, edgesOnly ) );
2820 result.mLastError = mLastError;
2821 return result;
2822}
2823
2825{
2826 if ( !d->geometry )
2827 {
2828 return QgsGeometry();
2829 }
2830
2831 QgsGeos geos( d->geometry.get() );
2832 mLastError.clear();
2833 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
2834 result.mLastError = mLastError;
2835 return result;
2836}
2837
2839{
2840 if ( !d->geometry )
2841 {
2842 return QgsGeometry();
2843 }
2844
2848 return QgsGeometry();
2849
2850 QgsGeos geos( d->geometry.get() );
2851 mLastError.clear();
2852 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
2853 result.mLastError = mLastError;
2854 return result;
2855}
2856
2858{
2859 if ( !d->geometry )
2860 {
2862 }
2863
2864 QgsGeos geos( d->geometry.get() );
2865 mLastError.clear();
2866 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
2867
2868 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
2869
2870 if ( invalidEdges && invalidEdgesGeom )
2871 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
2872
2873 return result;
2874}
2875
2876QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
2877{
2878 if ( !d->geometry )
2879 {
2880 return QgsGeometry();
2881 }
2882
2883 QgsGeos geos( d->geometry.get() );
2884 mLastError.clear();
2885 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
2886 result.mLastError = mLastError;
2887 return result;
2888}
2889
2891{
2892 if ( !d->geometry )
2893 {
2894 return QgsGeometry();
2895 }
2896
2897 QgsGeos geos( d->geometry.get() );
2898 mLastError.clear();
2899 QgsGeometry result( geos.node( &mLastError ) );
2900 result.mLastError = mLastError;
2901 return result;
2902}
2903
2905{
2906 if ( !d->geometry )
2907 {
2908 return QgsGeometry();
2909 }
2910
2911 QgsGeos geos( d->geometry.get() );
2912 mLastError.clear();
2913 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
2914 result.mLastError = mLastError;
2915 return result;
2916}
2917
2918QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters ) const
2919{
2920 if ( !d->geometry )
2921 {
2922 return QgsGeometry();
2923 }
2924
2925 const QgsAbstractGeometry *geom = d->geometry.get();
2926 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
2927 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
2928 {
2929 segmentizedCopy.reset( d->geometry->segmentize() );
2930 geom = segmentizedCopy.get();
2931 }
2932
2933 QgsGeos geos( geom );
2934 mLastError.clear();
2935 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters ) );
2936 if ( !result )
2937 {
2938 QgsGeometry geom;
2939 geom.mLastError = mLastError;
2940 return geom;
2941 }
2942 return QgsGeometry( std::move( result ) );
2943}
2944
2946{
2947 if ( !d->geometry )
2948 {
2949 return QgsGeometry();
2950 }
2951
2952 QgsGeometry line = *this;
2954 return QgsGeometry();
2955 else if ( type() == Qgis::GeometryType::Polygon )
2956 {
2957 line = QgsGeometry( d->geometry->boundary() );
2958 }
2959
2960 const QgsCurve *curve = nullptr;
2961 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( line.constGet() ) )
2962 {
2963 // if multi part, iterate through parts to find target part
2964 for ( int part = 0; part < collection->numGeometries(); ++part )
2965 {
2966 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
2967 if ( !candidate )
2968 continue;
2969 const double candidateLength = candidate->length();
2970 if ( candidateLength >= distance )
2971 {
2972 curve = candidate;
2973 break;
2974 }
2975
2976 distance -= candidateLength;
2977 }
2978 }
2979 else
2980 {
2981 curve = qgsgeometry_cast< const QgsCurve * >( line.constGet() );
2982 }
2983 if ( !curve )
2984 return QgsGeometry();
2985
2986 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
2987 if ( !result )
2988 {
2989 return QgsGeometry();
2990 }
2991 return QgsGeometry( std::move( result ) );
2992}
2993
2994double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
2995{
2996 if ( type() != Qgis::GeometryType::Line )
2997 return -1;
2998
3000 return -1;
3001
3002 QgsGeometry segmentized = *this;
3004 {
3005 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
3006 }
3007
3008 QgsGeos geos( d->geometry.get() );
3009 mLastError.clear();
3010 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
3011}
3012
3013double QgsGeometry::interpolateAngle( double distance ) const
3014{
3015 if ( !d->geometry || d->geometry->isEmpty() )
3016 return 0.0;
3017
3018 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
3020 return 0.0;
3021
3022 // always operate on segmentized geometries
3023 QgsGeometry segmentized = *this;
3024 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
3025 {
3026 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
3027 }
3028
3029 QgsVertexId previous;
3030 QgsVertexId next;
3031 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
3032 return 0.0;
3033
3034 if ( previous == next )
3035 {
3036 // distance coincided exactly with a vertex
3037 QgsVertexId v2 = previous;
3038 QgsVertexId v1;
3039 QgsVertexId v3;
3040 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
3041 if ( v1.isValid() && v3.isValid() )
3042 {
3043 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3044 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3045 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
3046 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3047 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
3048 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
3049 }
3050 else if ( v3.isValid() )
3051 {
3052 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
3053 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
3054 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3055 }
3056 else
3057 {
3058 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3059 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3060 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3061 }
3062 }
3063 else
3064 {
3065 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
3066 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
3067 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3068 }
3069}
3070
3072{
3073 if ( !d->geometry || geometry.isNull() )
3074 {
3075 return QgsGeometry();
3076 }
3077
3078 QgsGeos geos( d->geometry.get() );
3079
3080 mLastError.clear();
3081 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters ) );
3082
3083 if ( !resultGeom )
3084 {
3085 QgsGeometry geom;
3086 geom.mLastError = mLastError;
3087 return geom;
3088 }
3089
3090 return QgsGeometry( std::move( resultGeom ) );
3091}
3092
3093QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters ) const
3094{
3095 if ( !d->geometry || geometry.isNull() )
3096 {
3097 return QgsGeometry();
3098 }
3099
3100 QgsGeos geos( d->geometry.get() );
3101 mLastError.clear();
3102 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters ) );
3103 if ( !resultGeom )
3104 {
3105 QgsGeometry geom;
3106 geom.mLastError = mLastError;
3107 return geom;
3108 }
3109 return QgsGeometry( std::move( resultGeom ) );
3110}
3111
3113{
3114 if ( !d->geometry )
3115 {
3116 return QgsGeometry();
3117 }
3118
3120 {
3121 // special case - a single linestring was passed
3122 return QgsGeometry( *this );
3123 }
3124
3125 QgsGeos geos( d->geometry.get() );
3126 mLastError.clear();
3127 QgsGeometry result( geos.mergeLines( &mLastError, parameters ) );
3128 result.mLastError = mLastError;
3129 return result;
3130}
3131
3133{
3134 if ( !d->geometry || geometry.isNull() )
3135 {
3136 return QgsGeometry();
3137 }
3138
3139 QgsGeos geos( d->geometry.get() );
3140
3141 mLastError.clear();
3142 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters ) );
3143 if ( !resultGeom )
3144 {
3145 QgsGeometry geom;
3146 geom.mLastError = mLastError;
3147 return geom;
3148 }
3149 return QgsGeometry( std::move( resultGeom ) );
3150}
3151
3153{
3154 if ( !d->geometry || geometry.isNull() )
3155 {
3156 return QgsGeometry();
3157 }
3158
3159 QgsGeos geos( d->geometry.get() );
3160
3161 mLastError.clear();
3162 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters ) );
3163 if ( !resultGeom )
3164 {
3165 QgsGeometry geom;
3166 geom.mLastError = mLastError;
3167 return geom;
3168 }
3169 return QgsGeometry( std::move( resultGeom ) );
3170}
3171
3173{
3174 QgsInternalGeometryEngine engine( *this );
3175
3176 return engine.extrude( x, y );
3177}
3178
3180
3181QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3182{
3184 return QVector< QgsPointXY >();
3185
3186 QgsInternalGeometryEngine engine( *this );
3187 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3188 mLastError = engine.lastError();
3189 return res;
3190}
3191
3192QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3193{
3195 return QVector< QgsPointXY >();
3196
3197 QgsInternalGeometryEngine engine( *this );
3198 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3199 mLastError = engine.lastError();
3200 return res;
3201}
3203
3205{
3206 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3207}
3208
3210{
3211 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3212}
3213
3214QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3215{
3216 QVector<QgsGeometry> geometryList;
3217 if ( !d->geometry )
3218 {
3219 return geometryList;
3220 }
3221
3222 QgsGeometryCollection *gc = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
3223 if ( gc )
3224 {
3225 int numGeom = gc->numGeometries();
3226 geometryList.reserve( numGeom );
3227 for ( int i = 0; i < numGeom; ++i )
3228 {
3229 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3230 }
3231 }
3232 else //a singlepart geometry
3233 {
3234 geometryList.append( *this );
3235 }
3236
3237 return geometryList;
3238}
3239
3241{
3242 QgsPointXY point = asPoint();
3243 return point.toQPointF();
3244}
3245
3247{
3248 const QgsAbstractGeometry *part = constGet();
3249
3250 // if a geometry collection, get first part only
3251 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( part ) )
3252 {
3253 if ( collection->numGeometries() > 0 )
3254 part = collection->geometryN( 0 );
3255 else
3256 return QPolygonF();
3257 }
3258
3259 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3260 return curve->asQPolygonF();
3261 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3262 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3263 return QPolygonF();
3264}
3265
3266bool QgsGeometry::deleteRing( int ringNum, int partNum )
3267{
3268 if ( !d->geometry )
3269 {
3270 return false;
3271 }
3272
3273 detach();
3274 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3275 return ok;
3276}
3277
3278bool QgsGeometry::deletePart( int partNum )
3279{
3280 if ( !d->geometry )
3281 {
3282 return false;
3283 }
3284
3285 if ( !isMultipart() && partNum < 1 )
3286 {
3287 set( nullptr );
3288 return true;
3289 }
3290
3291 detach();
3292 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3293 return ok;
3294}
3295
3296Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3297{
3298 if ( !d->geometry )
3299 {
3301 }
3302
3303 Qgis::WkbType geomTypeBeforeModification = wkbType();
3304
3305 bool haveInvalidGeometry = false;
3306 bool geomModified = false;
3307
3308 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3309 if ( diffGeom )
3310 {
3311 reset( std::move( diffGeom ) );
3312 geomModified = true;
3313 }
3314
3315 if ( geomTypeBeforeModification != wkbType() )
3317 if ( haveInvalidGeometry )
3319 if ( !geomModified )
3321
3323}
3324
3356
3358{
3359 if ( !d->geometry )
3360 return QgsGeometry();
3361
3362 mLastError.clear();
3363 QgsGeos geos( d->geometry.get() );
3364 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError ) );
3365
3366 QgsGeometry result = QgsGeometry( std::move( g ) );
3367 result.mLastError = mLastError;
3368 return result;
3369}
3370
3375
3377{
3378 if ( !d->geometry )
3379 {
3381 }
3382
3383 if ( isMultipart() )
3384 {
3385 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3386 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3387 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3388 {
3389 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3390 }
3391 }
3392 else
3393 {
3394 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3395 {
3396 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3397 }
3398 }
3399
3401
3402}
3403
3405{
3406 if ( !d->geometry )
3407 return QgsGeometry();
3408
3409 if ( isMultipart() )
3410 {
3411 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3412 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3413 newCollection->reserve( collection->numGeometries() );
3414 for ( int i = 0; i < collection->numGeometries(); ++i )
3415 {
3416 const QgsAbstractGeometry *g = collection->geometryN( i );
3417 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3418 {
3419 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3420 corrected->forceClockwise();
3421 newCollection->addGeometry( corrected.release() );
3422 }
3423 else
3424 {
3425 newCollection->addGeometry( g->clone() );
3426 }
3427 }
3428 return QgsGeometry( std::move( newCollection ) );
3429 }
3430 else
3431 {
3432 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3433 {
3434 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3435 corrected->forceClockwise();
3436 return QgsGeometry( std::move( corrected ) );
3437 }
3438 else
3439 {
3440 // not a curve polygon, so return unchanged
3441 return *this;
3442 }
3443 }
3444}
3445
3447{
3448 if ( !d->geometry )
3449 return QgsGeometry();
3450
3451 if ( isMultipart() )
3452 {
3453 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3454 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3455 newCollection->reserve( collection->numGeometries() );
3456 for ( int i = 0; i < collection->numGeometries(); ++i )
3457 {
3458 const QgsAbstractGeometry *g = collection->geometryN( i );
3459 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3460 {
3461 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3462 corrected->forceCounterClockwise();
3463 newCollection->addGeometry( corrected.release() );
3464 }
3465 else
3466 {
3467 newCollection->addGeometry( g->clone() );
3468 }
3469 }
3470 return QgsGeometry( std::move( newCollection ) );
3471 }
3472 else
3473 {
3474 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3475 {
3476 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3477 corrected->forceCounterClockwise();
3478 return QgsGeometry( std::move( corrected ) );
3479 }
3480 else
3481 {
3482 // not a curve polygon, so return unchanged
3483 return *this;
3484 }
3485 }
3486}
3487
3488
3489void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3490{
3491 errors.clear();
3492 if ( !d->geometry )
3493 return;
3494
3495 // avoid expensive calcs for trivial point geometries
3497 {
3498 return;
3499 }
3500
3501 switch ( method )
3502 {
3504 QgsGeometryValidator::validateGeometry( *this, errors, method );
3505 return;
3506
3508 {
3509 QgsGeos geos( d->geometry.get(), 0, Qgis::GeosCreationFlags() );
3510 QString error;
3511 QgsGeometry errorLoc;
3512 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3513 {
3514 if ( errorLoc.isNull() )
3515 {
3516 errors.append( QgsGeometry::Error( error ) );
3517 }
3518 else
3519 {
3520 const QgsPointXY point = errorLoc.asPoint();
3521 errors.append( QgsGeometry::Error( error, point ) );
3522 }
3523 return;
3524 }
3525 }
3526 }
3527}
3528
3530{
3531 if ( !d->geometry )
3532 {
3533 return;
3534 }
3535
3536 detach();
3537 d->geometry->normalize();
3538}
3539
3541{
3542 if ( !d->geometry )
3543 {
3544 return false;
3545 }
3546
3547 return d->geometry->isValid( mLastError, flags );
3548}
3549
3551{
3552 if ( !d->geometry )
3553 return false;
3554
3555 QgsGeos geos( d->geometry.get() );
3556 mLastError.clear();
3557 return geos.isSimple( &mLastError );
3558}
3559
3560bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3561{
3562 if ( !d->geometry )
3563 return false;
3564
3565 QgsInternalGeometryEngine engine( *this );
3566 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3567}
3568
3570{
3571 if ( !d->geometry || !g.d->geometry )
3572 {
3573 return false;
3574 }
3575
3576 // fast check - are they shared copies of the same underlying geometry?
3577 if ( d == g.d )
3578 return true;
3579
3580 // fast check - distinct geometry types?
3581 if ( type() != g.type() )
3582 return false;
3583
3584 // avoid calling geos for trivial point case
3585 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point
3586 && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3587 {
3588 return equals( g );
3589 }
3590
3591 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3592 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3593 return false;
3594
3595 QgsGeos geos( d->geometry.get() );
3596 mLastError.clear();
3597 return geos.isEqual( g.d->geometry.get(), &mLastError );
3598}
3599
3600QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters )
3601{
3602 QgsGeos geos( nullptr );
3603
3604 QString error;
3605 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters ) );
3606 QgsGeometry result( std::move( geom ) );
3607 result.mLastError = error;
3608 return result;
3609}
3610
3611QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
3612{
3613 QVector<const QgsAbstractGeometry *> geomV2List;
3614 for ( const QgsGeometry &g : geometryList )
3615 {
3616 if ( !( g.isNull() ) )
3617 {
3618 geomV2List.append( g.constGet() );
3619 }
3620 }
3621
3622 QString error;
3623 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
3624 result.mLastError = error;
3625 return result;
3626}
3627
3629{
3631 {
3632 return;
3633 }
3634
3635 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3636 reset( std::move( straightGeom ) );
3637}
3638
3640{
3641 if ( !d->geometry )
3642 {
3643 return false;
3644 }
3645
3646 return d->geometry->hasCurvedSegments();
3647}
3648
3650{
3651 if ( !d->geometry )
3652 {
3654 }
3655
3656 detach();
3657 d->geometry->transform( ct, direction, transformZ );
3659}
3660
3661Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3662{
3663 if ( !d->geometry )
3664 {
3666 }
3667
3668 detach();
3669 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3671}
3672
3674{
3675 if ( d->geometry )
3676 {
3677 detach();
3678 d->geometry->transform( mtp.transform() );
3679 }
3680}
3681
3683{
3684 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
3685 {
3686 return QgsGeometry();
3687 }
3688
3689 QgsGeos geos( d->geometry.get() );
3690 mLastError.clear();
3691 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError );
3692 if ( !resultGeom )
3693 {
3694 QgsGeometry result;
3695 result.mLastError = mLastError;
3696 return result;
3697 }
3698 return QgsGeometry( std::move( resultGeom ) );
3699}
3700
3701void QgsGeometry::draw( QPainter &p ) const
3702{
3703 if ( d->geometry )
3704 {
3705 d->geometry->draw( p );
3706 }
3707}
3708
3709static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
3710{
3711 if ( vertexIndex < 0 )
3712 return false; // clearly something wrong
3713
3714 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3715 {
3716 partIndex = 0;
3717 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3718 {
3719 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3720
3721 // count total number of vertices in the part
3722 int numPoints = 0;
3723 for ( int k = 0; k < part->ringCount(); ++k )
3724 numPoints += part->vertexCount( 0, k );
3725
3726 if ( vertexIndex < numPoints )
3727 {
3728 int nothing;
3729 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3730 }
3731 vertexIndex -= numPoints;
3732 partIndex++;
3733 }
3734 }
3735 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3736 {
3737 const QgsCurve *ring = curvePolygon->exteriorRing();
3738 if ( vertexIndex < ring->numPoints() )
3739 {
3740 partIndex = 0;
3741 ringIndex = 0;
3742 vertex = vertexIndex;
3743 return true;
3744 }
3745 vertexIndex -= ring->numPoints();
3746 ringIndex = 1;
3747 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
3748 {
3749 const QgsCurve *ring = curvePolygon->interiorRing( i );
3750 if ( vertexIndex < ring->numPoints() )
3751 {
3752 partIndex = 0;
3753 vertex = vertexIndex;
3754 return true;
3755 }
3756 vertexIndex -= ring->numPoints();
3757 ringIndex += 1;
3758 }
3759 }
3760 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3761 {
3762 if ( vertexIndex < curve->numPoints() )
3763 {
3764 partIndex = 0;
3765 ringIndex = 0;
3766 vertex = vertexIndex;
3767 return true;
3768 }
3769 }
3770 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
3771 {
3772 if ( vertexIndex == 0 )
3773 {
3774 partIndex = 0;
3775 ringIndex = 0;
3776 vertex = 0;
3777 return true;
3778 }
3779 }
3780
3781 return false;
3782}
3783
3785{
3786 if ( !d->geometry )
3787 {
3788 return false;
3789 }
3790
3791 id.type = Qgis::VertexType::Segment;
3792
3793 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
3794 if ( !res )
3795 return false;
3796
3797 // now let's find out if it is a straight or circular segment
3798 const QgsAbstractGeometry *g = d->geometry.get();
3799 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3800 {
3801 g = geomCollection->geometryN( id.part );
3802 }
3803
3804 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3805 {
3806 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
3807 }
3808
3809 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3810 {
3811 QgsPoint p;
3812 res = curve->pointAt( id.vertex, p, id.type );
3813 if ( !res )
3814 return false;
3815 }
3816
3817 return true;
3818}
3819
3821{
3822 if ( !d->geometry )
3823 {
3824 return -1;
3825 }
3826 return d->geometry->vertexNumberFromVertexId( id );
3827}
3828
3830{
3831 return mLastError;
3832}
3833
3834void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
3835{
3836 if ( !d->geometry )
3837 return;
3838
3839 detach();
3840
3841 d->geometry->filterVertices( filter );
3842}
3843
3844void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
3845{
3846 if ( !d->geometry )
3847 return;
3848
3849 detach();
3850
3851 d->geometry->transformVertices( transform );
3852}
3853
3854void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
3855{
3856 output.clear();
3857 for ( const QgsPointXY &p : input )
3858 {
3859 output.append( QgsPoint( p ) );
3860 }
3861}
3862
3863void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
3864{
3865 output.clear();
3866 for ( const QgsPoint &p : input )
3867 {
3868 output.append( QgsPointXY( p.x(), p.y() ) );
3869 }
3870}
3871
3872void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
3873{
3874 output.clear();
3875
3876 auto convertRing = []( const QgsCurve * ring ) -> QgsPolylineXY
3877 {
3878 QgsPolylineXY res;
3879 bool doSegmentation = ( QgsWkbTypes::flatType( ring->wkbType() ) == Qgis::WkbType::CompoundCurve
3881 std::unique_ptr< QgsLineString > segmentizedLine;
3882 const QgsLineString *line = nullptr;
3883 if ( doSegmentation )
3884 {
3885 segmentizedLine.reset( ring->curveToLine() );
3886 line = segmentizedLine.get();
3887 }
3888 else
3889 {
3890 line = qgsgeometry_cast<const QgsLineString *>( ring );
3891 if ( !line )
3892 {
3893 return res;
3894 }
3895 }
3896
3897 int nVertices = line->numPoints();
3898 res.resize( nVertices );
3899 QgsPointXY *data = res.data();
3900 const double *xData = line->xData();
3901 const double *yData = line->yData();
3902 for ( int i = 0; i < nVertices; ++i )
3903 {
3904 data->setX( *xData++ );
3905 data->setY( *yData++ );
3906 data++;
3907 }
3908 return res;
3909 };
3910
3911 if ( const QgsCurve *exterior = input.exteriorRing() )
3912 {
3913 output.push_back( convertRing( exterior ) );
3914 }
3915
3916 const int interiorRingCount = input.numInteriorRings();
3917 output.reserve( output.size() + interiorRingCount );
3918 for ( int n = 0; n < interiorRingCount; ++n )
3919 {
3920 output.push_back( convertRing( input.interiorRing( n ) ) );
3921 }
3922}
3923
3925{
3926 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
3927}
3928
3929QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
3930{
3931 std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
3932
3933 if ( polygon.isClosed() )
3934 {
3935 auto poly = std::make_unique< QgsPolygon >();
3936 poly->setExteriorRing( ring.release() );
3937 return QgsGeometry( std::move( poly ) );
3938 }
3939 else
3940 {
3941 return QgsGeometry( std::move( ring ) );
3942 }
3943}
3944
3946{
3948 QgsPolygonXY result;
3949 result << createPolylineFromQPolygonF( polygon );
3950 return result;
3952}
3953
3955{
3956 QgsPolylineXY result;
3957 result.reserve( polygon.count() );
3958 for ( const QPointF &p : polygon )
3959 {
3960 result.append( QgsPointXY( p ) );
3961 }
3962 return result;
3963}
3964
3965bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
3966{
3967 if ( p1.count() != p2.count() )
3968 return false;
3969
3970 for ( int i = 0; i < p1.count(); ++i )
3971 {
3972 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
3973 return false;
3974 }
3975 return true;
3976}
3977
3978bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
3979{
3980 if ( p1.count() != p2.count() )
3981 return false;
3982
3983 for ( int i = 0; i < p1.count(); ++i )
3984 {
3985 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3986 return false;
3987 }
3988 return true;
3989}
3990
3991
3992bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
3993{
3994 if ( p1.count() != p2.count() )
3995 return false;
3996
3997 for ( int i = 0; i < p1.count(); ++i )
3998 {
3999 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4000 return false;
4001 }
4002 return true;
4003}
4004
4005QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4006{
4007 if ( !d->geometry || d->geometry->isEmpty() )
4008 return QgsGeometry();
4009
4010 QgsGeometry geom = *this;
4012 geom = QgsGeometry( d->geometry->segmentize() );
4013
4014 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
4015 {
4018 //can't smooth a point based geometry
4019 return geom;
4020
4022 {
4023 const QgsLineString *lineString = qgsgeometry_cast< const QgsLineString * >( geom.constGet() );
4024 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
4025 }
4026
4028 {
4029 const QgsMultiLineString *multiLine = qgsgeometry_cast< const QgsMultiLineString * >( geom.constGet() );
4030
4031 auto resultMultiline = std::make_unique< QgsMultiLineString> ();
4032 resultMultiline->reserve( multiLine->numGeometries() );
4033 for ( int i = 0; i < multiLine->numGeometries(); ++i )
4034 {
4035 resultMultiline->addGeometry( smoothLine( *( multiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4036 }
4037 return QgsGeometry( std::move( resultMultiline ) );
4038 }
4039
4041 {
4042 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( geom.constGet() );
4043 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
4044 }
4045
4047 {
4048 const QgsMultiPolygon *multiPoly = qgsgeometry_cast< const QgsMultiPolygon * >( geom.constGet() );
4049
4050 auto resultMultiPoly = std::make_unique< QgsMultiPolygon >();
4051 resultMultiPoly->reserve( multiPoly->numGeometries() );
4052 for ( int i = 0; i < multiPoly->numGeometries(); ++i )
4053 {
4054 resultMultiPoly->addGeometry( smoothPolygon( *( multiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4055 }
4056 return QgsGeometry( std::move( resultMultiPoly ) );
4057 }
4058
4060 default:
4061 return QgsGeometry( *this );
4062 }
4063}
4064
4065std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations,
4066 const double offset, double squareDistThreshold, double maxAngleRads,
4067 bool isRing )
4068{
4069 auto result = std::make_unique< QgsLineString >( line );
4070 QgsPointSequence outputLine;
4071 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
4072 {
4073 outputLine.resize( 0 );
4074 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
4075 bool skipFirst = false;
4076 bool skipLast = false;
4077 if ( isRing )
4078 {
4079 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
4080 QgsPoint p2 = result->pointN( 0 );
4081 QgsPoint p3 = result->pointN( 1 );
4082 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4083 p3.x(), p3.y() );
4084 angle = std::fabs( M_PI - angle );
4085 skipFirst = angle > maxAngleRads;
4086 }
4087 for ( int i = 0; i < result->numPoints() - 1; i++ )
4088 {
4089 QgsPoint p1 = result->pointN( i );
4090 QgsPoint p2 = result->pointN( i + 1 );
4091
4092 double angle = M_PI;
4093 if ( i == 0 && isRing )
4094 {
4095 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
4096 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4097 p3.x(), p3.y() );
4098 }
4099 else if ( i < result->numPoints() - 2 )
4100 {
4101 QgsPoint p3 = result->pointN( i + 2 );
4102 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4103 p3.x(), p3.y() );
4104 }
4105 else if ( i == result->numPoints() - 2 && isRing )
4106 {
4107 QgsPoint p3 = result->pointN( 1 );
4108 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
4109 p3.x(), p3.y() );
4110 }
4111
4112 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
4113
4114 // don't apply distance threshold to first or last segment
4115 if ( i == 0 || i >= result->numPoints() - 2
4116 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
4117 {
4118 if ( !isRing )
4119 {
4120 if ( !skipFirst )
4121 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
4122 if ( !skipLast )
4123 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
4124 else
4125 outputLine << p2;
4126 }
4127 else
4128 {
4129 // ring
4130 if ( !skipFirst )
4131 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
4132 else if ( i == 0 )
4133 outputLine << p1;
4134 if ( !skipLast )
4135 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
4136 else
4137 outputLine << p2;
4138 }
4139 }
4140 skipFirst = skipLast;
4141 }
4142
4143 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4144 outputLine << outputLine.at( 0 );
4145
4146 result->setPoints( outputLine );
4147 }
4148 return result;
4149}
4150
4151std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4152{
4153 double maxAngleRads = maxAngle * M_PI / 180.0;
4154 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4155 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4156}
4157
4158std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4159{
4160 double maxAngleRads = maxAngle * M_PI / 180.0;
4161 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4162 auto resultPoly = std::make_unique< QgsPolygon >();
4163
4164 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset,
4165 squareDistThreshold, maxAngleRads, true ).release() );
4166
4167 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4168 {
4169 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset,
4170 squareDistThreshold, maxAngleRads, true ).release() );
4171 }
4172 return resultPoly;
4173}
4174
4175QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4176{
4177 switch ( type() )
4178 {
4180 {
4181 bool srcIsMultipart = isMultipart();
4182
4183 if ( ( destMultipart && srcIsMultipart ) ||
4184 ( !destMultipart && !srcIsMultipart ) )
4185 {
4186 // return a copy of the same geom
4187 return QgsGeometry( *this );
4188 }
4189 if ( destMultipart )
4190 {
4191 // layer is multipart => make a multipoint with a single point
4192 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4193 }
4194 else
4195 {
4196 // destination is singlepart => make a single part if possible
4197 QgsMultiPointXY multiPoint = asMultiPoint();
4198 if ( multiPoint.count() == 1 )
4199 {
4200 return fromPointXY( multiPoint[0] );
4201 }
4202 }
4203 return QgsGeometry();
4204 }
4205
4207 {
4208 // only possible if destination is multipart
4209 if ( !destMultipart )
4210 return QgsGeometry();
4211
4212 // input geometry is multipart
4213 if ( isMultipart() )
4214 {
4215 const QgsMultiPolylineXY multiLine = asMultiPolyline();
4216 QgsMultiPointXY multiPoint;
4217 for ( const QgsPolylineXY &l : multiLine )
4218 for ( const QgsPointXY &p : l )
4219 multiPoint << p;
4220 return fromMultiPointXY( multiPoint );
4221 }
4222 // input geometry is not multipart: copy directly the line into a multipoint
4223 else
4224 {
4225 QgsPolylineXY line = asPolyline();
4226 if ( !line.isEmpty() )
4227 return fromMultiPointXY( line );
4228 }
4229 return QgsGeometry();
4230 }
4231
4233 {
4234 // can only transform if destination is multipoint
4235 if ( !destMultipart )
4236 return QgsGeometry();
4237
4238 // input geometry is multipart: make a multipoint from multipolygon
4239 if ( isMultipart() )
4240 {
4241 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4242 QgsMultiPointXY multiPoint;
4243 for ( const QgsPolygonXY &poly : multiPolygon )
4244 for ( const QgsPolylineXY &line : poly )
4245 for ( const QgsPointXY &pt : line )
4246 multiPoint << pt;
4247 return fromMultiPointXY( multiPoint );
4248 }
4249 // input geometry is not multipart: make a multipoint from polygon
4250 else
4251 {
4252 const QgsPolygonXY polygon = asPolygon();
4253 QgsMultiPointXY multiPoint;
4254 for ( const QgsPolylineXY &line : polygon )
4255 for ( const QgsPointXY &pt : line )
4256 multiPoint << pt;
4257 return fromMultiPointXY( multiPoint );
4258 }
4259 }
4260
4261 default:
4262 return QgsGeometry();
4263 }
4264}
4265
4266QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4267{
4268 switch ( type() )
4269 {
4271 {
4272 if ( !isMultipart() )
4273 return QgsGeometry();
4274
4275 QgsMultiPointXY multiPoint = asMultiPoint();
4276 if ( multiPoint.count() < 2 )
4277 return QgsGeometry();
4278
4279 if ( destMultipart )
4280 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4281 else
4282 return fromPolylineXY( multiPoint );
4283 }
4284
4286 {
4287 bool srcIsMultipart = isMultipart();
4288
4289 if ( ( destMultipart && srcIsMultipart ) ||
4290 ( !destMultipart && ! srcIsMultipart ) )
4291 {
4292 // return a copy of the same geom
4293 return QgsGeometry( *this );
4294 }
4295 if ( destMultipart )
4296 {
4297 // destination is multipart => makes a multipoint with a single line
4298 QgsPolylineXY line = asPolyline();
4299 if ( !line.isEmpty() )
4300 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4301 }
4302 else
4303 {
4304 // destination is singlepart => make a single part if possible
4305 QgsMultiPolylineXY multiLine = asMultiPolyline();
4306 if ( multiLine.count() == 1 )
4307 return fromPolylineXY( multiLine[0] );
4308 }
4309 return QgsGeometry();
4310 }
4311
4313 {
4314 // input geometry is multipolygon
4315 if ( isMultipart() )
4316 {
4317 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4318 QgsMultiPolylineXY multiLine;
4319 for ( const QgsPolygonXY &poly : multiPolygon )
4320 for ( const QgsPolylineXY &line : poly )
4321 multiLine << line;
4322
4323 if ( destMultipart )
4324 {
4325 // destination is multipart
4326 return fromMultiPolylineXY( multiLine );
4327 }
4328 else if ( multiLine.count() == 1 )
4329 {
4330 // destination is singlepart => make a single part if possible
4331 return fromPolylineXY( multiLine[0] );
4332 }
4333 }
4334 // input geometry is single polygon
4335 else
4336 {
4337 QgsPolygonXY polygon = asPolygon();
4338 // if polygon has rings
4339 if ( polygon.count() > 1 )
4340 {
4341 // cannot fit a polygon with rings in a single line layer
4342 // TODO: would it be better to remove rings?
4343 if ( destMultipart )
4344 {
4345 const QgsPolygonXY polygon = asPolygon();
4346 QgsMultiPolylineXY multiLine;
4347 multiLine.reserve( polygon.count() );
4348 for ( const QgsPolylineXY &line : polygon )
4349 multiLine << line;
4350 return fromMultiPolylineXY( multiLine );
4351 }
4352 }
4353 // no rings
4354 else if ( polygon.count() == 1 )
4355 {
4356 if ( destMultipart )
4357 {
4358 return fromMultiPolylineXY( polygon );
4359 }
4360 else
4361 {
4362 return fromPolylineXY( polygon[0] );
4363 }
4364 }
4365 }
4366 return QgsGeometry();
4367 }
4368
4369 default:
4370 return QgsGeometry();
4371 }
4372}
4373
4374QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4375{
4376 switch ( type() )
4377 {
4379 {
4380 if ( !isMultipart() )
4381 return QgsGeometry();
4382
4383 QgsMultiPointXY multiPoint = asMultiPoint();
4384 if ( multiPoint.count() < 3 )
4385 return QgsGeometry();
4386
4387 if ( multiPoint.last() != multiPoint.first() )
4388 multiPoint << multiPoint.first();
4389
4390 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4391 if ( destMultipart )
4392 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4393 else
4394 return fromPolygonXY( polygon );
4395 }
4396
4398 {
4399 // input geometry is multiline
4400 if ( isMultipart() )
4401 {
4402 QgsMultiPolylineXY multiLine = asMultiPolyline();
4403 QgsMultiPolygonXY multiPolygon;
4404 for ( QgsMultiPolylineXY::iterator multiLineIt = multiLine.begin(); multiLineIt != multiLine.end(); ++multiLineIt )
4405 {
4406 // do not create polygon for a 1 segment line
4407 if ( ( *multiLineIt ).count() < 3 )
4408 return QgsGeometry();
4409 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4410 return QgsGeometry();
4411
4412 // add closing node
4413 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4414 *multiLineIt << ( *multiLineIt ).first();
4415 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4416 }
4417 // check that polygons were inserted
4418 if ( !multiPolygon.isEmpty() )
4419 {
4420 if ( destMultipart )
4421 {
4422 return fromMultiPolygonXY( multiPolygon );
4423 }
4424 else if ( multiPolygon.count() == 1 )
4425 {
4426 // destination is singlepart => make a single part if possible
4427 return fromPolygonXY( multiPolygon[0] );
4428 }
4429 }
4430 }
4431 // input geometry is single line
4432 else
4433 {
4434 QgsPolylineXY line = asPolyline();
4435
4436 // do not create polygon for a 1 segment line
4437 if ( line.count() < 3 )
4438 return QgsGeometry();
4439 if ( line.count() == 3 && line.first() == line.last() )
4440 return QgsGeometry();
4441
4442 // add closing node
4443 if ( line.first() != line.last() )
4444 line << line.first();
4445
4446 // destination is multipart
4447 if ( destMultipart )
4448 {
4449 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4450 }
4451 else
4452 {
4453 return fromPolygonXY( QgsPolygonXY() << line );
4454 }
4455 }
4456 return QgsGeometry();
4457 }
4458
4460 {
4461 bool srcIsMultipart = isMultipart();
4462
4463 if ( ( destMultipart && srcIsMultipart ) ||
4464 ( !destMultipart && ! srcIsMultipart ) )
4465 {
4466 // return a copy of the same geom
4467 return QgsGeometry( *this );
4468 }
4469 if ( destMultipart )
4470 {
4471 // destination is multipart => makes a multipoint with a single polygon
4472 QgsPolygonXY polygon = asPolygon();
4473 if ( !polygon.isEmpty() )
4474 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4475 }
4476 else
4477 {
4478 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4479 if ( multiPolygon.count() == 1 )
4480 {
4481 // destination is singlepart => make a single part if possible
4482 return fromPolygonXY( multiPolygon[0] );
4483 }
4484 }
4485 return QgsGeometry();
4486 }
4487
4488 default:
4489 return QgsGeometry();
4490 }
4491}
4492
4494{
4495 return new QgsGeos( geometry, precision, flags );
4496}
4497
4498QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
4499{
4500 out << geometry.asWkb();
4501 return out;
4502}
4503
4504QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4505{
4506 QByteArray byteArray;
4507 in >> byteArray;
4508 if ( byteArray.isEmpty() )
4509 {
4510 geometry.set( nullptr );
4511 return in;
4512 }
4513
4514 geometry.fromWkb( byteArray );
4515 return in;
4516}
4517
4518
4520{
4521 return mMessage;
4522}
4523
4525{
4526 return mLocation;
4527}
4528
4530{
4531 return mHasLocation;
4532}
@ 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:3220
AngularDirection
Angular directions.
Definition qgis.h:3335
@ 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:3205
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:2621
The part_iterator class provides an STL-style iterator for const references to geometry parts.
The part_iterator class provides an STL-style iterator for geometry parts.
The vertex_iterator class provides an STL-style iterator for vertices.
Abstract base class for all geometries.
virtual int ringCount(int part=0) const =0
Returns the number of rings of which this geometry is built.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual 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
Handles coordinate transforms between two coordinate systems.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
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.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=Qgis::DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
bool deleteRing(int ringNum, int partNum=0)
Deletes a ring in polygon or multipolygon.
QVector< QgsPointXY > randomPointsInPolygon(int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr, int maxTriesPerPoint=0) const
Returns a list of count random points generated inside a (multi)polygon geometry (if acceptPoint is s...
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
QgsGeometry clipped(const QgsRectangle &rectangle)
Clips the geometry using the specified rectangle.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
int makeDifferenceInPlace(const QgsGeometry &other)
Changes this geometry such that it does not intersect the other geometry.
void adjacentVertices(int atVertex, int &beforeVertex, int &afterVertex) const
Returns the indexes of the vertices before and after the given vertex index.
QgsMultiPolygonXY asMultiPolygon() const
Returns the contents of the geometry as a multi-polygon.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
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.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
double frechetDistance(const QgsGeometry &geom) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry convertToType(Qgis::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
void normalize()
Reorganizes the geometry into a normalized form (or "canonical" form).
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Returns the length of the QByteArray returned by asWkb()
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static Q_DECL_DEPRECATED QgsPolylineXY createPolylineFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolylineXY from a QPolygonF.
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
QgsBox3D boundingBox3D() const
Returns the 3D bounding box of the geometry.
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.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
QVector< QgsGeometry > coerceToType(Qgis::WkbType type, double defaultZ=0, double defaultM=0, bool avoidDuplicates=true) const
Attempts to coerce this geometry into the specified destination type.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsGeometry node() const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false)
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
bool convertGeometryCollectionToSubclass(Qgis::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
QgsAbstractGeometry::part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QString asJson(int precision=17) const
Exports the geometry to a GeoJSON string.
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
double frechetDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
QgsGeometry unionCoverage() const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
bool convertToCurvedMultiType()
Converts a geometry into a multitype geometry of curve kind (when there is a corresponding curve type...
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
QgsGeometry orientedMinimumBoundingBox() const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometryConstPartIterator constParts() const
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult addPartV2(const QVector< QgsPointXY > &points, Qgis::WkbType wkbType=Qgis::WkbType::Unknown)
Adds a new part to a the geometry.
double minimumClearance() const
Computes the minimum clearance of a geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
Qgis::GeometryOperationResult reshapeGeometry(const QgsLineString &reshapeLineString)
Replaces a part of this geometry with another line.
double closestVertexWithContext(const QgsPointXY &point, int &atVertex) const
Searches for the closest vertex in this geometry to the given point.
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false) const
Returns the Delaunay triangulation for the vertices of the geometry.
void draw(QPainter &p) const
Draws the geometry onto a QPainter.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
bool toggleCircularAtVertex(int atVertex)
Converts the vertex at the given position from/to circular.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
bool moveVertex(double x, double y, int atVertex)
Moves the vertex at the given position number and item (first number is index 0) to the given coordin...
QgsGeometry constrainedDelaunayTriangulation() const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
static QgsGeometry fromBox3D(const QgsBox3D &box)
Creates a new geometry from a QgsBox3D object Returns a 2D polygon geometry if the box is purely 2d,...
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
static QgsGeometry createWedgeBufferFromAngles(const QgsPoint &center, double startAngle, double endAngle, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
bool deletePart(int partNum)
Deletes part identified by the part number.
QgsGeometry removeInteriorRings(double minimumAllowedArea=-1) const
Removes the interior rings from a (multi)polygon geometry.
static QgsGeometry fromPoint(const QgsPoint &point)
Creates a new geometry from a QgsPoint object.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
Q_DECL_DEPRECATED int avoidIntersections(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h: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:2087
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:3248
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
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.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
static std::unique_ptr< QgsLineString > fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
int numPoints() const override
Returns the number of points in the curve.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Perform transforms between map coordinates and device coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
QgsPoint * pointN(int index)
Returns the point with the specified index.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
Represents a 2D point.
Definition qgspointxy.h: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 dataset.
Java-style iterator for traversal of vertices of a geometry.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition qgswkbtypes.h:53
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Q_INVOKABLE bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static Qgis::WkbType curveType(Qgis::WkbType type)
Returns the curve type for a WKB type.
Contains geos related utilities and functions.
Definition qgsgeos.h: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:6945
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6944
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6392
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:91
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:97
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.
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition qgsgeometry.h:71
#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