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