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