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