QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
58 : d( new QgsGeometryPrivate() )
59{
60}
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.addPart( ( *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 );
950 return addPart( l, geomType );
951}
952
954{
955 std::unique_ptr< QgsAbstractGeometry > partGeom;
956 if ( points.size() == 1 )
957 {
958 partGeom = std::make_unique< QgsPoint >( points[0] );
959 }
960 else if ( points.size() > 1 )
961 {
962 std::unique_ptr< QgsLineString > ringLine = std::make_unique< QgsLineString >();
963 ringLine->setPoints( points );
964 partGeom = std::move( ringLine );
965 }
966 return addPart( partGeom.release(), geomType );
967}
968
970{
971 std::unique_ptr< QgsAbstractGeometry > p( part );
972 if ( !d->geometry )
973 {
974 switch ( geomType )
975 {
977 reset( std::make_unique< QgsMultiPoint >() );
978 break;
980 reset( std::make_unique< QgsMultiLineString >() );
981 break;
983 reset( std::make_unique< QgsMultiPolygon >() );
984 break;
985 default:
986 reset( nullptr );
988 }
989 }
990 else
991 {
992 detach();
993 }
994
996 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
997}
998
1000{
1001 if ( !d->geometry )
1002 {
1004 }
1005 if ( newPart.isNull() || !newPart.d->geometry )
1006 {
1008 }
1009
1010 return addPart( newPart.d->geometry->clone() );
1011}
1012
1013QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
1014{
1015 if ( !d->geometry || type() != Qgis::GeometryType::Polygon )
1016 {
1017 return QgsGeometry();
1018 }
1019
1020 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
1021 {
1022 const QVector<QgsGeometry> parts = asGeometryCollection();
1023 QVector<QgsGeometry> results;
1024 results.reserve( parts.count() );
1025 for ( const QgsGeometry &part : parts )
1026 {
1027 QgsGeometry result = part.removeInteriorRings( minimumRingArea );
1028 if ( !result.isNull() )
1029 results << result;
1030 }
1031 if ( results.isEmpty() )
1032 return QgsGeometry();
1033
1034 QgsGeometry first = results.takeAt( 0 );
1035 for ( const QgsGeometry &result : std::as_const( results ) )
1036 {
1037 first.addPart( result );
1038 }
1039 return first;
1040 }
1041 else
1042 {
1043 std::unique_ptr< QgsCurvePolygon > newPoly( static_cast< QgsCurvePolygon * >( d->geometry->clone() ) );
1044 newPoly->removeInteriorRings( minimumRingArea );
1045 return QgsGeometry( std::move( newPoly ) );
1046 }
1047}
1048
1049Qgis::GeometryOperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
1050{
1051 if ( !d->geometry )
1052 {
1054 }
1055
1056 detach();
1057
1058 d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
1060}
1061
1063{
1064 if ( !d->geometry )
1065 {
1067 }
1068
1069 detach();
1070
1071 QTransform t = QTransform::fromTranslate( center.x(), center.y() );
1072 t.rotate( -rotation );
1073 t.translate( -center.x(), -center.y() );
1074 d->geometry->transform( t );
1076}
1077
1078Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature )
1079{
1080 QgsPointSequence split, topology;
1081 convertPointList( splitLine, split );
1082 convertPointList( topologyTestPoints, topology );
1083 Qgis::GeometryOperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
1084 convertPointList( topology, topologyTestPoints );
1085 return result;
1086}
1087Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest )
1088{
1089 if ( !d->geometry )
1090 {
1092 }
1093
1094 // We're trying adding the split line's vertices to the geometry so that
1095 // snap to segment always produces a valid split (see https://github.com/qgis/QGIS/issues/29270)
1096 QgsGeometry tmpGeom( *this );
1097 for ( const QgsPoint &v : splitLine )
1098 {
1099 tmpGeom.addTopologicalPoint( v );
1100 }
1101
1102 QVector<QgsGeometry > newGeoms;
1103 QgsLineString splitLineString( splitLine );
1104
1105 QgsGeos geos( tmpGeom.get() );
1106 mLastError.clear();
1107 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
1108
1109 if ( result == QgsGeometryEngine::Success )
1110 {
1111 if ( splitFeature )
1112 *this = newGeoms.takeAt( 0 );
1113 newGeometries = newGeoms;
1114 }
1115
1116 switch ( result )
1117 {
1132 //default: do not implement default to handle properly all cases
1133 }
1134
1135 // this should never be reached
1136 Q_ASSERT( false );
1138}
1139
1140Qgis::GeometryOperationResult QgsGeometry::splitGeometry( const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature )
1141{
1142 std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
1143 QgsPointSequence points;
1144 segmentizedLine->points( points );
1145 Qgis::GeometryOperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
1146
1148 {
1149 if ( preserveCircular )
1150 {
1151 for ( int i = 0; i < newGeometries.count(); ++i )
1152 newGeometries[i] = newGeometries[i].convertToCurves();
1153 *this = convertToCurves();
1154 }
1155 }
1156
1157 return result;
1158}
1159
1161{
1162 if ( !d->geometry )
1163 {
1165 }
1166
1167 QgsGeos geos( d->geometry.get() );
1169 mLastError.clear();
1170 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1171 if ( errorCode == QgsGeometryEngine::Success && geom )
1172 {
1173 reset( std::move( geom ) );
1175 }
1176
1177 switch ( errorCode )
1178 {
1189 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1193 }
1194
1195 // should not be reached
1197}
1198
1200{
1201 if ( !d->geometry || !other.d->geometry )
1202 {
1203 return 0;
1204 }
1205
1206 QgsGeos geos( d->geometry.get() );
1207
1208 mLastError.clear();
1209 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1210 if ( !diffGeom )
1211 {
1212 return 1;
1213 }
1214
1215 reset( std::move( diffGeom ) );
1216 return 0;
1217}
1218
1220{
1221 if ( !d->geometry || other.isNull() )
1222 {
1223 return QgsGeometry();
1224 }
1225
1226 QgsGeos geos( d->geometry.get() );
1227
1228 mLastError.clear();
1229 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError ) );
1230 if ( !diffGeom )
1231 {
1232 QgsGeometry result;
1233 result.mLastError = mLastError;
1234 return result;
1235 }
1236
1237 return QgsGeometry( diffGeom.release() );
1238}
1239
1241{
1242 if ( d->geometry )
1243 {
1244 return d->geometry->boundingBox();
1245 }
1246 return QgsRectangle();
1247}
1248
1250{
1251 if ( d->geometry )
1252 {
1253 return d->geometry->boundingBox3D();
1254 }
1255 return QgsBox3D();
1256}
1257
1258
1259QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1260{
1261 mLastError.clear();
1262 QgsInternalGeometryEngine engine( *this );
1263 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1264 if ( res.isNull() )
1265 mLastError = engine.lastError();
1266 return res;
1267}
1268
1270{
1271 double area, angle, width, height;
1272 return orientedMinimumBoundingBox( area, angle, width, height );
1273}
1274
1275static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1276{
1277 auto l_boundary = boundary.length();
1278 QgsCircle circ_mec;
1279 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1280 {
1281 switch ( l_boundary )
1282 {
1283 case 0:
1284 circ_mec = QgsCircle();
1285 break;
1286 case 1:
1287 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1288 boundary.pop_back();
1289 break;
1290 case 2:
1291 {
1292 QgsPointXY p1 = boundary.last();
1293 boundary.pop_back();
1294 QgsPointXY p2 = boundary.last();
1295 boundary.pop_back();
1296 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1297 }
1298 break;
1299 default:
1300 QgsPoint p1( boundary.at( 0 ) );
1301 QgsPoint p2( boundary.at( 1 ) );
1302 QgsPoint p3( boundary.at( 2 ) );
1303 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1304 break;
1305 }
1306 return circ_mec;
1307 }
1308 else
1309 {
1310 QgsPointXY pxy = points.last();
1311 points.pop_back();
1312 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1313 QgsPoint p( pxy );
1314 if ( !circ_mec.contains( p ) )
1315 {
1316 boundary.append( pxy );
1317 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1318 }
1319 }
1320 return circ_mec;
1321}
1322
1323QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1324{
1325 center = QgsPointXY();
1326 radius = 0;
1327
1328 if ( isEmpty() )
1329 {
1330 return QgsGeometry();
1331 }
1332
1333 /* optimization */
1334 QgsGeometry hull = convexHull();
1335 if ( hull.isNull() )
1336 return QgsGeometry();
1337
1338 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1340
1341 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1342 center = QgsPointXY( circ.center() );
1343 radius = circ.radius();
1344 QgsGeometry geom;
1345 geom.set( circ.toPolygon( segments ) );
1346 return geom;
1347
1348}
1349
1351{
1352 QgsPointXY center;
1353 double radius;
1354 return minimalEnclosingCircle( center, radius, segments );
1355
1356}
1357
1358QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1359{
1360 QgsInternalGeometryEngine engine( *this );
1361
1362 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1363}
1364
1365QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1366{
1367 QgsInternalGeometryEngine engine( *this );
1368 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1369}
1370
1371QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1372{
1373 QgsInternalGeometryEngine engine( *this );
1374 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1375}
1376
1377QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1378{
1379 QgsInternalGeometryEngine engine( *this );
1380 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1381}
1382
1383QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1384{
1385 QgsInternalGeometryEngine engine( *this );
1386 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1387}
1388
1389QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1390{
1391 QgsInternalGeometryEngine engine( *this );
1392 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1393}
1394
1395QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1396{
1397 QgsInternalGeometryEngine engine( *this );
1398 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1399}
1400
1401QgsGeometry QgsGeometry::applyDashPattern( const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset ) const
1402{
1403 QgsInternalGeometryEngine engine( *this );
1404 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1405}
1406
1407QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1408{
1409 if ( !d->geometry )
1410 {
1411 return QgsGeometry();
1412 }
1413 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1414}
1415
1416bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1417{
1418 if ( !d->geometry )
1419 return false;
1420
1421 detach();
1422 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1423}
1424
1426{
1427 // fast case, check bounding boxes
1428 if ( !boundingBoxIntersects( r ) )
1429 return false;
1430
1431 const Qgis::WkbType flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1432 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1433 if ( flatType == Qgis::WkbType::Point )
1434 {
1435 return true;
1436 }
1437
1438 // Workaround for issue issue GH #51429
1439 // in case of multi polygon, intersection with an empty rect fails
1440 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1441 {
1442 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1443 return contains( QgsGeometry::fromPointXY( center ) );
1444 }
1445
1446 QgsGeometry g = fromRect( r );
1447 return intersects( g );
1448}
1449
1450bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1451{
1452 if ( !d->geometry || geometry.isNull() )
1453 {
1454 return false;
1455 }
1456
1457 QgsGeos geos( d->geometry.get() );
1458 mLastError.clear();
1459 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1460}
1461
1463{
1464 if ( !d->geometry )
1465 {
1466 return false;
1467 }
1468
1469 return d->geometry->boundingBoxIntersects( rectangle );
1470}
1471
1473{
1474 if ( !d->geometry || geometry.isNull() )
1475 {
1476 return false;
1477 }
1478
1479 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1480}
1481
1482bool QgsGeometry::contains( const QgsPointXY *p ) const
1483{
1484 if ( !d->geometry || !p )
1485 {
1486 return false;
1487 }
1488
1489 QgsPoint pt( p->x(), p->y() );
1490 QgsGeos geos( d->geometry.get() );
1491 mLastError.clear();
1492 return geos.contains( &pt, &mLastError );
1493}
1494
1495bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1496{
1497 if ( !d->geometry || geometry.isNull() )
1498 {
1499 return false;
1500 }
1501
1502 QgsGeos geos( d->geometry.get() );
1503 mLastError.clear();
1504 return geos.contains( geometry.d->geometry.get(), &mLastError );
1505}
1506
1507bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1508{
1509 if ( !d->geometry || geometry.isNull() )
1510 {
1511 return false;
1512 }
1513
1514 QgsGeos geos( d->geometry.get() );
1515 mLastError.clear();
1516 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1517}
1518
1519bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1520{
1521 if ( !d->geometry || geometry.isNull() )
1522 {
1523 return false;
1524 }
1525
1526 // fast check - are they shared copies of the same underlying geometry?
1527 if ( d == geometry.d )
1528 return true;
1529
1530 // fast check - distinct geometry types?
1531 if ( type() != geometry.type() )
1532 return false;
1533
1534 // slower check - actually test the geometries
1535 return *d->geometry == *geometry.d->geometry;
1536}
1537
1538bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1539{
1540 if ( !d->geometry || geometry.isNull() )
1541 {
1542 return false;
1543 }
1544
1545 QgsGeos geos( d->geometry.get() );
1546 mLastError.clear();
1547 return geos.touches( geometry.d->geometry.get(), &mLastError );
1548}
1549
1550bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1551{
1552 if ( !d->geometry || geometry.isNull() )
1553 {
1554 return false;
1555 }
1556
1557 QgsGeos geos( d->geometry.get() );
1558 mLastError.clear();
1559 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1560}
1561
1562bool QgsGeometry::within( const QgsGeometry &geometry ) const
1563{
1564 if ( !d->geometry || geometry.isNull() )
1565 {
1566 return false;
1567 }
1568
1569 QgsGeos geos( d->geometry.get() );
1570 mLastError.clear();
1571 return geos.within( geometry.d->geometry.get(), &mLastError );
1572}
1573
1574bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1575{
1576 if ( !d->geometry || geometry.isNull() )
1577 {
1578 return false;
1579 }
1580
1581 QgsGeos geos( d->geometry.get() );
1582 mLastError.clear();
1583 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1584}
1585
1586QString QgsGeometry::asWkt( int precision ) const
1587{
1588 if ( !d->geometry )
1589 {
1590 return QString();
1591 }
1592 return d->geometry->asWkt( precision );
1593}
1594
1595QString QgsGeometry::asJson( int precision ) const
1596{
1597 return QString::fromStdString( asJsonObject( precision ).dump() );
1598}
1599
1601{
1602 if ( !d->geometry )
1603 {
1604 return nullptr;
1605 }
1606 return d->geometry->asJsonObject( precision );
1607
1608}
1609
1610QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM ) const
1611{
1612 QVector< QgsGeometry > res;
1613 if ( isNull() )
1614 return res;
1615
1616 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1617 {
1618 res << *this;
1619 return res;
1620 }
1621
1623 {
1624 return res;
1625 }
1626
1627 QgsGeometry newGeom = *this;
1628
1629 // Curved -> straight
1631 {
1632 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1633 }
1634
1635 // polygon -> line
1637 newGeom.type() == Qgis::GeometryType::Polygon )
1638 {
1639 // boundary gives us a (multi)line string of exterior + interior rings
1640 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1641 }
1642 // line -> polygon
1644 newGeom.type() == Qgis::GeometryType::Line )
1645 {
1646 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1647 const QgsGeometry source = newGeom;
1648 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1649 {
1650 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1651 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1652 {
1654 {
1655 std::unique_ptr< QgsCurvePolygon > cp = std::make_unique< QgsCurvePolygon >();
1656 cp->setExteriorRing( curve );
1657 exterior.release();
1658 gc->addGeometry( cp.release() );
1659 }
1660 else
1661 {
1662 std::unique_ptr< QgsPolygon > p = std::make_unique< QgsPolygon >();
1663 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1664 exterior.release();
1665 gc->addGeometry( p.release() );
1666 }
1667 }
1668 }
1669 newGeom = QgsGeometry( std::move( gc ) );
1670 }
1671
1672 // line/polygon -> points
1674 ( newGeom.type() == Qgis::GeometryType::Line ||
1675 newGeom.type() == Qgis::GeometryType::Polygon ) )
1676 {
1677 // lines/polygons to a point layer, extract all vertices
1678 std::unique_ptr< QgsMultiPoint > mp = std::make_unique< QgsMultiPoint >();
1679 const QgsGeometry source = newGeom;
1680 QSet< QgsPoint > added;
1681 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1682 {
1683 if ( added.contains( *vertex ) )
1684 continue; // avoid duplicate points, e.g. start/end of rings
1685 mp->addGeometry( ( *vertex ).clone() );
1686 added.insert( *vertex );
1687 }
1688 newGeom = QgsGeometry( std::move( mp ) );
1689 }
1690
1691 // Single -> multi
1692 if ( QgsWkbTypes::isMultiType( type ) && ! newGeom.isMultipart( ) )
1693 {
1694 newGeom.convertToMultiType();
1695 }
1696 // Drop Z/M
1697 if ( newGeom.constGet()->is3D() && ! QgsWkbTypes::hasZ( type ) )
1698 {
1699 newGeom.get()->dropZValue();
1700 }
1701 if ( newGeom.constGet()->isMeasure() && ! QgsWkbTypes::hasM( type ) )
1702 {
1703 newGeom.get()->dropMValue();
1704 }
1705 // Add Z/M back, set to 0
1706 if ( ! newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
1707 {
1708 newGeom.get()->addZValue( defaultZ );
1709 }
1710 if ( ! newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
1711 {
1712 newGeom.get()->addMValue( defaultM );
1713 }
1714
1715 // Straight -> curve
1717 {
1718 newGeom.convertToCurvedMultiType();
1719 }
1720
1721 // Multi -> single
1722 if ( ! QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart( ) )
1723 {
1724 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1725 res.reserve( parts->partCount() );
1726 for ( int i = 0; i < parts->partCount( ); i++ )
1727 {
1728 res << QgsGeometry( parts->geometryN( i )->clone() );
1729 }
1730 }
1731 else
1732 {
1733 res << newGeom;
1734 }
1735 return res;
1736}
1737
1738QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
1739{
1740 switch ( destType )
1741 {
1743 return convertToPoint( destMultipart );
1744
1746 return convertToLine( destMultipart );
1747
1749 return convertToPolygon( destMultipart );
1750
1751 default:
1752 return QgsGeometry();
1753 }
1754}
1755
1757{
1758 if ( !d->geometry )
1759 {
1760 return false;
1761 }
1762
1763 if ( isMultipart() ) //already multitype, no need to convert
1764 {
1765 return true;
1766 }
1767
1768 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
1769 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1770 if ( !multiGeom )
1771 {
1772 return false;
1773 }
1774
1775 //try to avoid cloning existing geometry whenever we can
1776
1777 //want to see a magic trick?... gather round kiddies...
1778 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
1779 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
1780 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
1781 multiGeom->addGeometry( d->geometry.release() );
1782 // and replace it with the multi geometry.
1783 // TADA! a clone free conversion in some cases
1784 d->geometry = std::move( geom );
1785 return true;
1786}
1787
1789{
1790 if ( !d->geometry )
1791 {
1792 return false;
1793 }
1794
1795 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
1796 {
1801 {
1802 return true;
1803 }
1804 default:
1805 break;
1806 }
1807
1808 std::unique_ptr< QgsAbstractGeometry >geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
1809 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() );
1810 if ( !multiGeom )
1811 {
1812 return false;
1813 }
1814
1815 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1816 if ( sourceMultiGeom )
1817 {
1818 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
1819 {
1820 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
1821 return false;
1822 }
1823 }
1824 else
1825 {
1826 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
1827 return false;
1828 }
1829
1830 reset( std::move( geom ) );
1831 return true;
1832}
1833
1835{
1836 if ( !d->geometry )
1837 {
1838 return false;
1839 }
1840
1841 if ( !isMultipart() ) //already single part, no need to convert
1842 {
1843 return true;
1844 }
1845
1846 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
1847 if ( !multiGeom || multiGeom->partCount() < 1 )
1848 return false;
1849
1850 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
1851 reset( std::move( firstPart ) );
1852 return true;
1853}
1854
1855
1857{
1858 const QgsGeometryCollection *origGeom = qgsgeometry_cast<const QgsGeometryCollection *>( constGet() );
1859 if ( !origGeom )
1860 return false;
1861
1862 std::unique_ptr<QgsGeometryCollection> resGeom;
1863 switch ( geomType )
1864 {
1866 resGeom = std::make_unique<QgsMultiPoint>();
1867 break;
1869 resGeom = std::make_unique<QgsMultiLineString>();
1870 break;
1872 resGeom = std::make_unique<QgsMultiPolygon>();
1873 break;
1874 default:
1875 break;
1876 }
1877 if ( !resGeom )
1878 return false;
1879
1880 resGeom->reserve( origGeom->numGeometries() );
1881 for ( int i = 0; i < origGeom->numGeometries(); ++i )
1882 {
1883 const QgsAbstractGeometry *g = origGeom->geometryN( i );
1884 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
1885 resGeom->addGeometry( g->clone() );
1886 }
1887
1888 set( resGeom.release() );
1889 return true;
1890}
1891
1892
1894{
1895 if ( !d->geometry )
1896 {
1897 return QgsPointXY();
1898 }
1899 if ( QgsPoint *pt = qgsgeometry_cast<QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
1900 {
1901 return QgsPointXY( pt->x(), pt->y() );
1902 }
1903 else
1904 {
1905 return QgsPointXY();
1906 }
1907}
1908
1910{
1911 QgsPolylineXY polyLine;
1912 if ( !d->geometry )
1913 {
1914 return polyLine;
1915 }
1916
1917 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve
1919 std::unique_ptr< QgsLineString > segmentizedLine;
1920 QgsLineString *line = nullptr;
1921 if ( doSegmentation )
1922 {
1923 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
1924 if ( !curve )
1925 {
1926 return polyLine;
1927 }
1928 segmentizedLine.reset( curve->curveToLine() );
1929 line = segmentizedLine.get();
1930 }
1931 else
1932 {
1933 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
1934 if ( !line )
1935 {
1936 return polyLine;
1937 }
1938 }
1939
1940 int nVertices = line->numPoints();
1941 polyLine.resize( nVertices );
1942 QgsPointXY *data = polyLine.data();
1943 const double *xData = line->xData();
1944 const double *yData = line->yData();
1945 for ( int i = 0; i < nVertices; ++i )
1946 {
1947 data->setX( *xData++ );
1948 data->setY( *yData++ );
1949 data++;
1950 }
1951
1952 return polyLine;
1953}
1954
1956{
1957 if ( !d->geometry )
1958 return QgsPolygonXY();
1959
1960 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
1961
1962 QgsPolygon *p = nullptr;
1963 std::unique_ptr< QgsPolygon > segmentized;
1964 if ( doSegmentation )
1965 {
1966 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
1967 if ( !curvePoly )
1968 {
1969 return QgsPolygonXY();
1970 }
1971 segmentized.reset( curvePoly->toPolygon() );
1972 p = segmentized.get();
1973 }
1974 else
1975 {
1976 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
1977 }
1978
1979 if ( !p )
1980 {
1981 return QgsPolygonXY();
1982 }
1983
1984 QgsPolygonXY polygon;
1985 convertPolygon( *p, polygon );
1986
1987 return polygon;
1988}
1989
1991{
1992 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
1993 {
1994 return QgsMultiPointXY();
1995 }
1996
1997 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
1998 if ( !mp )
1999 {
2000 return QgsMultiPointXY();
2001 }
2002
2003 int nPoints = mp->numGeometries();
2004 QgsMultiPointXY multiPoint( nPoints );
2005 for ( int i = 0; i < nPoints; ++i )
2006 {
2007 const QgsPoint *pt = mp->pointN( i );
2008 multiPoint[i].setX( pt->x() );
2009 multiPoint[i].setY( pt->y() );
2010 }
2011 return multiPoint;
2012}
2013
2015{
2016 if ( !d->geometry )
2017 {
2018 return QgsMultiPolylineXY();
2019 }
2020
2021 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2022 if ( !geomCollection )
2023 {
2024 return QgsMultiPolylineXY();
2025 }
2026
2027 int nLines = geomCollection->numGeometries();
2028 if ( nLines < 1 )
2029 {
2030 return QgsMultiPolylineXY();
2031 }
2032
2034 mpl.reserve( nLines );
2035 for ( int i = 0; i < nLines; ++i )
2036 {
2037 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2038 std::unique_ptr< QgsLineString > segmentized;
2039 if ( !line )
2040 {
2041 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2042 if ( !curve )
2043 {
2044 continue;
2045 }
2046 segmentized.reset( curve->curveToLine() );
2047 line = segmentized.get();
2048 }
2049
2050 QgsPolylineXY polyLine;
2051 int nVertices = line->numPoints();
2052 polyLine.resize( nVertices );
2053 QgsPointXY *data = polyLine.data();
2054 const double *xData = line->xData();
2055 const double *yData = line->yData();
2056 for ( int i = 0; i < nVertices; ++i )
2057 {
2058 data->setX( *xData++ );
2059 data->setY( *yData++ );
2060 data++;
2061 }
2062 mpl.append( polyLine );
2063 }
2064 return mpl;
2065}
2066
2068{
2069 if ( !d->geometry )
2070 {
2071 return QgsMultiPolygonXY();
2072 }
2073
2074 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2075 if ( !geomCollection )
2076 {
2077 return QgsMultiPolygonXY();
2078 }
2079
2080 const int nPolygons = geomCollection->numGeometries();
2081 if ( nPolygons < 1 )
2082 {
2083 return QgsMultiPolygonXY();
2084 }
2085
2087 mp.reserve( nPolygons );
2088 for ( int i = 0; i < nPolygons; ++i )
2089 {
2090 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2091 if ( !polygon )
2092 {
2093 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2094 if ( cPolygon )
2095 {
2096 polygon = cPolygon->toPolygon();
2097 }
2098 else
2099 {
2100 continue;
2101 }
2102 }
2103
2104 QgsPolygonXY poly;
2105 convertPolygon( *polygon, poly );
2106 mp.push_back( poly );
2107 }
2108 return mp;
2109}
2110
2111double QgsGeometry::area() const
2112{
2113 if ( !d->geometry )
2114 {
2115 return -1.0;
2116 }
2117
2118 return d->geometry->area();
2119}
2120
2122{
2123 if ( !d->geometry )
2124 {
2125 return -1.0;
2126 }
2127
2128 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2129 {
2131 return 0.0;
2132
2134 return d->geometry->length();
2135
2137 return d->geometry->perimeter();
2138
2141 return d->geometry->length();
2142 }
2143 return -1;
2144}
2145
2146double QgsGeometry::distance( const QgsGeometry &geom ) const
2147{
2148 if ( !d->geometry || !geom.d->geometry )
2149 {
2150 return -1.0;
2151 }
2152
2153 // avoid calling geos for trivial point-to-point distance calculations
2155 {
2156 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2157 }
2158
2159 QgsGeos g( d->geometry.get() );
2160 mLastError.clear();
2161 return g.distance( geom.d->geometry.get(), &mLastError );
2162}
2163
2165{
2166 if ( !d->geometry || !geom.d->geometry )
2167 {
2168 return -1.0;
2169 }
2170
2171 QgsGeos g( d->geometry.get() );
2172 mLastError.clear();
2173 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2174}
2175
2176double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2177{
2178 if ( !d->geometry || !geom.d->geometry )
2179 {
2180 return -1.0;
2181 }
2182
2183 QgsGeos g( d->geometry.get() );
2184 mLastError.clear();
2185 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2186}
2187
2188
2190{
2191 if ( !d->geometry || !geom.d->geometry )
2192 {
2193 return -1.0;
2194 }
2195
2196 QgsGeos g( d->geometry.get() );
2197 mLastError.clear();
2198 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
2199}
2200
2201double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2202{
2203 if ( !d->geometry || !geom.d->geometry )
2204 {
2205 return -1.0;
2206 }
2207
2208 QgsGeos g( d->geometry.get() );
2209 mLastError.clear();
2210 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2211}
2212
2214{
2215 if ( !d->geometry || d->geometry.get()->isEmpty() )
2217 return d->geometry->vertices_begin();
2218}
2219
2221{
2222 if ( !d->geometry || d->geometry.get()->isEmpty() )
2224 return d->geometry->vertices_end();
2225}
2226
2228{
2229 if ( !d->geometry || d->geometry.get()->isEmpty() )
2230 return QgsVertexIterator();
2231 return QgsVertexIterator( d->geometry.get() );
2232}
2233
2235{
2236 if ( !d->geometry )
2238
2239 detach();
2240 return d->geometry->parts_begin();
2241}
2242
2244{
2245 if ( !d->geometry )
2247 return d->geometry->parts_end();
2248}
2249
2251{
2252 if ( !d->geometry )
2254 return d->geometry->const_parts_begin();
2255}
2256
2258{
2259 if ( !d->geometry )
2261 return d->geometry->const_parts_end();
2262}
2263
2265{
2266 if ( !d->geometry )
2267 return QgsGeometryPartIterator();
2268
2269 detach();
2270 return QgsGeometryPartIterator( d->geometry.get() );
2271}
2272
2274{
2275 if ( !d->geometry )
2277
2278 return QgsGeometryConstPartIterator( d->geometry.get() );
2279}
2280
2281QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
2282{
2283 if ( !d->geometry )
2284 {
2285 return QgsGeometry();
2286 }
2287
2288 QgsGeos g( d->geometry.get() );
2289 mLastError.clear();
2290 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError ) );
2291 if ( !geom )
2292 {
2293 QgsGeometry result;
2294 result.mLastError = mLastError;
2295 return result;
2296 }
2297 return QgsGeometry( std::move( geom ) );
2298}
2299
2300QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit ) const
2301{
2302 if ( !d->geometry )
2303 {
2304 return QgsGeometry();
2305 }
2306
2307 QgsGeos g( d->geometry.get() );
2308 mLastError.clear();
2309 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError );
2310 if ( !geom )
2311 {
2312 QgsGeometry result;
2313 result.mLastError = mLastError;
2314 return result;
2315 }
2316 return QgsGeometry( geom );
2317}
2318
2319QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2320{
2321 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2322 {
2323 return QgsGeometry();
2324 }
2325
2326 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2327 {
2328 const QVector<QgsGeometry> parts = asGeometryCollection();
2329 QVector<QgsGeometry> results;
2330 results.reserve( parts.count() );
2331 for ( const QgsGeometry &part : parts )
2332 {
2333 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2334 if ( !result.isNull() )
2335 results << result;
2336 }
2337 if ( results.isEmpty() )
2338 return QgsGeometry();
2339
2340 QgsGeometry first = results.takeAt( 0 );
2341 for ( const QgsGeometry &result : std::as_const( results ) )
2342 {
2343 first.addPart( result );
2344 }
2345 return first;
2346 }
2347 else
2348 {
2349 QgsGeos geos( d->geometry.get() );
2350 mLastError.clear();
2351
2352 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2353 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2354
2355 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2356 if ( !offsetGeom )
2357 {
2358 QgsGeometry result;
2359 result.mLastError = mLastError;
2360 return result;
2361 }
2362
2363 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2364 {
2365 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2366 if ( newOrientation != prevOrientation )
2367 {
2368 // GEOS has flipped line orientation, flip it back
2369 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2370 offsetGeom = std::move( flipped );
2371 }
2372 }
2373 return QgsGeometry( std::move( offsetGeom ) );
2374 }
2375}
2376
2377QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2378{
2379 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2380 {
2381 return QgsGeometry();
2382 }
2383
2384 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2385 {
2386 const QVector<QgsGeometry> parts = asGeometryCollection();
2387 QVector<QgsGeometry> results;
2388 results.reserve( parts.count() );
2389 for ( const QgsGeometry &part : parts )
2390 {
2391 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2392 if ( !result.isNull() )
2393 results << result;
2394 }
2395 if ( results.isEmpty() )
2396 return QgsGeometry();
2397
2398 QgsGeometry first = results.takeAt( 0 );
2399 for ( const QgsGeometry &result : std::as_const( results ) )
2400 {
2401 first.addPart( result );
2402 }
2403 return first;
2404 }
2405 else
2406 {
2407 QgsGeos geos( d->geometry.get() );
2408 mLastError.clear();
2409 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side,
2410 joinStyle, miterLimit, &mLastError );
2411 if ( !bufferGeom )
2412 {
2413 QgsGeometry result;
2414 result.mLastError = mLastError;
2415 return result;
2416 }
2417 return QgsGeometry( std::move( bufferGeom ) );
2418 }
2419}
2420
2421QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2422{
2423 QgsInternalGeometryEngine engine( *this );
2424
2425 return engine.taperedBuffer( startWidth, endWidth, segments );
2426}
2427
2429{
2430 QgsInternalGeometryEngine engine( *this );
2431
2432 return engine.variableWidthBufferByM( segments );
2433}
2434
2435QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2436{
2437 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2438 {
2439 return QgsGeometry();
2440 }
2441
2442 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2443 {
2444 const QVector<QgsGeometry> parts = asGeometryCollection();
2445 QVector<QgsGeometry> results;
2446 results.reserve( parts.count() );
2447 for ( const QgsGeometry &part : parts )
2448 {
2449 QgsGeometry result = part.extendLine( startDistance, endDistance );
2450 if ( !result.isNull() )
2451 results << result;
2452 }
2453 if ( results.isEmpty() )
2454 return QgsGeometry();
2455
2456 QgsGeometry first = results.takeAt( 0 );
2457 for ( const QgsGeometry &result : std::as_const( results ) )
2458 {
2459 first.addPart( result );
2460 }
2461 return first;
2462 }
2463 else
2464 {
2465 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2466 if ( !line )
2467 return QgsGeometry();
2468
2469 std::unique_ptr< QgsLineString > newLine( line->clone() );
2470 newLine->extend( startDistance, endDistance );
2471 return QgsGeometry( std::move( newLine ) );
2472 }
2473}
2474
2475QgsGeometry QgsGeometry::simplify( double tolerance ) const
2476{
2477 if ( !d->geometry )
2478 {
2479 return QgsGeometry();
2480 }
2481
2482 QgsGeos geos( d->geometry.get() );
2483 mLastError.clear();
2484 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError ) );
2485 if ( !simplifiedGeom )
2486 {
2487 QgsGeometry result;
2488 result.mLastError = mLastError;
2489 return result;
2490 }
2491 return QgsGeometry( std::move( simplifiedGeom ) );
2492}
2493
2494QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2495{
2496 QgsInternalGeometryEngine engine( *this );
2497
2498 return engine.densifyByCount( extraNodesPerSegment );
2499}
2500
2502{
2503 QgsInternalGeometryEngine engine( *this );
2504
2505 return engine.densifyByDistance( distance );
2506}
2507
2508QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2509{
2510 QgsInternalGeometryEngine engine( *this );
2511
2512 return engine.convertToCurves( distanceTolerance, angleTolerance );
2513}
2514
2516{
2517 if ( !d->geometry )
2518 {
2519 return QgsGeometry();
2520 }
2521
2522 // avoid calling geos for trivial point centroids
2523 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
2524 {
2525 QgsGeometry c = *this;
2526 c.get()->dropZValue();
2527 c.get()->dropMValue();
2528 return c;
2529 }
2530
2531 QgsGeos geos( d->geometry.get() );
2532
2533 mLastError.clear();
2534 QgsGeometry result( geos.centroid( &mLastError ) );
2535 result.mLastError = mLastError;
2536 return result;
2537}
2538
2540{
2541 if ( !d->geometry )
2542 {
2543 return QgsGeometry();
2544 }
2545
2546 QgsGeos geos( d->geometry.get() );
2547
2548 mLastError.clear();
2549 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2550 result.mLastError = mLastError;
2551 return result;
2552}
2553
2554QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2555{
2556 QgsInternalGeometryEngine engine( *this );
2557
2558 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2559}
2560
2561QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2562{
2563 if ( !d->geometry )
2564 {
2565 return QgsGeometry();
2566 }
2567
2568 QgsGeos geos( d->geometry.get() );
2569
2570 mLastError.clear();
2571 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2572 result.mLastError = mLastError;
2573 return result;
2574}
2575
2577{
2578 if ( !d->geometry )
2579 {
2580 return QgsGeometry();
2581 }
2582
2583 QgsGeos geos( d->geometry.get() );
2584
2585 mLastError.clear();
2586 QgsGeometry result( geos.minimumWidth( &mLastError ) );
2587 result.mLastError = mLastError;
2588 return result;
2589}
2590
2592{
2593 if ( !d->geometry )
2594 {
2595 return std::numeric_limits< double >::quiet_NaN();
2596 }
2597
2598 QgsGeos geos( d->geometry.get() );
2599
2600 mLastError.clear();
2601 return geos.minimumClearance( &mLastError );
2602}
2603
2605{
2606 if ( !d->geometry )
2607 {
2608 return QgsGeometry();
2609 }
2610
2611 QgsGeos geos( d->geometry.get() );
2612
2613 mLastError.clear();
2614 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
2615 result.mLastError = mLastError;
2616 return result;
2617}
2618
2620{
2621 if ( !d->geometry )
2622 {
2623 return QgsGeometry();
2624 }
2625 QgsGeos geos( d->geometry.get() );
2626 mLastError.clear();
2627 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
2628 if ( !cHull )
2629 {
2630 QgsGeometry geom;
2631 geom.mLastError = mLastError;
2632 return geom;
2633 }
2634 return QgsGeometry( std::move( cHull ) );
2635}
2636
2637QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles ) const
2638{
2639 if ( !d->geometry )
2640 {
2641 return QgsGeometry();
2642 }
2643 QgsGeos geos( d->geometry.get() );
2644 mLastError.clear();
2645 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError ) );
2646 if ( !concaveHull )
2647 {
2648 QgsGeometry geom;
2649 geom.mLastError = mLastError;
2650 return geom;
2651 }
2652 return QgsGeometry( std::move( concaveHull ) );
2653}
2654
2655QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
2656{
2657 if ( !d->geometry )
2658 {
2659 return QgsGeometry();
2660 }
2661
2662 QgsGeos geos( d->geometry.get() );
2663 mLastError.clear();
2664 QgsGeometry result = geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError );
2665 result.mLastError = mLastError;
2666 return result;
2667}
2668
2669QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
2670{
2671 if ( !d->geometry )
2672 {
2673 return QgsGeometry();
2674 }
2675
2676 QgsGeos geos( d->geometry.get() );
2677 mLastError.clear();
2678 QgsGeometry result = geos.delaunayTriangulation( tolerance, edgesOnly );
2679 result.mLastError = mLastError;
2680 return result;
2681}
2682
2684{
2685 if ( !d->geometry )
2686 {
2687 return QgsGeometry();
2688 }
2689
2690 QgsGeos geos( d->geometry.get() );
2691 mLastError.clear();
2692 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
2693 result.mLastError = mLastError;
2694 return result;
2695}
2696
2698{
2699 if ( !d->geometry )
2700 {
2701 return QgsGeometry();
2702 }
2703
2707 return QgsGeometry();
2708
2709 QgsGeos geos( d->geometry.get() );
2710 mLastError.clear();
2711 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
2712 result.mLastError = mLastError;
2713 return result;
2714}
2715
2717{
2718 if ( !d->geometry )
2719 {
2721 }
2722
2723 QgsGeos geos( d->geometry.get() );
2724 mLastError.clear();
2725 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
2726
2727 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
2728
2729 if ( invalidEdges && invalidEdgesGeom )
2730 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
2731
2732 return result;
2733}
2734
2735QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
2736{
2737 if ( !d->geometry )
2738 {
2739 return QgsGeometry();
2740 }
2741
2742 QgsGeos geos( d->geometry.get() );
2743 mLastError.clear();
2744 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
2745 result.mLastError = mLastError;
2746 return result;
2747}
2748
2750{
2751 if ( !d->geometry )
2752 {
2753 return QgsGeometry();
2754 }
2755
2756 QgsGeos geos( d->geometry.get() );
2757 mLastError.clear();
2758 QgsGeometry result( geos.node( &mLastError ) );
2759 result.mLastError = mLastError;
2760 return result;
2761}
2762
2764{
2765 if ( !d->geometry )
2766 {
2767 return QgsGeometry();
2768 }
2769
2770 QgsGeos geos( d->geometry.get() );
2771 mLastError.clear();
2772 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
2773 result.mLastError = mLastError;
2774 return result;
2775}
2776
2777QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters ) const
2778{
2779 if ( !d->geometry )
2780 {
2781 return QgsGeometry();
2782 }
2783
2784 const QgsAbstractGeometry *geom = d->geometry.get();
2785 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
2786 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
2787 {
2788 segmentizedCopy.reset( d->geometry->segmentize() );
2789 geom = segmentizedCopy.get();
2790 }
2791
2792 QgsGeos geos( geom );
2793 mLastError.clear();
2794 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters ) );
2795 if ( !result )
2796 {
2797 QgsGeometry geom;
2798 geom.mLastError = mLastError;
2799 return geom;
2800 }
2801 return QgsGeometry( std::move( result ) );
2802}
2803
2805{
2806 if ( !d->geometry )
2807 {
2808 return QgsGeometry();
2809 }
2810
2811 QgsGeometry line = *this;
2813 return QgsGeometry();
2814 else if ( type() == Qgis::GeometryType::Polygon )
2815 {
2816 line = QgsGeometry( d->geometry->boundary() );
2817 }
2818
2819 const QgsCurve *curve = nullptr;
2820 if ( line.isMultipart() )
2821 {
2822 // if multi part, iterate through parts to find target part
2823 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( line.constGet() );
2824 for ( int part = 0; part < collection->numGeometries(); ++part )
2825 {
2826 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
2827 if ( !candidate )
2828 continue;
2829 const double candidateLength = candidate->length();
2830 if ( candidateLength >= distance )
2831 {
2832 curve = candidate;
2833 break;
2834 }
2835
2836 distance -= candidateLength;
2837 }
2838 }
2839 else
2840 {
2841 curve = qgsgeometry_cast< const QgsCurve * >( line.constGet() );
2842 }
2843 if ( !curve )
2844 return QgsGeometry();
2845
2846 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
2847 if ( !result )
2848 {
2849 return QgsGeometry();
2850 }
2851 return QgsGeometry( std::move( result ) );
2852}
2853
2854double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
2855{
2856 if ( type() != Qgis::GeometryType::Line )
2857 return -1;
2858
2860 return -1;
2861
2862 QgsGeometry segmentized = *this;
2864 {
2865 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
2866 }
2867
2868 QgsGeos geos( d->geometry.get() );
2869 mLastError.clear();
2870 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
2871}
2872
2873double QgsGeometry::interpolateAngle( double distance ) const
2874{
2875 if ( !d->geometry || d->geometry->isEmpty() )
2876 return 0.0;
2877
2878 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
2880 return 0.0;
2881
2882 // always operate on segmentized geometries
2883 QgsGeometry segmentized = *this;
2884 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
2885 {
2886 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
2887 }
2888
2889 QgsVertexId previous;
2890 QgsVertexId next;
2891 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
2892 return 0.0;
2893
2894 if ( previous == next )
2895 {
2896 // distance coincided exactly with a vertex
2897 QgsVertexId v2 = previous;
2898 QgsVertexId v1;
2899 QgsVertexId v3;
2900 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
2901 if ( v1.isValid() && v3.isValid() )
2902 {
2903 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
2904 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
2905 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
2906 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2907 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
2908 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
2909 }
2910 else if ( v3.isValid() )
2911 {
2912 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
2913 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
2914 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2915 }
2916 else
2917 {
2918 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
2919 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
2920 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2921 }
2922 }
2923 else
2924 {
2925 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
2926 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
2927 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
2928 }
2929}
2930
2932{
2933 if ( !d->geometry || geometry.isNull() )
2934 {
2935 return QgsGeometry();
2936 }
2937
2938 QgsGeos geos( d->geometry.get() );
2939
2940 mLastError.clear();
2941 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters ) );
2942
2943 if ( !resultGeom )
2944 {
2945 QgsGeometry geom;
2946 geom.mLastError = mLastError;
2947 return geom;
2948 }
2949
2950 return QgsGeometry( std::move( resultGeom ) );
2951}
2952
2953QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters ) const
2954{
2955 if ( !d->geometry || geometry.isNull() )
2956 {
2957 return QgsGeometry();
2958 }
2959
2960 QgsGeos geos( d->geometry.get() );
2961 mLastError.clear();
2962 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters ) );
2963 if ( !resultGeom )
2964 {
2965 QgsGeometry geom;
2966 geom.mLastError = mLastError;
2967 return geom;
2968 }
2969 return QgsGeometry( std::move( resultGeom ) );
2970}
2971
2973{
2974 if ( !d->geometry )
2975 {
2976 return QgsGeometry();
2977 }
2978
2980 {
2981 // special case - a single linestring was passed
2982 return QgsGeometry( *this );
2983 }
2984
2985 QgsGeos geos( d->geometry.get() );
2986 mLastError.clear();
2987 QgsGeometry result = geos.mergeLines( &mLastError );
2988 result.mLastError = mLastError;
2989 return result;
2990}
2991
2993{
2994 if ( !d->geometry || geometry.isNull() )
2995 {
2996 return QgsGeometry();
2997 }
2998
2999 QgsGeos geos( d->geometry.get() );
3000
3001 mLastError.clear();
3002 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters ) );
3003 if ( !resultGeom )
3004 {
3005 QgsGeometry geom;
3006 geom.mLastError = mLastError;
3007 return geom;
3008 }
3009 return QgsGeometry( std::move( resultGeom ) );
3010}
3011
3013{
3014 if ( !d->geometry || geometry.isNull() )
3015 {
3016 return QgsGeometry();
3017 }
3018
3019 QgsGeos geos( d->geometry.get() );
3020
3021 mLastError.clear();
3022 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters ) );
3023 if ( !resultGeom )
3024 {
3025 QgsGeometry geom;
3026 geom.mLastError = mLastError;
3027 return geom;
3028 }
3029 return QgsGeometry( std::move( resultGeom ) );
3030}
3031
3033{
3034 QgsInternalGeometryEngine engine( *this );
3035
3036 return engine.extrude( x, y );
3037}
3038
3040
3041QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3042{
3044 return QVector< QgsPointXY >();
3045
3046 QgsInternalGeometryEngine engine( *this );
3047 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3048 mLastError = engine.lastError();
3049 return res;
3050}
3051
3052QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3053{
3055 return QVector< QgsPointXY >();
3056
3057 QgsInternalGeometryEngine engine( *this );
3058 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3059 mLastError = engine.lastError();
3060 return res;
3061}
3063
3065{
3066 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3067}
3068
3070{
3071 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3072}
3073
3074QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3075{
3076 QVector<QgsGeometry> geometryList;
3077 if ( !d->geometry )
3078 {
3079 return geometryList;
3080 }
3081
3082 QgsGeometryCollection *gc = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
3083 if ( gc )
3084 {
3085 int numGeom = gc->numGeometries();
3086 geometryList.reserve( numGeom );
3087 for ( int i = 0; i < numGeom; ++i )
3088 {
3089 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3090 }
3091 }
3092 else //a singlepart geometry
3093 {
3094 geometryList.append( *this );
3095 }
3096
3097 return geometryList;
3098}
3099
3101{
3102 QgsPointXY point = asPoint();
3103 return point.toQPointF();
3104}
3105
3107{
3108 const QgsAbstractGeometry *part = constGet();
3109
3110 // if a geometry collection, get first part only
3111 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( part ) )
3112 {
3113 if ( collection->numGeometries() > 0 )
3114 part = collection->geometryN( 0 );
3115 else
3116 return QPolygonF();
3117 }
3118
3119 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3120 return curve->asQPolygonF();
3121 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3122 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3123 return QPolygonF();
3124}
3125
3126bool QgsGeometry::deleteRing( int ringNum, int partNum )
3127{
3128 if ( !d->geometry )
3129 {
3130 return false;
3131 }
3132
3133 detach();
3134 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3135 return ok;
3136}
3137
3138bool QgsGeometry::deletePart( int partNum )
3139{
3140 if ( !d->geometry )
3141 {
3142 return false;
3143 }
3144
3145 if ( !isMultipart() && partNum < 1 )
3146 {
3147 set( nullptr );
3148 return true;
3149 }
3150
3151 detach();
3152 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3153 return ok;
3154}
3155
3156Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3157{
3158 if ( !d->geometry )
3159 {
3161 }
3162
3163 Qgis::WkbType geomTypeBeforeModification = wkbType();
3164
3165 bool haveInvalidGeometry = false;
3166 bool geomModified = false;
3167
3168 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3169 if ( diffGeom )
3170 {
3171 reset( std::move( diffGeom ) );
3172 geomModified = true;
3173 }
3174
3175 if ( geomTypeBeforeModification != wkbType() )
3177 if ( haveInvalidGeometry )
3179 if ( !geomModified )
3181
3183}
3184
3185int QgsGeometry::avoidIntersections( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3186{
3187 const Qgis::GeometryOperationResult result = avoidIntersectionsV2( avoidIntersectionsLayers, ignoreFeatures );
3188 switch ( result )
3189 {
3191 return 0;
3193 return 1;
3195 return 2;
3197 return 3;
3199
3200 // these should never happen
3212 return 4;
3213 }
3214 return 4;
3215}
3216
3218{
3219 if ( !d->geometry )
3220 return QgsGeometry();
3221
3222 mLastError.clear();
3223 QgsGeos geos( d->geometry.get() );
3224 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError ) );
3225
3226 QgsGeometry result = QgsGeometry( std::move( g ) );
3227 result.mLastError = mLastError;
3228 return result;
3229}
3230
3232{
3233 return forcePolygonClockwise();
3234}
3235
3237{
3238 if ( !d->geometry )
3239 {
3241 }
3242
3243 if ( isMultipart() )
3244 {
3245 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3246 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3247 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3248 {
3249 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3250 }
3251 }
3252 else
3253 {
3254 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3255 {
3256 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3257 }
3258 }
3259
3261
3262}
3263
3265{
3266 if ( !d->geometry )
3267 return QgsGeometry();
3268
3269 if ( isMultipart() )
3270 {
3271 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3272 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3273 newCollection->reserve( collection->numGeometries() );
3274 for ( int i = 0; i < collection->numGeometries(); ++i )
3275 {
3276 const QgsAbstractGeometry *g = collection->geometryN( i );
3277 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3278 {
3279 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3280 corrected->forceClockwise();
3281 newCollection->addGeometry( corrected.release() );
3282 }
3283 else
3284 {
3285 newCollection->addGeometry( g->clone() );
3286 }
3287 }
3288 return QgsGeometry( std::move( newCollection ) );
3289 }
3290 else
3291 {
3292 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3293 {
3294 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3295 corrected->forceClockwise();
3296 return QgsGeometry( std::move( corrected ) );
3297 }
3298 else
3299 {
3300 // not a curve polygon, so return unchanged
3301 return *this;
3302 }
3303 }
3304}
3305
3307{
3308 if ( !d->geometry )
3309 return QgsGeometry();
3310
3311 if ( isMultipart() )
3312 {
3313 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3314 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3315 newCollection->reserve( collection->numGeometries() );
3316 for ( int i = 0; i < collection->numGeometries(); ++i )
3317 {
3318 const QgsAbstractGeometry *g = collection->geometryN( i );
3319 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
3320 {
3321 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3322 corrected->forceCounterClockwise();
3323 newCollection->addGeometry( corrected.release() );
3324 }
3325 else
3326 {
3327 newCollection->addGeometry( g->clone() );
3328 }
3329 }
3330 return QgsGeometry( std::move( newCollection ) );
3331 }
3332 else
3333 {
3334 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3335 {
3336 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3337 corrected->forceCounterClockwise();
3338 return QgsGeometry( std::move( corrected ) );
3339 }
3340 else
3341 {
3342 // not a curve polygon, so return unchanged
3343 return *this;
3344 }
3345 }
3346}
3347
3348
3349void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3350{
3351 errors.clear();
3352 if ( !d->geometry )
3353 return;
3354
3355 // avoid expensive calcs for trivial point geometries
3357 {
3358 return;
3359 }
3360
3361 switch ( method )
3362 {
3364 QgsGeometryValidator::validateGeometry( *this, errors, method );
3365 return;
3366
3368 {
3369 QgsGeos geos( d->geometry.get() );
3370 QString error;
3371 QgsGeometry errorLoc;
3372 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3373 {
3374 if ( errorLoc.isNull() )
3375 {
3376 errors.append( QgsGeometry::Error( error ) );
3377 }
3378 else
3379 {
3380 const QgsPointXY point = errorLoc.asPoint();
3381 errors.append( QgsGeometry::Error( error, point ) );
3382 }
3383 return;
3384 }
3385 }
3386 }
3387}
3388
3390{
3391 if ( !d->geometry )
3392 {
3393 return;
3394 }
3395
3396 detach();
3397 d->geometry->normalize();
3398}
3399
3401{
3402 if ( !d->geometry )
3403 {
3404 return false;
3405 }
3406
3407 return d->geometry->isValid( mLastError, flags );
3408}
3409
3411{
3412 if ( !d->geometry )
3413 return false;
3414
3415 QgsGeos geos( d->geometry.get() );
3416 mLastError.clear();
3417 return geos.isSimple( &mLastError );
3418}
3419
3420bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3421{
3422 if ( !d->geometry )
3423 return false;
3424
3425 QgsInternalGeometryEngine engine( *this );
3426 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3427}
3428
3430{
3431 if ( !d->geometry || !g.d->geometry )
3432 {
3433 return false;
3434 }
3435
3436 // fast check - are they shared copies of the same underlying geometry?
3437 if ( d == g.d )
3438 return true;
3439
3440 // fast check - distinct geometry types?
3441 if ( type() != g.type() )
3442 return false;
3443
3444 // avoid calling geos for trivial point case
3445 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point
3446 && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3447 {
3448 return equals( g );
3449 }
3450
3451 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3452 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3453 return false;
3454
3455 QgsGeos geos( d->geometry.get() );
3456 mLastError.clear();
3457 return geos.isEqual( g.d->geometry.get(), &mLastError );
3458}
3459
3460QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters )
3461{
3462 QgsGeos geos( nullptr );
3463
3464 QString error;
3465 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters ) );
3466 QgsGeometry result( std::move( geom ) );
3467 result.mLastError = error;
3468 return result;
3469}
3470
3471QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
3472{
3473 QVector<const QgsAbstractGeometry *> geomV2List;
3474 for ( const QgsGeometry &g : geometryList )
3475 {
3476 if ( !( g.isNull() ) )
3477 {
3478 geomV2List.append( g.constGet() );
3479 }
3480 }
3481
3482 QString error;
3483 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
3484 result.mLastError = error;
3485 return result;
3486}
3487
3489{
3491 {
3492 return;
3493 }
3494
3495 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3496 reset( std::move( straightGeom ) );
3497}
3498
3500{
3501 if ( !d->geometry )
3502 {
3503 return false;
3504 }
3505
3506 return d->geometry->hasCurvedSegments();
3507}
3508
3510{
3511 if ( !d->geometry )
3512 {
3514 }
3515
3516 detach();
3517 d->geometry->transform( ct, direction, transformZ );
3519}
3520
3521Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3522{
3523 if ( !d->geometry )
3524 {
3526 }
3527
3528 detach();
3529 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3531}
3532
3534{
3535 if ( d->geometry )
3536 {
3537 detach();
3538 d->geometry->transform( mtp.transform() );
3539 }
3540}
3541
3543{
3544 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
3545 {
3546 return QgsGeometry();
3547 }
3548
3549 QgsGeos geos( d->geometry.get() );
3550 mLastError.clear();
3551 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError );
3552 if ( !resultGeom )
3553 {
3554 QgsGeometry result;
3555 result.mLastError = mLastError;
3556 return result;
3557 }
3558 return QgsGeometry( std::move( resultGeom ) );
3559}
3560
3561void QgsGeometry::draw( QPainter &p ) const
3562{
3563 if ( d->geometry )
3564 {
3565 d->geometry->draw( p );
3566 }
3567}
3568
3569static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
3570{
3571 if ( vertexIndex < 0 )
3572 return false; // clearly something wrong
3573
3574 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3575 {
3576 partIndex = 0;
3577 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3578 {
3579 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3580
3581 // count total number of vertices in the part
3582 int numPoints = 0;
3583 for ( int k = 0; k < part->ringCount(); ++k )
3584 numPoints += part->vertexCount( 0, k );
3585
3586 if ( vertexIndex < numPoints )
3587 {
3588 int nothing;
3589 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3590 }
3591 vertexIndex -= numPoints;
3592 partIndex++;
3593 }
3594 }
3595 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3596 {
3597 const QgsCurve *ring = curvePolygon->exteriorRing();
3598 if ( vertexIndex < ring->numPoints() )
3599 {
3600 partIndex = 0;
3601 ringIndex = 0;
3602 vertex = vertexIndex;
3603 return true;
3604 }
3605 vertexIndex -= ring->numPoints();
3606 ringIndex = 1;
3607 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
3608 {
3609 const QgsCurve *ring = curvePolygon->interiorRing( i );
3610 if ( vertexIndex < ring->numPoints() )
3611 {
3612 partIndex = 0;
3613 vertex = vertexIndex;
3614 return true;
3615 }
3616 vertexIndex -= ring->numPoints();
3617 ringIndex += 1;
3618 }
3619 }
3620 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3621 {
3622 if ( vertexIndex < curve->numPoints() )
3623 {
3624 partIndex = 0;
3625 ringIndex = 0;
3626 vertex = vertexIndex;
3627 return true;
3628 }
3629 }
3630 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
3631 {
3632 if ( vertexIndex == 0 )
3633 {
3634 partIndex = 0;
3635 ringIndex = 0;
3636 vertex = 0;
3637 return true;
3638 }
3639 }
3640
3641 return false;
3642}
3643
3645{
3646 if ( !d->geometry )
3647 {
3648 return false;
3649 }
3650
3651 id.type = Qgis::VertexType::Segment;
3652
3653 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
3654 if ( !res )
3655 return false;
3656
3657 // now let's find out if it is a straight or circular segment
3658 const QgsAbstractGeometry *g = d->geometry.get();
3659 if ( const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( g ) )
3660 {
3661 g = geomCollection->geometryN( id.part );
3662 }
3663
3664 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3665 {
3666 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
3667 }
3668
3669 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3670 {
3671 QgsPoint p;
3672 res = curve->pointAt( id.vertex, p, id.type );
3673 if ( !res )
3674 return false;
3675 }
3676
3677 return true;
3678}
3679
3681{
3682 if ( !d->geometry )
3683 {
3684 return -1;
3685 }
3686 return d->geometry->vertexNumberFromVertexId( id );
3687}
3688
3690{
3691 return mLastError;
3692}
3693
3694void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
3695{
3696 if ( !d->geometry )
3697 return;
3698
3699 detach();
3700
3701 d->geometry->filterVertices( filter );
3702}
3703
3704void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
3705{
3706 if ( !d->geometry )
3707 return;
3708
3709 detach();
3710
3711 d->geometry->transformVertices( transform );
3712}
3713
3714void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
3715{
3716 output.clear();
3717 for ( const QgsPointXY &p : input )
3718 {
3719 output.append( QgsPoint( p ) );
3720 }
3721}
3722
3723void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
3724{
3725 output.clear();
3726 for ( const QgsPoint &p : input )
3727 {
3728 output.append( QgsPointXY( p.x(), p.y() ) );
3729 }
3730}
3731
3732void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
3733{
3734 output.clear();
3735
3736 auto convertRing = []( const QgsCurve * ring ) -> QgsPolylineXY
3737 {
3738 QgsPolylineXY res;
3739 bool doSegmentation = ( QgsWkbTypes::flatType( ring->wkbType() ) == Qgis::WkbType::CompoundCurve
3741 std::unique_ptr< QgsLineString > segmentizedLine;
3742 const QgsLineString *line = nullptr;
3743 if ( doSegmentation )
3744 {
3745 segmentizedLine.reset( ring->curveToLine() );
3746 line = segmentizedLine.get();
3747 }
3748 else
3749 {
3750 line = qgsgeometry_cast<const QgsLineString *>( ring );
3751 if ( !line )
3752 {
3753 return res;
3754 }
3755 }
3756
3757 int nVertices = line->numPoints();
3758 res.resize( nVertices );
3759 QgsPointXY *data = res.data();
3760 const double *xData = line->xData();
3761 const double *yData = line->yData();
3762 for ( int i = 0; i < nVertices; ++i )
3763 {
3764 data->setX( *xData++ );
3765 data->setY( *yData++ );
3766 data++;
3767 }
3768 return res;
3769 };
3770
3771 if ( const QgsCurve *exterior = input.exteriorRing() )
3772 {
3773 output.push_back( convertRing( exterior ) );
3774 }
3775
3776 const int interiorRingCount = input.numInteriorRings();
3777 output.reserve( output.size() + interiorRingCount );
3778 for ( int n = 0; n < interiorRingCount; ++n )
3779 {
3780 output.push_back( convertRing( input.interiorRing( n ) ) );
3781 }
3782}
3783
3785{
3786 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
3787}
3788
3789QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
3790{
3791 std::unique_ptr < QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
3792
3793 if ( polygon.isClosed() )
3794 {
3795 std::unique_ptr< QgsPolygon > poly = std::make_unique< QgsPolygon >();
3796 poly->setExteriorRing( ring.release() );
3797 return QgsGeometry( std::move( poly ) );
3798 }
3799 else
3800 {
3801 return QgsGeometry( std::move( ring ) );
3802 }
3803}
3804
3806{
3808 QgsPolygonXY result;
3809 result << createPolylineFromQPolygonF( polygon );
3810 return result;
3812}
3813
3815{
3816 QgsPolylineXY result;
3817 result.reserve( polygon.count() );
3818 for ( const QPointF &p : polygon )
3819 {
3820 result.append( QgsPointXY( p ) );
3821 }
3822 return result;
3823}
3824
3825bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
3826{
3827 if ( p1.count() != p2.count() )
3828 return false;
3829
3830 for ( int i = 0; i < p1.count(); ++i )
3831 {
3832 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
3833 return false;
3834 }
3835 return true;
3836}
3837
3838bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
3839{
3840 if ( p1.count() != p2.count() )
3841 return false;
3842
3843 for ( int i = 0; i < p1.count(); ++i )
3844 {
3845 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3846 return false;
3847 }
3848 return true;
3849}
3850
3851
3852bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
3853{
3854 if ( p1.count() != p2.count() )
3855 return false;
3856
3857 for ( int i = 0; i < p1.count(); ++i )
3858 {
3859 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
3860 return false;
3861 }
3862 return true;
3863}
3864
3865QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
3866{
3867 if ( !d->geometry || d->geometry->isEmpty() )
3868 return QgsGeometry();
3869
3870 QgsGeometry geom = *this;
3872 geom = QgsGeometry( d->geometry->segmentize() );
3873
3874 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
3875 {
3878 //can't smooth a point based geometry
3879 return geom;
3880
3882 {
3883 const QgsLineString *lineString = qgsgeometry_cast< const QgsLineString * >( geom.constGet() );
3884 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
3885 }
3886
3888 {
3889 const QgsMultiLineString *multiLine = qgsgeometry_cast< const QgsMultiLineString * >( geom.constGet() );
3890
3891 std::unique_ptr< QgsMultiLineString > resultMultiline = std::make_unique< QgsMultiLineString> ();
3892 resultMultiline->reserve( multiLine->numGeometries() );
3893 for ( int i = 0; i < multiLine->numGeometries(); ++i )
3894 {
3895 resultMultiline->addGeometry( smoothLine( *( multiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3896 }
3897 return QgsGeometry( std::move( resultMultiline ) );
3898 }
3899
3901 {
3902 const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( geom.constGet() );
3903 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
3904 }
3905
3907 {
3908 const QgsMultiPolygon *multiPoly = qgsgeometry_cast< const QgsMultiPolygon * >( geom.constGet() );
3909
3910 std::unique_ptr< QgsMultiPolygon > resultMultiPoly = std::make_unique< QgsMultiPolygon >();
3911 resultMultiPoly->reserve( multiPoly->numGeometries() );
3912 for ( int i = 0; i < multiPoly->numGeometries(); ++i )
3913 {
3914 resultMultiPoly->addGeometry( smoothPolygon( *( multiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
3915 }
3916 return QgsGeometry( std::move( resultMultiPoly ) );
3917 }
3918
3920 default:
3921 return QgsGeometry( *this );
3922 }
3923}
3924
3925std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations,
3926 const double offset, double squareDistThreshold, double maxAngleRads,
3927 bool isRing )
3928{
3929 std::unique_ptr< QgsLineString > result = std::make_unique< QgsLineString >( line );
3930 QgsPointSequence outputLine;
3931 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
3932 {
3933 outputLine.resize( 0 );
3934 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
3935 bool skipFirst = false;
3936 bool skipLast = false;
3937 if ( isRing )
3938 {
3939 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
3940 QgsPoint p2 = result->pointN( 0 );
3941 QgsPoint p3 = result->pointN( 1 );
3942 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3943 p3.x(), p3.y() );
3944 angle = std::fabs( M_PI - angle );
3945 skipFirst = angle > maxAngleRads;
3946 }
3947 for ( int i = 0; i < result->numPoints() - 1; i++ )
3948 {
3949 QgsPoint p1 = result->pointN( i );
3950 QgsPoint p2 = result->pointN( i + 1 );
3951
3952 double angle = M_PI;
3953 if ( i == 0 && isRing )
3954 {
3955 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
3956 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3957 p3.x(), p3.y() );
3958 }
3959 else if ( i < result->numPoints() - 2 )
3960 {
3961 QgsPoint p3 = result->pointN( i + 2 );
3962 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3963 p3.x(), p3.y() );
3964 }
3965 else if ( i == result->numPoints() - 2 && isRing )
3966 {
3967 QgsPoint p3 = result->pointN( 1 );
3968 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(),
3969 p3.x(), p3.y() );
3970 }
3971
3972 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
3973
3974 // don't apply distance threshold to first or last segment
3975 if ( i == 0 || i >= result->numPoints() - 2
3976 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
3977 {
3978 if ( !isRing )
3979 {
3980 if ( !skipFirst )
3981 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
3982 if ( !skipLast )
3983 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
3984 else
3985 outputLine << p2;
3986 }
3987 else
3988 {
3989 // ring
3990 if ( !skipFirst )
3991 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
3992 else if ( i == 0 )
3993 outputLine << p1;
3994 if ( !skipLast )
3995 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
3996 else
3997 outputLine << p2;
3998 }
3999 }
4000 skipFirst = skipLast;
4001 }
4002
4003 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4004 outputLine << outputLine.at( 0 );
4005
4006 result->setPoints( outputLine );
4007 }
4008 return result;
4009}
4010
4011std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4012{
4013 double maxAngleRads = maxAngle * M_PI / 180.0;
4014 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4015 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4016}
4017
4018std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4019{
4020 double maxAngleRads = maxAngle * M_PI / 180.0;
4021 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4022 std::unique_ptr< QgsPolygon > resultPoly = std::make_unique< QgsPolygon >();
4023
4024 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset,
4025 squareDistThreshold, maxAngleRads, true ).release() );
4026
4027 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4028 {
4029 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset,
4030 squareDistThreshold, maxAngleRads, true ).release() );
4031 }
4032 return resultPoly;
4033}
4034
4035QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4036{
4037 switch ( type() )
4038 {
4040 {
4041 bool srcIsMultipart = isMultipart();
4042
4043 if ( ( destMultipart && srcIsMultipart ) ||
4044 ( !destMultipart && !srcIsMultipart ) )
4045 {
4046 // return a copy of the same geom
4047 return QgsGeometry( *this );
4048 }
4049 if ( destMultipart )
4050 {
4051 // layer is multipart => make a multipoint with a single point
4052 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4053 }
4054 else
4055 {
4056 // destination is singlepart => make a single part if possible
4057 QgsMultiPointXY multiPoint = asMultiPoint();
4058 if ( multiPoint.count() == 1 )
4059 {
4060 return fromPointXY( multiPoint[0] );
4061 }
4062 }
4063 return QgsGeometry();
4064 }
4065
4067 {
4068 // only possible if destination is multipart
4069 if ( !destMultipart )
4070 return QgsGeometry();
4071
4072 // input geometry is multipart
4073 if ( isMultipart() )
4074 {
4075 const QgsMultiPolylineXY multiLine = asMultiPolyline();
4076 QgsMultiPointXY multiPoint;
4077 for ( const QgsPolylineXY &l : multiLine )
4078 for ( const QgsPointXY &p : l )
4079 multiPoint << p;
4080 return fromMultiPointXY( multiPoint );
4081 }
4082 // input geometry is not multipart: copy directly the line into a multipoint
4083 else
4084 {
4085 QgsPolylineXY line = asPolyline();
4086 if ( !line.isEmpty() )
4087 return fromMultiPointXY( line );
4088 }
4089 return QgsGeometry();
4090 }
4091
4093 {
4094 // can only transform if destination is multipoint
4095 if ( !destMultipart )
4096 return QgsGeometry();
4097
4098 // input geometry is multipart: make a multipoint from multipolygon
4099 if ( isMultipart() )
4100 {
4101 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4102 QgsMultiPointXY multiPoint;
4103 for ( const QgsPolygonXY &poly : multiPolygon )
4104 for ( const QgsPolylineXY &line : poly )
4105 for ( const QgsPointXY &pt : line )
4106 multiPoint << pt;
4107 return fromMultiPointXY( multiPoint );
4108 }
4109 // input geometry is not multipart: make a multipoint from polygon
4110 else
4111 {
4112 const QgsPolygonXY polygon = asPolygon();
4113 QgsMultiPointXY multiPoint;
4114 for ( const QgsPolylineXY &line : polygon )
4115 for ( const QgsPointXY &pt : line )
4116 multiPoint << pt;
4117 return fromMultiPointXY( multiPoint );
4118 }
4119 }
4120
4121 default:
4122 return QgsGeometry();
4123 }
4124}
4125
4126QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4127{
4128 switch ( type() )
4129 {
4131 {
4132 if ( !isMultipart() )
4133 return QgsGeometry();
4134
4135 QgsMultiPointXY multiPoint = asMultiPoint();
4136 if ( multiPoint.count() < 2 )
4137 return QgsGeometry();
4138
4139 if ( destMultipart )
4140 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4141 else
4142 return fromPolylineXY( multiPoint );
4143 }
4144
4146 {
4147 bool srcIsMultipart = isMultipart();
4148
4149 if ( ( destMultipart && srcIsMultipart ) ||
4150 ( !destMultipart && ! srcIsMultipart ) )
4151 {
4152 // return a copy of the same geom
4153 return QgsGeometry( *this );
4154 }
4155 if ( destMultipart )
4156 {
4157 // destination is multipart => makes a multipoint with a single line
4158 QgsPolylineXY line = asPolyline();
4159 if ( !line.isEmpty() )
4160 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4161 }
4162 else
4163 {
4164 // destination is singlepart => make a single part if possible
4165 QgsMultiPolylineXY multiLine = asMultiPolyline();
4166 if ( multiLine.count() == 1 )
4167 return fromPolylineXY( multiLine[0] );
4168 }
4169 return QgsGeometry();
4170 }
4171
4173 {
4174 // input geometry is multipolygon
4175 if ( isMultipart() )
4176 {
4177 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4178 QgsMultiPolylineXY multiLine;
4179 for ( const QgsPolygonXY &poly : multiPolygon )
4180 for ( const QgsPolylineXY &line : poly )
4181 multiLine << line;
4182
4183 if ( destMultipart )
4184 {
4185 // destination is multipart
4186 return fromMultiPolylineXY( multiLine );
4187 }
4188 else if ( multiLine.count() == 1 )
4189 {
4190 // destination is singlepart => make a single part if possible
4191 return fromPolylineXY( multiLine[0] );
4192 }
4193 }
4194 // input geometry is single polygon
4195 else
4196 {
4197 QgsPolygonXY polygon = asPolygon();
4198 // if polygon has rings
4199 if ( polygon.count() > 1 )
4200 {
4201 // cannot fit a polygon with rings in a single line layer
4202 // TODO: would it be better to remove rings?
4203 if ( destMultipart )
4204 {
4205 const QgsPolygonXY polygon = asPolygon();
4206 QgsMultiPolylineXY multiLine;
4207 multiLine.reserve( polygon.count() );
4208 for ( const QgsPolylineXY &line : polygon )
4209 multiLine << line;
4210 return fromMultiPolylineXY( multiLine );
4211 }
4212 }
4213 // no rings
4214 else if ( polygon.count() == 1 )
4215 {
4216 if ( destMultipart )
4217 {
4218 return fromMultiPolylineXY( polygon );
4219 }
4220 else
4221 {
4222 return fromPolylineXY( polygon[0] );
4223 }
4224 }
4225 }
4226 return QgsGeometry();
4227 }
4228
4229 default:
4230 return QgsGeometry();
4231 }
4232}
4233
4234QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4235{
4236 switch ( type() )
4237 {
4239 {
4240 if ( !isMultipart() )
4241 return QgsGeometry();
4242
4243 QgsMultiPointXY multiPoint = asMultiPoint();
4244 if ( multiPoint.count() < 3 )
4245 return QgsGeometry();
4246
4247 if ( multiPoint.last() != multiPoint.first() )
4248 multiPoint << multiPoint.first();
4249
4250 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4251 if ( destMultipart )
4252 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4253 else
4254 return fromPolygonXY( polygon );
4255 }
4256
4258 {
4259 // input geometry is multiline
4260 if ( isMultipart() )
4261 {
4262 QgsMultiPolylineXY multiLine = asMultiPolyline();
4263 QgsMultiPolygonXY multiPolygon;
4264 for ( QgsMultiPolylineXY::iterator multiLineIt = multiLine.begin(); multiLineIt != multiLine.end(); ++multiLineIt )
4265 {
4266 // do not create polygon for a 1 segment line
4267 if ( ( *multiLineIt ).count() < 3 )
4268 return QgsGeometry();
4269 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4270 return QgsGeometry();
4271
4272 // add closing node
4273 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4274 *multiLineIt << ( *multiLineIt ).first();
4275 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4276 }
4277 // check that polygons were inserted
4278 if ( !multiPolygon.isEmpty() )
4279 {
4280 if ( destMultipart )
4281 {
4282 return fromMultiPolygonXY( multiPolygon );
4283 }
4284 else if ( multiPolygon.count() == 1 )
4285 {
4286 // destination is singlepart => make a single part if possible
4287 return fromPolygonXY( multiPolygon[0] );
4288 }
4289 }
4290 }
4291 // input geometry is single line
4292 else
4293 {
4294 QgsPolylineXY line = asPolyline();
4295
4296 // do not create polygon for a 1 segment line
4297 if ( line.count() < 3 )
4298 return QgsGeometry();
4299 if ( line.count() == 3 && line.first() == line.last() )
4300 return QgsGeometry();
4301
4302 // add closing node
4303 if ( line.first() != line.last() )
4304 line << line.first();
4305
4306 // destination is multipart
4307 if ( destMultipart )
4308 {
4309 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4310 }
4311 else
4312 {
4313 return fromPolygonXY( QgsPolygonXY() << line );
4314 }
4315 }
4316 return QgsGeometry();
4317 }
4318
4320 {
4321 bool srcIsMultipart = isMultipart();
4322
4323 if ( ( destMultipart && srcIsMultipart ) ||
4324 ( !destMultipart && ! srcIsMultipart ) )
4325 {
4326 // return a copy of the same geom
4327 return QgsGeometry( *this );
4328 }
4329 if ( destMultipart )
4330 {
4331 // destination is multipart => makes a multipoint with a single polygon
4332 QgsPolygonXY polygon = asPolygon();
4333 if ( !polygon.isEmpty() )
4334 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4335 }
4336 else
4337 {
4338 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4339 if ( multiPolygon.count() == 1 )
4340 {
4341 // destination is singlepart => make a single part if possible
4342 return fromPolygonXY( multiPolygon[0] );
4343 }
4344 }
4345 return QgsGeometry();
4346 }
4347
4348 default:
4349 return QgsGeometry();
4350 }
4351}
4352
4354{
4355 return new QgsGeos( geometry, precision );
4356}
4357
4358QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
4359{
4360 out << geometry.asWkb();
4361 return out;
4362}
4363
4364QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4365{
4366 QByteArray byteArray;
4367 in >> byteArray;
4368 if ( byteArray.isEmpty() )
4369 {
4370 geometry.set( nullptr );
4371 return in;
4372 }
4373
4374 geometry.fromWkb( byteArray );
4375 return in;
4376}
4377
4378
4380{
4381 return mMessage;
4382}
4383
4385{
4386 return mLocation;
4387}
4388
4390{
4391 return mHasLocation;
4392}
@ 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:1669
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition: qgis.h:2675
AngularDirection
Angular directions.
Definition: qgis.h:2790
@ NoOrientation
Unknown orientation or sentinel value.
GeometryOperationResult
Success or failure of a geometry operation.
Definition: qgis.h:1615
@ 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:1648
@ Segment
The actual start or end point of a segment.
GeometryValidationEngine
Available engines for validating geometries.
Definition: qgis.h:1657
@ 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:255
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
JoinStyle
Join styles for buffers.
Definition: qgis.h:1694
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:1681
CoverageValidityResult
Coverage validity results.
Definition: qgis.h:1707
@ Error
An exception occurred while determining validity.
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition: qgis.h:2660
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition: qgis.h:1720
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ MultiPoint
MultiPoint.
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
@ 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:2191
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.
Definition: qgscircle.cpp:453
QgsCircularString * toCircularString(bool oriented=false) const
Returns a circular string from the circle.
Definition: qgscircle.cpp:430
static QgsCircle minimalCircleFrom3Points(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon=1E-8)
Constructs the smallest circle from 3 points.
Definition: qgscircle.cpp:326
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.
Definition: qgsellipse.cpp:224
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
Geometry collection.
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.
Definition: qgsgeometry.h:109
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.
Definition: qgsgeometry.h:2694
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.
Definition: qgsgeometry.h:162
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.
Q_GADGET bool isNull
Definition: qgsgeometry.h:164
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
Definition: qgsgeometry.h:165
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.
Definition: qgsgeometry.cpp:86
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.
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()
Definition: qgsgeometry.cpp:62
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.
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...
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.
Definition: qgsgeometry.cpp:57
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:98
double hausdorffDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:660
double hausdorffDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:637
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1864
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition: qgsgeos.cpp:507
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:2966
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:683
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:706
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.
Definition: qgslinestring.h:45
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.
Definition: qgsmaptopixel.h:39
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:88
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
Definition: qgsmultipoint.h:29
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
Q_GADGET 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
Q_GADGET double x
Definition: qgspoint.h:52
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgspoint.cpp:105
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:701
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:33
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:196
bool isNull() const
Test if the rectangle is null (holding no spatial information).
Definition: qgsrectangle.h:505
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206
bool isEmpty() const
Returns true if the rectangle has no area.
Definition: qgsrectangle.h:492
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...
Definition: qgswkbtypes.h:862
static bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:758
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:200
static bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:806
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:628
static Qgis::WkbType curveType(Qgis::WkbType type)
Returns the curve type for a WKB type.
Definition: qgswkbtypes.h:377
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:716
Contains geos related utilities and functions.
Definition: qgsgeos.h:36
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:5776
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:5775
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
QVector< QgsPoint > QgsPointSequence
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)
Q_GLOBAL_STATIC_WITH_ARGS(WktCache, sWktCache,(2000)) QgsGeometry QgsGeometry
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
Definition: qgsgeometry.cpp:54
QgsGeometryPrivate(std::unique_ptr< QgsAbstractGeometry > geometry)
Definition: qgsgeometry.cpp:52
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