QGIS API Documentation 4.1.0-Master (467af3bbe65)
Loading...
Searching...
No Matches
qgsgeometry.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometry.cpp - Geometry (stored as Open Geospatial Consortium WKB)
3 -------------------------------------------------------------------
4Date : 02 May 2005
5Copyright : (C) 2005 by Brendan Morley
6email : morb at ozemail dot com dot au
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsgeometry.h"
17
18#include <cmath>
19#include <cstdarg>
20#include <cstdio>
21#include <geos_c.h>
22#include <limits>
23#include <nlohmann/json.hpp>
24
25#include "qgis.h"
26#include "qgsabstractgeometry.h"
27#include "qgscircle.h"
28#include "qgscurve.h"
30#include "qgsgeometryfactory.h"
31#include "qgsgeometryutils.h"
33#include "qgsgeos.h"
35#include "qgslinestring.h"
36#include "qgsmaptopixel.h"
37#include "qgsmultilinestring.h"
38#include "qgsmultipoint.h"
39#include "qgsmultipolygon.h"
40#include "qgsnurbsutils.h"
41#include "qgspoint.h"
42#include "qgspointxy.h"
43#include "qgspolygon.h"
45#include "qgsrectangle.h"
46#include "qgstriangle.h"
48#include "qgsvectorlayer.h"
49
50#include <QCache>
51#include <QString>
52
53#include "moc_qgsgeometry.cpp"
54
55using namespace Qt::StringLiterals;
56
58{
60 : ref( 1 )
61 {}
62 QgsGeometryPrivate( std::unique_ptr< QgsAbstractGeometry > geometry )
63 : ref( 1 )
64 , geometry( std::move( geometry ) )
65 {}
66 QAtomicInt ref;
67 std::unique_ptr< QgsAbstractGeometry > geometry;
68};
69
73
75{
76 if ( !d->ref.deref() )
77 delete d;
78}
79
81 : d( new QgsGeometryPrivate() )
82{
83 d->geometry.reset( geom );
84}
85
86QgsGeometry::QgsGeometry( std::unique_ptr<QgsAbstractGeometry> geom )
87 : d( new QgsGeometryPrivate( std::move( geom ) ) )
88{}
89
91 : d( other.d )
92{
93 mLastError = other.mLastError;
94 d->ref.ref();
95}
96
98{
99 if ( this != &other )
100 {
101 if ( !d->ref.deref() )
102 {
103 delete d;
104 }
105
106 mLastError = other.mLastError;
107 d = other.d;
108 d->ref.ref();
109 }
110 return *this;
111}
112
113void QgsGeometry::detach()
114{
115 if ( d->ref <= 1 )
116 return;
117
118 std::unique_ptr< QgsAbstractGeometry > cGeom;
119 if ( d->geometry )
120 cGeom.reset( d->geometry->clone() );
121
122 reset( std::move( cGeom ) );
123}
124
125void QgsGeometry::reset( std::unique_ptr<QgsAbstractGeometry> newGeometry )
126{
127 if ( d->ref > 1 )
128 {
129 ( void ) d->ref.deref();
130 d = new QgsGeometryPrivate();
131 }
132 d->geometry = std::move( newGeometry );
133}
134
136{
137 return d->geometry.get();
138}
139
141{
142 detach();
143 return d->geometry.get();
144}
145
147{
148 if ( d->geometry.get() == geometry )
149 {
150 return;
151 }
152
153 reset( std::unique_ptr< QgsAbstractGeometry >( geometry ) );
154}
155
157{
158 return !d->geometry;
159}
160
161typedef QCache< QString, QgsGeometry > WktCache;
162Q_GLOBAL_STATIC_WITH_ARGS( WktCache, sWktCache, ( 2000 ) ) // store up to 2000 geometries
163Q_GLOBAL_STATIC( QMutex, sWktMutex )
164
165QgsGeometry QgsGeometry::fromWkt( const QString &wkt )
166{
167 QMutexLocker lock( sWktMutex() );
168 if ( const QgsGeometry *cached = sWktCache()->object( wkt ) )
169 return *cached;
170 const QgsGeometry result( QgsGeometryFactory::geomFromWkt( wkt ) );
171 sWktCache()->insert( wkt, new QgsGeometry( result ), 1 );
172 return result;
173}
174
176{
177 std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::fromPointXY( point ) );
178 if ( geom )
179 {
180 return QgsGeometry( geom.release() );
181 }
182 return QgsGeometry();
183}
184
186{
187 return QgsGeometry( point.clone() );
188}
189
191{
192 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::fromPolylineXY( polyline );
193 if ( geom )
194 {
195 return QgsGeometry( std::move( geom ) );
196 }
197 return QgsGeometry();
198}
199
201{
202 return QgsGeometry( std::make_unique< QgsLineString >( polyline ) );
203}
204
206{
207 std::unique_ptr< QgsPolygon > geom = QgsGeometryFactory::fromPolygonXY( polygon );
208 if ( geom )
209 {
210 return QgsGeometry( std::move( geom ) );
211 }
212 return QgsGeometry();
213}
214
216{
217 std::unique_ptr< QgsMultiPoint > geom = QgsGeometryFactory::fromMultiPointXY( multipoint );
218 if ( geom )
219 {
220 return QgsGeometry( std::move( geom ) );
221 }
222 return QgsGeometry();
223}
224
226{
227 std::unique_ptr< QgsMultiLineString > geom = QgsGeometryFactory::fromMultiPolylineXY( multiline );
228 if ( geom )
229 {
230 return QgsGeometry( std::move( geom ) );
231 }
232 return QgsGeometry();
233}
234
236{
237 std::unique_ptr< QgsMultiPolygon > geom = QgsGeometryFactory::fromMultiPolygonXY( multipoly );
238 if ( geom )
239 {
240 return QgsGeometry( std::move( geom ) );
241 }
242 return QgsGeometry();
243}
244
246{
247 if ( rect.isNull() )
248 return QgsGeometry();
249
250 auto ext = std::make_unique< QgsLineString >(
251 QVector< double >() << rect.xMinimum() << rect.xMaximum() << rect.xMaximum() << rect.xMinimum() << rect.xMinimum(),
252 QVector< double >() << rect.yMinimum() << rect.yMinimum() << rect.yMaximum() << rect.yMaximum() << rect.yMinimum()
253 );
254 auto polygon = std::make_unique< QgsPolygon >();
255 polygon->setExteriorRing( ext.release() );
256 return QgsGeometry( std::move( polygon ) );
257}
258
260{
261 if ( box.is2d() )
262 {
263 return fromRect( box.toRectangle() );
264 }
265
266 auto polyhedralSurface = std::make_unique< QgsPolyhedralSurface >();
267
268 auto ext1 = std::make_unique< QgsLineString >(
269 QVector< double >() << box.xMinimum() << box.xMinimum() << box.xMaximum() << box.xMaximum() << box.xMinimum(),
270 QVector< double >() << box.yMinimum() << box.yMaximum() << box.yMaximum() << box.yMinimum() << box.yMinimum(),
271 QVector< double >() << box.zMinimum() << box.zMinimum() << box.zMinimum() << box.zMinimum() << box.zMinimum()
272 );
273 auto polygon1 = std::make_unique< QgsPolygon >( ext1.release() );
274 polyhedralSurface->addPatch( polygon1.release() );
275
276 auto ext2 = std::make_unique< QgsLineString >(
277 QVector< double >() << box.xMinimum() << box.xMinimum() << box.xMinimum() << box.xMinimum() << box.xMinimum(),
278 QVector< double >() << box.yMinimum() << box.yMaximum() << box.yMaximum() << box.yMinimum() << box.yMinimum(),
279 QVector< double >() << box.zMinimum() << box.zMinimum() << box.zMaximum() << box.zMaximum() << box.zMinimum()
280 );
281 auto polygon2 = std::make_unique< QgsPolygon >( ext2.release() );
282 polyhedralSurface->addPatch( polygon2.release() );
283
284 auto ext3 = std::make_unique< QgsLineString >(
285 QVector< double >() << box.xMinimum() << box.xMaximum() << box.xMaximum() << box.xMinimum() << box.xMinimum(),
286 QVector< double >() << box.yMinimum() << box.yMinimum() << box.yMinimum() << box.yMinimum() << box.yMinimum(),
287 QVector< double >() << box.zMinimum() << box.zMinimum() << box.zMaximum() << box.zMaximum() << box.zMinimum()
288 );
289 auto polygon3 = std::make_unique< QgsPolygon >( ext3.release() );
290 polyhedralSurface->addPatch( polygon3.release() );
291
292 auto ext4 = std::make_unique< QgsLineString >(
293 QVector< double >() << box.xMaximum() << box.xMaximum() << box.xMinimum() << box.xMinimum() << box.xMaximum(),
294 QVector< double >() << box.yMaximum() << box.yMinimum() << box.yMinimum() << box.yMaximum() << box.yMaximum(),
295 QVector< double >() << box.zMaximum() << box.zMaximum() << box.zMaximum() << box.zMaximum() << box.zMaximum()
296 );
297 auto polygon4 = std::make_unique< QgsPolygon >( ext4.release() );
298 polyhedralSurface->addPatch( polygon4.release() );
299
300 auto ext5 = std::make_unique< QgsLineString >(
301 QVector< double >() << box.xMaximum() << box.xMaximum() << box.xMaximum() << box.xMaximum() << box.xMaximum(),
302 QVector< double >() << box.yMaximum() << box.yMinimum() << box.yMinimum() << box.yMaximum() << box.yMaximum(),
303 QVector< double >() << box.zMaximum() << box.zMaximum() << box.zMinimum() << box.zMinimum() << box.zMaximum()
304 );
305 auto polygon5 = std::make_unique< QgsPolygon >( ext5.release() );
306 polyhedralSurface->addPatch( polygon5.release() );
307
308 auto ext6 = std::make_unique< QgsLineString >(
309 QVector< double >() << box.xMaximum() << box.xMaximum() << box.xMinimum() << box.xMinimum() << box.xMaximum(),
310 QVector< double >() << box.yMaximum() << box.yMaximum() << box.yMaximum() << box.yMaximum() << box.yMaximum(),
311 QVector< double >() << box.zMaximum() << box.zMinimum() << box.zMinimum() << box.zMaximum() << box.zMaximum()
312 );
313 auto polygon6 = std::make_unique< QgsPolygon >( ext6.release() );
314 polyhedralSurface->addPatch( polygon6.release() );
315
316 return QgsGeometry( std::move( polyhedralSurface ) );
317}
318
319QgsGeometry QgsGeometry::collectGeometry( const QVector< QgsGeometry > &geometries )
320{
321 QgsGeometry collected;
322
323 for ( const QgsGeometry &g : geometries )
324 {
325 if ( collected.isNull() )
326 {
327 collected = g;
328 collected.convertToMultiType();
329 }
330 else
331 {
332 if ( g.isMultipart() )
333 {
334 for ( auto p = g.const_parts_begin(); p != g.const_parts_end(); ++p )
335 {
336 collected.addPartV2( ( *p )->clone() );
337 }
338 }
339 else
340 {
341 collected.addPart( g );
342 }
343 }
344 }
345 return collected;
346}
347
348QgsGeometry QgsGeometry::collectTinPatches( const QVector<QgsGeometry> &geometries )
349{
350 auto resultTin = std::make_unique<QgsTriangulatedSurface>();
351 bool first = true;
352
353 for ( const QgsGeometry &geom : geometries )
354 {
355 if ( geom.isNull() )
356 continue;
357
358 const QgsAbstractGeometry *abstractGeom = geom.constGet();
359
361 {
362 // Preserve Z/M from first valid geometry
363 if ( first )
364 {
365 if ( tin->is3D() )
366 resultTin->addZValue( 0 );
367 if ( tin->isMeasure() )
368 resultTin->addMValue( 0 );
369 first = false;
370 }
371
372 // Copy all patches (triangles) from the TIN
373 for ( int j = 0; j < tin->numPatches(); ++j )
374 {
375 if ( const QgsPolygon *patch = tin->patchN( j ) )
376 {
377 resultTin->addPatch( patch->clone() );
378 }
379 }
380 }
381 else if ( const QgsTriangle *triangle = qgsgeometry_cast<const QgsTriangle *>( abstractGeom ) )
382 {
383 // Preserve Z/M from first valid geometry
384 if ( first )
385 {
386 if ( triangle->is3D() )
387 resultTin->addZValue( 0 );
388 if ( triangle->isMeasure() )
389 resultTin->addMValue( 0 );
390 first = false;
391 }
392
393 resultTin->addPatch( triangle->clone() );
394 }
395 }
396
397 if ( resultTin->numPatches() == 0 )
398 return QgsGeometry();
399
400 return QgsGeometry( std::move( resultTin ) );
401}
402
403QgsGeometry QgsGeometry::createWedgeBuffer( const QgsPoint &center, const double azimuth, const double angularWidth, const double outerRadius, const double innerRadius )
404{
405 const double startAngle = azimuth - angularWidth * 0.5;
406 const double endAngle = azimuth + angularWidth * 0.5;
407
408 return createWedgeBufferFromAngles( center, startAngle, endAngle, outerRadius, innerRadius );
409}
410
411QgsGeometry QgsGeometry::createWedgeBufferFromAngles( const QgsPoint &center, double startAngle, double endAngle, double outerRadius, double innerRadius )
412{
413 auto wedge = std::make_unique< QgsCompoundCurve >();
414
415 const double DEG_TO_RAD = M_PI / 180.0;
416 const double RAD_TO_DEG = 180.0 / M_PI;
417
418 const double angularWidth = endAngle - startAngle;
419 const bool useShortestArc = QgsGeometryUtilsBase::normalizedAngle( angularWidth * DEG_TO_RAD ) * RAD_TO_DEG <= 180.0;
420
421 if ( std::abs( angularWidth ) >= 360.0 )
422 {
423 auto outerCc = std::make_unique< QgsCompoundCurve >();
424
425 QgsCircle outerCircle = QgsCircle( center, outerRadius );
426 outerCc->addCurve( outerCircle.toCircularString().release() );
427
428 auto cp = std::make_unique< QgsCurvePolygon >();
429 cp->setExteriorRing( outerCc.release() );
430
431 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
432 {
433 auto innerCc = std::make_unique< QgsCompoundCurve >();
434
435 QgsCircle innerCircle = QgsCircle( center, innerRadius );
436 innerCc->addCurve( innerCircle.toCircularString().release() );
437
438 cp->setInteriorRings( { innerCc.release() } );
439 }
440
441 return QgsGeometry( std::move( cp ) );
442 }
443
444 const QgsPoint outerP1 = center.project( outerRadius, startAngle );
445 const QgsPoint outerP2 = center.project( outerRadius, endAngle );
446
447 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( outerP1, outerP2, center, useShortestArc ) ) );
448
449 if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
450 {
451 const QgsPoint innerP1 = center.project( innerRadius, startAngle );
452 const QgsPoint innerP2 = center.project( innerRadius, endAngle );
453 wedge->addCurve( new QgsLineString( outerP2, innerP2 ) );
454 wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( innerP2, innerP1, center, useShortestArc ) ) );
455 wedge->addCurve( new QgsLineString( innerP1, outerP1 ) );
456 }
457 else
458 {
459 wedge->addCurve( new QgsLineString( outerP2, center ) );
460 wedge->addCurve( new QgsLineString( center, outerP1 ) );
461 }
462
463 auto cp = std::make_unique< QgsCurvePolygon >();
464 cp->setExteriorRing( wedge.release() );
465 return QgsGeometry( std::move( cp ) );
466}
467
468void QgsGeometry::fromWkb( unsigned char *wkb, int length )
469{
470 QgsConstWkbPtr ptr( wkb, length );
471 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
472 delete[] wkb;
473}
474
475void QgsGeometry::fromWkb( const QByteArray &wkb )
476{
477 QgsConstWkbPtr ptr( wkb );
478 reset( QgsGeometryFactory::geomFromWkb( ptr ) );
479}
480
482{
483 if ( !d->geometry )
484 {
486 }
487 else
488 {
489 return d->geometry->wkbType();
490 }
491}
492
494{
495 if ( !d->geometry )
496 {
498 }
499 return QgsWkbTypes::geometryType( d->geometry->wkbType() );
500}
501
503{
504 if ( !d->geometry )
505 {
506 return true;
507 }
508
509 return d->geometry->isEmpty();
510}
511
513{
514 if ( !d->geometry )
515 {
516 return false;
517 }
518 return QgsWkbTypes::isMultiType( d->geometry->wkbType() );
519}
520QgsPointXY QgsGeometry::closestVertex( const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist ) const
521{
522 if ( !d->geometry )
523 {
524 sqrDist = -1;
525 return QgsPointXY();
526 }
527
528 QgsPoint pt( point );
529 QgsVertexId id;
530
531 QgsPoint vp = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, id );
532 if ( !id.isValid() )
533 {
534 sqrDist = -1;
535 return QgsPointXY();
536 }
537 sqrDist = QgsGeometryUtils::sqrDistance2D( pt, vp );
538
539 QgsVertexId prevVertex;
540 QgsVertexId nextVertex;
541 d->geometry->adjacentVertices( id, prevVertex, nextVertex );
542 closestVertexIndex = vertexNrFromVertexId( id );
543 previousVertexIndex = vertexNrFromVertexId( prevVertex );
544 nextVertexIndex = vertexNrFromVertexId( nextVertex );
545 return QgsPointXY( vp.x(), vp.y() );
546}
547
548double QgsGeometry::distanceToVertex( int vertex ) const
549{
550 if ( !d->geometry )
551 {
552 return -1;
553 }
554
555 QgsVertexId id;
556 if ( !vertexIdFromVertexNr( vertex, id ) )
557 {
558 return -1;
559 }
560
561 return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id );
562}
563
564double QgsGeometry::angleAtVertex( int vertex ) const
565{
566 if ( !d->geometry )
567 {
568 return 0;
569 }
570
571 QgsVertexId v2;
572 if ( !vertexIdFromVertexNr( vertex, v2 ) )
573 {
574 return 0;
575 }
576
577 return d->geometry->vertexAngle( v2 );
578}
579
580void QgsGeometry::adjacentVertices( int atVertex, int &beforeVertex, int &afterVertex ) const
581{
582 if ( !d->geometry )
583 {
584 return;
585 }
586
587 QgsVertexId id;
588 if ( !vertexIdFromVertexNr( atVertex, id ) )
589 {
590 beforeVertex = -1;
591 afterVertex = -1;
592 return;
593 }
594
595 QgsVertexId beforeVertexId, afterVertexId;
596 d->geometry->adjacentVertices( id, beforeVertexId, afterVertexId );
597 beforeVertex = vertexNrFromVertexId( beforeVertexId );
598 afterVertex = vertexNrFromVertexId( afterVertexId );
599}
600
601bool QgsGeometry::moveVertex( double x, double y, int atVertex )
602{
603 if ( !d->geometry )
604 {
605 return false;
606 }
607
608 QgsVertexId id;
609 if ( !vertexIdFromVertexNr( atVertex, id ) )
610 {
611 return false;
612 }
613
614 detach();
615
616 return d->geometry->moveVertex( id, QgsPoint( x, y ) );
617}
618
619bool QgsGeometry::moveVertex( const QgsPoint &p, int atVertex )
620{
621 if ( !d->geometry )
622 {
623 return false;
624 }
625
626 QgsVertexId id;
627 if ( !vertexIdFromVertexNr( atVertex, id ) )
628 {
629 return false;
630 }
631
632 detach();
633
634 return d->geometry->moveVertex( id, p );
635}
636
637bool QgsGeometry::deleteVertex( int atVertex )
638{
639 if ( !d->geometry )
640 {
641 return false;
642 }
643
644 //maintain compatibility with < 2.10 API
645 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::MultiPoint )
646 {
647 detach();
648 //delete geometry instead of point
649 return static_cast< QgsGeometryCollection * >( d->geometry.get() )->removeGeometry( atVertex );
650 }
651
652 //if it is a point, set the geometry to nullptr
653 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
654 {
655 reset( nullptr );
656 return true;
657 }
658
659 QgsVertexId id;
660 if ( !vertexIdFromVertexNr( atVertex, id ) )
661 {
662 return false;
663 }
664
665 detach();
666
667 return d->geometry->deleteVertex( id );
668}
669
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;
686 if ( owningCollection )
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;
694 if ( owningPolygon )
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 )
702 return false;
703
704 bool success = false;
706 if ( cpdCurve )
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 auto 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 && !owningCollection )
723 {
724 // Standalone linestring
725 reset( std::make_unique<QgsCompoundCurve>( *cpdCurve ) ); // <- REVIEW PLZ
726 }
727 else if ( owningPolygon )
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 )
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
760 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::MultiPoint )
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
786 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::MultiPoint )
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( u"failed to insert topo point"_s );
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 = QgsGeometry( geos.closestPoint( other ) );
868 result.mLastError = mLastError;
869 return result;
870}
871
873{
874 // avoid calling geos for trivial point-to-point line calculations
875 if ( d->geometry && QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( other.wkbType() ) == Qgis::WkbType::Point )
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 = QgsGeometry( 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
903double QgsGeometry::closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment, double epsilon ) const
904{
905 if ( !d->geometry )
906 {
907 return -1;
908 }
909
910 QgsPoint segmentPt;
911 QgsVertexId vertexAfter;
912
913 double sqrDist = d->geometry->closestSegment( QgsPoint( point ), segmentPt, vertexAfter, leftOrRightOfSegment, epsilon );
914 if ( sqrDist < 0 )
915 return -1;
916
917 minDistPoint.setX( segmentPt.x() );
918 minDistPoint.setY( segmentPt.y() );
919 nextVertexIndex = vertexNrFromVertexId( vertexAfter );
920 return sqrDist;
921}
922
923Qgis::GeometryOperationResult QgsGeometry::addRing( const QVector<QgsPointXY> &ring )
924{
925 auto ringLine = std::make_unique< QgsLineString >( ring );
926 return addRing( ringLine.release() );
927}
928
930{
931 std::unique_ptr< QgsCurve > r( ring );
932 if ( !d->geometry )
933 {
935 }
936
937 detach();
938
939 return QgsGeometryEditUtils::addRing( d->geometry.get(), std::move( r ) );
940}
941
942Qgis::GeometryOperationResult QgsGeometry::addPart( const QVector<QgsPointXY> &points, Qgis::GeometryType geomType )
943{
945 convertPointList( points, l );
947 return addPart( l, geomType );
949}
950
952{
954 convertPointList( points, l );
955 return addPartV2( l, wkbType );
956}
957
959{
960 std::unique_ptr< QgsAbstractGeometry > partGeom;
961 if ( points.size() == 1 )
962 {
963 partGeom = std::make_unique< QgsPoint >( points[0] );
964 }
965 else if ( points.size() > 1 )
966 {
967 auto ringLine = std::make_unique< QgsLineString >();
968 ringLine->setPoints( points );
969 partGeom = std::move( ringLine );
970 }
972 return addPart( partGeom.release(), geomType );
974}
975
977{
978 std::unique_ptr< QgsAbstractGeometry > partGeom;
979 if ( points.size() == 1 )
980 {
981 partGeom = std::make_unique< QgsPoint >( points[0] );
982 }
983 else if ( points.size() > 1 )
984 {
985 auto ringLine = std::make_unique< QgsLineString >();
986 ringLine->setPoints( points );
987 partGeom = std::move( ringLine );
988 }
989 return addPartV2( partGeom.release(), wkbType );
990}
991
993{
994 std::unique_ptr< QgsAbstractGeometry > p( part );
995 if ( !d->geometry )
996 {
997 switch ( geomType )
998 {
1000 reset( std::make_unique< QgsMultiPoint >() );
1001 break;
1003 reset( std::make_unique< QgsMultiLineString >() );
1004 break;
1006 reset( std::make_unique< QgsMultiPolygon >() );
1007 break;
1008 default:
1009 reset( nullptr );
1011 }
1012 }
1013 else
1014 {
1015 detach();
1016 }
1017
1019 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
1020}
1021
1023{
1024 std::unique_ptr< QgsAbstractGeometry > p( part );
1025 if ( !d->geometry )
1026 {
1028 {
1030 reset( std::make_unique< QgsMultiPoint >() );
1031 break;
1033 reset( std::make_unique< QgsMultiLineString >() );
1034 break;
1037 reset( std::make_unique< QgsMultiPolygon >() );
1038 break;
1040 reset( std::make_unique< QgsMultiSurface >() );
1041 break;
1044 reset( std::make_unique< QgsMultiCurve >() );
1045 break;
1047 reset( std::make_unique< QgsPolyhedralSurface >() );
1048 break;
1049 case Qgis::WkbType::TIN:
1050 reset( std::make_unique< QgsTriangulatedSurface >() );
1051 break;
1052 default:
1053 reset( nullptr );
1055 }
1056 }
1057 else
1058 {
1059 detach();
1060 // For TIN and PolyhedralSurface, they already support multiple patches, no conversion needed
1061 const Qgis::WkbType flatType = QgsWkbTypes::flatType( d->geometry->wkbType() );
1062 if ( flatType != Qgis::WkbType::TIN && flatType != Qgis::WkbType::PolyhedralSurface )
1063 {
1065 }
1066 }
1067
1068 return QgsGeometryEditUtils::addPart( d->geometry.get(), std::move( p ) );
1069}
1070
1072{
1073 if ( !d->geometry )
1074 {
1076 }
1077 if ( newPart.isNull() || !newPart.d->geometry )
1078 {
1080 }
1081
1082 return addPartV2( newPart.d->geometry->clone() );
1083}
1084
1085QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
1086{
1087 if ( !d->geometry || type() != Qgis::GeometryType::Polygon )
1088 {
1089 return QgsGeometry();
1090 }
1091
1092 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
1093 {
1094 const QVector<QgsGeometry> parts = asGeometryCollection();
1095 QVector<QgsGeometry> results;
1096 results.reserve( parts.count() );
1097 for ( const QgsGeometry &part : parts )
1098 {
1099 QgsGeometry result = part.removeInteriorRings( minimumRingArea );
1100 if ( !result.isNull() )
1101 results << result;
1102 }
1103 if ( results.isEmpty() )
1104 return QgsGeometry();
1105
1106 QgsGeometry first = results.takeAt( 0 );
1107 for ( const QgsGeometry &result : std::as_const( results ) )
1108 {
1109 first.addPart( result );
1110 }
1111 return first;
1112 }
1113 else
1114 {
1115 std::unique_ptr< QgsCurvePolygon > newPoly( static_cast< QgsCurvePolygon * >( d->geometry->clone() ) );
1116 newPoly->removeInteriorRings( minimumRingArea );
1117 return QgsGeometry( std::move( newPoly ) );
1118 }
1119}
1120
1121Qgis::GeometryOperationResult QgsGeometry::translate( double dx, double dy, double dz, double dm )
1122{
1123 if ( !d->geometry )
1124 {
1126 }
1127
1128 detach();
1129
1130 d->geometry->transform( QTransform::fromTranslate( dx, dy ), dz, 1.0, dm );
1132}
1133
1135{
1136 if ( !d->geometry )
1137 {
1139 }
1140
1141 detach();
1142
1143 QTransform t = QTransform::fromTranslate( center.x(), center.y() );
1144 t.rotate( -rotation );
1145 t.translate( -center.x(), -center.y() );
1146 d->geometry->transform( t );
1148}
1149
1151 const QVector<QgsPointXY> &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QVector<QgsPointXY> &topologyTestPoints, bool splitFeature
1152)
1153{
1154 QgsPointSequence split, topology;
1155 convertPointList( splitLine, split );
1156 convertPointList( topologyTestPoints, topology );
1157 Qgis::GeometryOperationResult result = splitGeometry( split, newGeometries, topological, topology, splitFeature );
1158 convertPointList( topology, topologyTestPoints );
1159 return result;
1160}
1162 const QgsPointSequence &splitLine, QVector<QgsGeometry> &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature, bool skipIntersectionTest
1163)
1164{
1165 if ( !d->geometry )
1166 {
1168 }
1169
1170 // We're trying adding the split line's vertices to the geometry so that
1171 // snap to segment always produces a valid split (see https://github.com/qgis/QGIS/issues/29270)
1172 QgsGeometry tmpGeom( *this );
1173 for ( const QgsPoint &v : splitLine )
1174 {
1175 tmpGeom.addTopologicalPoint( v );
1176 }
1177
1178 QVector<QgsGeometry > newGeoms;
1179 QgsLineString splitLineString( splitLine );
1180
1181 QgsGeos geos( tmpGeom.get() );
1182 mLastError.clear();
1183 QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, topologyTestPoints, &mLastError, skipIntersectionTest );
1184
1185 if ( result == QgsGeometryEngine::Success )
1186 {
1187 if ( splitFeature )
1188 *this = newGeoms.takeAt( 0 );
1189 newGeometries = newGeoms;
1190 }
1191
1192 switch ( result )
1193 {
1208 //default: do not implement default to handle properly all cases
1209 }
1210
1211 // this should never be reached
1212 Q_ASSERT( false );
1214}
1215
1217 const QgsCurve *curve, QVector<QgsGeometry> &newGeometries, bool preserveCircular, bool topological, QgsPointSequence &topologyTestPoints, bool splitFeature
1218)
1219{
1220 std::unique_ptr<QgsLineString> segmentizedLine( curve->curveToLine() );
1221 QgsPointSequence points;
1222 segmentizedLine->points( points );
1223 Qgis::GeometryOperationResult result = splitGeometry( points, newGeometries, topological, topologyTestPoints, splitFeature );
1224
1226 {
1227 if ( preserveCircular )
1228 {
1229 for ( int i = 0; i < newGeometries.count(); ++i )
1230 newGeometries[i] = newGeometries[i].convertToCurves();
1231 *this = convertToCurves();
1232 }
1233 }
1234
1235 return result;
1236}
1237
1239{
1240 if ( !d->geometry )
1241 {
1243 }
1244
1245 // We're trying adding the reshape line's vertices to the geometry so that
1246 // snap to segment always produces a valid reshape
1247 QgsPointSequence reshapePoints;
1248 reshapeLineString.points( reshapePoints );
1249 QgsGeometry tmpGeom( *this );
1250 for ( const QgsPoint &v : std::as_const( reshapePoints ) )
1251 {
1252 tmpGeom.addTopologicalPoint( v );
1253 }
1254
1255 QgsGeos geos( tmpGeom.get() );
1257 mLastError.clear();
1258 std::unique_ptr< QgsAbstractGeometry > geom( geos.reshapeGeometry( reshapeLineString, &errorCode, &mLastError ) );
1259 if ( errorCode == QgsGeometryEngine::Success && geom )
1260 {
1261 reset( std::move( geom ) );
1263 }
1264
1265 switch ( errorCode )
1266 {
1277 case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
1281 }
1282
1283 // should not be reached
1285}
1286
1288{
1289 if ( !d->geometry || !other.d->geometry )
1290 {
1291 return 0;
1292 }
1293
1294 QgsGeos geos( d->geometry.get() );
1295
1296 mLastError.clear();
1297 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError, QgsGeometryParameters(), feedback ) );
1298 if ( !diffGeom )
1299 {
1300 return 1;
1301 }
1302
1303 reset( std::move( diffGeom ) );
1304 return 0;
1305}
1306
1308{
1309 if ( !d->geometry || other.isNull() )
1310 {
1311 return QgsGeometry();
1312 }
1313
1314 QgsGeos geos( d->geometry.get() );
1315
1316 mLastError.clear();
1317 std::unique_ptr< QgsAbstractGeometry > diffGeom( geos.intersection( other.constGet(), &mLastError, QgsGeometryParameters(), feedback ) );
1318 if ( !diffGeom )
1319 {
1320 QgsGeometry result;
1321 result.mLastError = mLastError;
1322 return result;
1323 }
1324
1325 return QgsGeometry( diffGeom.release() );
1326}
1327
1329{
1330 if ( d->geometry )
1331 {
1332 return d->geometry->boundingBox();
1333 }
1334 return QgsRectangle();
1335}
1336
1338{
1339 if ( d->geometry )
1340 {
1341 return d->geometry->boundingBox3D();
1342 }
1343 return QgsBox3D();
1344}
1345
1346
1347QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
1348{
1349 mLastError.clear();
1350
1351 if ( isNull() )
1352 return QgsGeometry();
1353
1354 if ( type() == Qgis::GeometryType::Point && d->geometry->partCount() == 1 )
1355 {
1356 area = 0;
1357 angle = 0;
1358 width = 0;
1359 height = 0;
1360 return QgsGeometry::fromRect( d->geometry->boundingBox() );
1361 }
1362
1363 QgsInternalGeometryEngine engine( *this );
1364 const QgsGeometry res = engine.orientedMinimumBoundingBox( area, angle, width, height );
1365 if ( res.isNull() )
1366 mLastError = engine.lastError();
1367 return res;
1368}
1369
1371{
1372 double area, angle, width, height;
1373 return orientedMinimumBoundingBox( area, angle, width, height );
1374}
1375
1376static QgsCircle __recMinimalEnclosingCircle( QgsMultiPointXY points, QgsMultiPointXY boundary )
1377{
1378 auto l_boundary = boundary.length();
1379 QgsCircle circ_mec;
1380 if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
1381 {
1382 switch ( l_boundary )
1383 {
1384 case 0:
1385 circ_mec = QgsCircle();
1386 break;
1387 case 1:
1388 circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
1389 boundary.pop_back();
1390 break;
1391 case 2:
1392 {
1393 QgsPointXY p1 = boundary.last();
1394 boundary.pop_back();
1395 QgsPointXY p2 = boundary.last();
1396 boundary.pop_back();
1397 circ_mec = QgsCircle::from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
1398 }
1399 break;
1400 default:
1401 QgsPoint p1( boundary.at( 0 ) );
1402 QgsPoint p2( boundary.at( 1 ) );
1403 QgsPoint p3( boundary.at( 2 ) );
1404 circ_mec = QgsCircle::minimalCircleFrom3Points( p1, p2, p3 );
1405 break;
1406 }
1407 return circ_mec;
1408 }
1409 else
1410 {
1411 QgsPointXY pxy = points.last();
1412 points.pop_back();
1413 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1414 QgsPoint p( pxy );
1415 if ( !circ_mec.contains( p ) )
1416 {
1417 boundary.append( pxy );
1418 circ_mec = __recMinimalEnclosingCircle( points, boundary );
1419 }
1420 }
1421 return circ_mec;
1422}
1423
1424QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY &center, double &radius, unsigned int segments ) const
1425{
1426 center = QgsPointXY();
1427 radius = 0;
1428
1429 if ( isEmpty() )
1430 {
1431 return QgsGeometry();
1432 }
1433
1434 /* optimization */
1435 QgsGeometry hull = convexHull();
1436 if ( hull.isNull() )
1437 return QgsGeometry();
1438
1439 QgsMultiPointXY P = hull.convertToPoint( true ).asMultiPoint();
1441
1442 QgsCircle circ = __recMinimalEnclosingCircle( P, R );
1443 center = QgsPointXY( circ.center() );
1444 radius = circ.radius();
1445 QgsGeometry geom;
1446 geom.set( circ.toPolygon( segments ) );
1447 return geom;
1448}
1449
1451{
1452 QgsPointXY center;
1453 double radius;
1454 return minimalEnclosingCircle( center, radius, segments );
1455}
1456
1457QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
1458{
1459 QgsInternalGeometryEngine engine( *this );
1460
1461 return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
1462}
1463
1464QgsGeometry QgsGeometry::triangularWaves( double wavelength, double amplitude, bool strictWavelength ) const
1465{
1466 QgsInternalGeometryEngine engine( *this );
1467 return engine.triangularWaves( wavelength, amplitude, strictWavelength );
1468}
1469
1470QgsGeometry QgsGeometry::triangularWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1471{
1472 QgsInternalGeometryEngine engine( *this );
1473 return engine.triangularWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1474}
1475
1476QgsGeometry QgsGeometry::squareWaves( double wavelength, double amplitude, bool strictWavelength ) const
1477{
1478 QgsInternalGeometryEngine engine( *this );
1479 return engine.squareWaves( wavelength, amplitude, strictWavelength );
1480}
1481
1482QgsGeometry QgsGeometry::squareWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1483{
1484 QgsInternalGeometryEngine engine( *this );
1485 return engine.squareWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1486}
1487
1488QgsGeometry QgsGeometry::roundWaves( double wavelength, double amplitude, bool strictWavelength ) const
1489{
1490 QgsInternalGeometryEngine engine( *this );
1491 return engine.roundWaves( wavelength, amplitude, strictWavelength );
1492}
1493
1494QgsGeometry QgsGeometry::roundWavesRandomized( double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed ) const
1495{
1496 QgsInternalGeometryEngine engine( *this );
1497 return engine.roundWavesRandomized( minimumWavelength, maximumWavelength, minimumAmplitude, maximumAmplitude, seed );
1498}
1499
1501 const QVector<double> &pattern, Qgis::DashPatternLineEndingRule startRule, Qgis::DashPatternLineEndingRule endRule, Qgis::DashPatternSizeAdjustment adjustment, double patternOffset
1502) const
1503{
1504 QgsInternalGeometryEngine engine( *this );
1505 return engine.applyDashPattern( pattern, startRule, endRule, adjustment, patternOffset );
1506}
1507
1508QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
1509{
1510 if ( !d->geometry )
1511 {
1512 return QgsGeometry();
1513 }
1514 return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
1515}
1516
1517bool QgsGeometry::removeDuplicateNodes( double epsilon, bool useZValues )
1518{
1519 if ( !d->geometry )
1520 return false;
1521
1522 detach();
1523 return d->geometry->removeDuplicateNodes( epsilon, useZValues );
1524}
1525
1527{
1528 // fast case, check bounding boxes
1529 if ( !boundingBoxIntersects( r ) )
1530 return false;
1531
1532 const Qgis::WkbType flatType { QgsWkbTypes::flatType( d->geometry->wkbType() ) };
1533 // optimise trivial case for point intersections -- the bounding box test has already given us the answer
1534 if ( flatType == Qgis::WkbType::Point )
1535 {
1536 return true;
1537 }
1538
1539#if ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12 )
1540 // Workaround for issue issue GH #51492
1541 // in case of multi polygon, intersection with an empty rect fails
1542 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1543 {
1544 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1545 return contains( QgsGeometry::fromPointXY( center ) );
1546 }
1547#endif
1548
1549 QgsGeometry g = fromRect( r );
1550 return intersects( g );
1551}
1552
1553bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1554{
1555 if ( !d->geometry || geometry.isNull() )
1556 {
1557 return false;
1558 }
1559
1560 QgsGeos geos( d->geometry.get() );
1561 mLastError.clear();
1562 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1563}
1564
1566{
1567 if ( !d->geometry )
1568 {
1569 return false;
1570 }
1571
1572 return d->geometry->boundingBoxIntersects( rectangle );
1573}
1574
1576{
1577 if ( !d->geometry || geometry.isNull() )
1578 {
1579 return false;
1580 }
1581
1582 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1583}
1584
1585bool QgsGeometry::contains( const QgsPointXY *p ) const
1586{
1587 if ( !d->geometry || !p )
1588 {
1589 return false;
1590 }
1591
1592 QgsGeos geos( d->geometry.get() );
1593 mLastError.clear();
1594 return geos.contains( p->x(), p->y(), &mLastError );
1595}
1596
1597bool QgsGeometry::contains( double x, double y ) const
1598{
1599 if ( !d->geometry )
1600 {
1601 return false;
1602 }
1603
1604 QgsGeos geos( d->geometry.get() );
1605 mLastError.clear();
1606 return geos.contains( x, y, &mLastError );
1607}
1608
1609bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1610{
1611 if ( !d->geometry || geometry.isNull() )
1612 {
1613 return false;
1614 }
1615
1616 QgsGeos geos( d->geometry.get() );
1617 mLastError.clear();
1618 return geos.contains( geometry.d->geometry.get(), &mLastError );
1619}
1620
1621bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1622{
1623 if ( !d->geometry || geometry.isNull() )
1624 {
1625 return false;
1626 }
1627
1628 QgsGeos geos( d->geometry.get() );
1629 mLastError.clear();
1630 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1631}
1632
1633bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1634{
1635 return isExactlyEqual( geometry );
1636}
1637
1638bool QgsGeometry::touches( const QgsGeometry &geometry ) const
1639{
1640 if ( !d->geometry || geometry.isNull() )
1641 {
1642 return false;
1643 }
1644
1645 QgsGeos geos( d->geometry.get() );
1646 mLastError.clear();
1647 return geos.touches( geometry.d->geometry.get(), &mLastError );
1648}
1649
1650bool QgsGeometry::overlaps( const QgsGeometry &geometry ) const
1651{
1652 if ( !d->geometry || geometry.isNull() )
1653 {
1654 return false;
1655 }
1656
1657 QgsGeos geos( d->geometry.get() );
1658 mLastError.clear();
1659 return geos.overlaps( geometry.d->geometry.get(), &mLastError );
1660}
1661
1662bool QgsGeometry::within( const QgsGeometry &geometry ) const
1663{
1664 if ( !d->geometry || geometry.isNull() )
1665 {
1666 return false;
1667 }
1668
1669 QgsGeos geos( d->geometry.get() );
1670 mLastError.clear();
1671 return geos.within( geometry.d->geometry.get(), &mLastError );
1672}
1673
1674bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1675{
1676 if ( !d->geometry || geometry.isNull() )
1677 {
1678 return false;
1679 }
1680
1681 QgsGeos geos( d->geometry.get() );
1682 mLastError.clear();
1683 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1684}
1685
1686QString QgsGeometry::asWkt( int precision ) const
1687{
1688 if ( !d->geometry )
1689 {
1690 return QString();
1691 }
1692 return d->geometry->asWkt( precision );
1693}
1694
1695QString QgsGeometry::asJson( int precision ) const
1696{
1697 return QString::fromStdString( asJsonObject( precision ).dump() );
1698}
1699
1700json QgsGeometry::asJsonObject( int precision ) const
1701{
1702 if ( !d->geometry )
1703 {
1704 return nullptr;
1705 }
1706 return d->geometry->asJsonObject( precision );
1707}
1708
1709QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM, bool avoidDuplicates ) const
1710{
1711 mLastError.clear();
1712 QVector< QgsGeometry > res;
1713 if ( isNull() )
1714 return res;
1715
1716 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1717 {
1718 res << *this;
1719 return res;
1720 }
1721
1723 {
1724 return res;
1725 }
1726
1727 QgsGeometry newGeom = *this;
1728
1729 // Curved -> straight
1731 {
1732 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1733 }
1734
1735 // Handle NurbsCurve: if target is curved but NOT NurbsCurve, and source contains NurbsCurve,
1736 // we need to segmentize the NURBS parts first
1738 {
1739 // Check if geometry contains NurbsCurve that needs conversion
1740 bool hasNurbs = false;
1741 if ( QgsWkbTypes::isNurbsType( newGeom.wkbType() ) )
1742 {
1743 hasNurbs = true;
1744 }
1745 else if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( newGeom.constGet() ) )
1746 {
1747 for ( int i = 0; i < collection->numGeometries(); ++i )
1748 {
1749 if ( QgsWkbTypes::isNurbsType( collection->geometryN( i )->wkbType() ) )
1750 {
1751 hasNurbs = true;
1752 break;
1753 }
1754 }
1755 }
1756 else if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( newGeom.constGet() ) )
1757 {
1758 if ( cp->exteriorRing() && QgsWkbTypes::isNurbsType( cp->exteriorRing()->wkbType() ) )
1759 hasNurbs = true;
1760 for ( int i = 0; !hasNurbs && i < cp->numInteriorRings(); ++i )
1761 {
1762 if ( QgsWkbTypes::isNurbsType( cp->interiorRing( i )->wkbType() ) )
1763 hasNurbs = true;
1764 }
1765 }
1767 {
1768 for ( int i = 0; i < cc->nCurves(); ++i )
1769 {
1770 if ( QgsWkbTypes::isNurbsType( cc->curveAt( i )->wkbType() ) )
1771 {
1772 hasNurbs = true;
1773 break;
1774 }
1775 }
1776 }
1777
1778 if ( hasNurbs )
1779 {
1780 // Segmentize to remove NURBS, then we'll convert back to curve type below
1781 newGeom = QgsGeometry( newGeom.constGet()->segmentize() );
1782 }
1783 }
1784
1785 // polygon -> line
1787 {
1788 // boundary gives us a (multi)line string of exterior + interior rings
1789 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1790 }
1791 // line -> polygon
1793 {
1794 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1795 const QgsGeometry source = newGeom;
1796 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1797 {
1798 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1799 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1800 {
1802 {
1803 auto cp = std::make_unique< QgsCurvePolygon >();
1804 cp->setExteriorRing( curve );
1805 ( void ) exterior.release();
1806 gc->addGeometry( cp.release() );
1807 }
1808 else
1809 {
1810 auto p = std::make_unique< QgsPolygon >();
1811 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1812 ( void ) exterior.release();
1813 gc->addGeometry( p.release() );
1814 }
1815 }
1816 }
1817 newGeom = QgsGeometry( std::move( gc ) );
1818 }
1819
1820 // line/polygon -> points
1822 {
1823 // lines/polygons to a point layer, extract all vertices
1824 auto mp = std::make_unique< QgsMultiPoint >();
1825 const QgsGeometry source = newGeom;
1826 QSet< QgsPoint > added;
1827 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1828 {
1829 if ( avoidDuplicates && added.contains( *vertex ) )
1830 continue; // avoid duplicate points, e.g. start/end of rings
1831 mp->addGeometry( ( *vertex ).clone() );
1832 added.insert( *vertex );
1833 }
1834 newGeom = QgsGeometry( std::move( mp ) );
1835 }
1836
1837 //(Multi)Polygon to PolyhedralSurface
1839 {
1840 auto polySurface = std::make_unique< QgsPolyhedralSurface >();
1841 const QgsGeometry source = newGeom;
1842 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1843 {
1844 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1845 {
1846 polySurface->addPatch( polygon->clone() );
1847 }
1848 }
1849 newGeom = QgsGeometry( std::move( polySurface ) );
1850 }
1851
1852 //(Multi)Polygon/Triangle to TIN
1855 {
1856 auto tin = std::make_unique< QgsTriangulatedSurface >();
1857 const QgsGeometry source = newGeom;
1858 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1859 {
1860 if ( const QgsTriangle *triangle = qgsgeometry_cast< const QgsTriangle * >( *part ) )
1861 {
1862 tin->addPatch( triangle->clone() );
1863 }
1864 else if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1865 {
1866 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
1867 if ( polygon->exteriorRing() )
1868 {
1869 const int numPoints = polygon->exteriorRing()->numPoints();
1870 if ( numPoints != 4 )
1871 {
1872 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
1873 return res;
1874 }
1875 auto triangle = std::make_unique< QgsTriangle >();
1876 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
1877 tin->addPatch( triangle.release() );
1878 }
1879 }
1880 }
1881 newGeom = QgsGeometry( std::move( tin ) );
1882 }
1883
1884 // PolyhedralSurface/TIN to (Multi)Polygon
1887 {
1888 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
1890 {
1891 for ( int i = 0; i < polySurface->numPatches(); ++i )
1892 {
1893 const QgsPolygon *patch = polySurface->patchN( i );
1894 auto polygon = std::make_unique< QgsPolygon >();
1895 polygon->setExteriorRing( patch->exteriorRing()->clone() );
1896 for ( int j = 0; j < patch->numInteriorRings(); ++j )
1897 {
1898 polygon->addInteriorRing( patch->interiorRing( j )->clone() );
1899 }
1900 multiPolygon->addGeometry( polygon.release() );
1901 }
1902 }
1903 newGeom = QgsGeometry( std::move( multiPolygon ) );
1904 }
1905
1906 // Polygon -> Triangle
1908 {
1909 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( newGeom.constGet() ) )
1910 {
1911 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
1912 if ( polygon->exteriorRing() )
1913 {
1914 const int numPoints = polygon->exteriorRing()->numPoints();
1915 if ( numPoints != 4 )
1916 {
1917 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
1918 return res;
1919 }
1920 auto triangle = std::make_unique< QgsTriangle >();
1921 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
1922 newGeom = QgsGeometry( std::move( triangle ) );
1923 }
1924 }
1925 }
1926
1927
1928 // Single -> multi
1929 if ( QgsWkbTypes::isMultiType( type ) && !newGeom.isMultipart() )
1930 {
1931 newGeom.convertToMultiType();
1932 }
1933 // Drop Z/M
1934 if ( newGeom.constGet()->is3D() && !QgsWkbTypes::hasZ( type ) )
1935 {
1936 newGeom.get()->dropZValue();
1937 }
1938 if ( newGeom.constGet()->isMeasure() && !QgsWkbTypes::hasM( type ) )
1939 {
1940 newGeom.get()->dropMValue();
1941 }
1942 // Add Z/M back, set to 0
1943 if ( !newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
1944 {
1945 newGeom.get()->addZValue( defaultZ );
1946 }
1947 if ( !newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
1948 {
1949 newGeom.get()->addMValue( defaultM );
1950 }
1951
1952 // Straight -> curve
1954 {
1955 newGeom.convertToCurvedMultiType();
1956 }
1957
1958 // Multi -> single
1959 if ( !QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart() )
1960 {
1961 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1962 res.reserve( parts->partCount() );
1963 for ( int i = 0; i < parts->partCount(); i++ )
1964 {
1965 res << QgsGeometry( parts->geometryN( i )->clone() );
1966 }
1967 }
1968 // GeometryCollection (of Point/LineString/Polygon) -> MultiPoint/MultiLineString/MultiPolygon
1970 {
1972 const QgsGeometryCollection *geomColl( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1973
1974 bool allExpectedType = true;
1975 for ( int i = 0; i < geomColl->numGeometries(); ++i )
1976 {
1977 if ( geomColl->geometryN( i )->wkbType() != singleType )
1978 {
1979 allExpectedType = false;
1980 break;
1981 }
1982 }
1983 if ( allExpectedType )
1984 {
1985 std::unique_ptr< QgsGeometryCollection > newGeomCol;
1987 {
1988 newGeomCol = std::make_unique< QgsMultiPoint >();
1989 }
1991 {
1992 newGeomCol = std::make_unique< QgsMultiLineString >();
1993 }
1994 else
1995 {
1996 newGeomCol = std::make_unique< QgsMultiPolygon >();
1997 }
1998 newGeomCol->reserve( geomColl->numGeometries() );
1999 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2000 {
2001 newGeomCol->addGeometry( geomColl->geometryN( i )->clone() );
2002 }
2003 res << QgsGeometry( std::move( newGeomCol ) );
2004 }
2005 else
2006 {
2007 res << newGeom;
2008 }
2009 }
2010 else
2011 {
2012 res << newGeom;
2013 }
2014 return res;
2015}
2016
2017QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
2018{
2019 switch ( destType )
2020 {
2022 return convertToPoint( destMultipart );
2023
2025 return convertToLine( destMultipart );
2026
2028 return convertToPolygon( destMultipart );
2029
2030 default:
2031 return QgsGeometry();
2032 }
2033}
2034
2036{
2037 if ( !d->geometry )
2038 {
2039 return false;
2040 }
2041
2042 if ( isMultipart() ) //already multitype, no need to convert
2043 {
2044 return true;
2045 }
2046
2047 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
2049 if ( !multiGeom )
2050 {
2051 return false;
2052 }
2053
2054 //try to avoid cloning existing geometry whenever we can
2055
2056 //want to see a magic trick?... gather round kiddies...
2057 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
2058 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
2059 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
2060 multiGeom->addGeometry( d->geometry.release() );
2061 // and replace it with the multi geometry.
2062 // TADA! a clone free conversion in some cases
2063 d->geometry = std::move( geom );
2064 return true;
2065}
2066
2068{
2069 if ( !d->geometry )
2070 {
2071 return false;
2072 }
2073
2074 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
2075 {
2080 {
2081 return true;
2082 }
2083 default:
2084 break;
2085 }
2086
2087 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
2089 if ( !multiGeom )
2090 {
2091 return false;
2092 }
2093
2094 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2095 if ( sourceMultiGeom )
2096 {
2097 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
2098 {
2099 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
2100 return false;
2101 }
2102 }
2103 else
2104 {
2105 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
2106 return false;
2107 }
2108
2109 reset( std::move( geom ) );
2110 return true;
2111}
2112
2114{
2115 if ( !d->geometry )
2116 {
2117 return false;
2118 }
2119
2120 if ( !isMultipart() ) //already single part, no need to convert
2121 {
2122 return true;
2123 }
2124
2125 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2126 if ( !multiGeom || multiGeom->partCount() < 1 )
2127 return false;
2128
2129 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
2130 reset( std::move( firstPart ) );
2131 return true;
2132}
2133
2134
2136{
2138 if ( !origGeom )
2139 return false;
2140
2141 std::unique_ptr<QgsGeometryCollection> resGeom;
2142 switch ( geomType )
2143 {
2145 resGeom = std::make_unique<QgsMultiPoint>();
2146 break;
2148 resGeom = std::make_unique<QgsMultiLineString>();
2149 break;
2151 resGeom = std::make_unique<QgsMultiPolygon>();
2152 break;
2153 default:
2154 break;
2155 }
2156 if ( !resGeom )
2157 return false;
2158
2159 resGeom->reserve( origGeom->numGeometries() );
2160 for ( int i = 0; i < origGeom->numGeometries(); ++i )
2161 {
2162 const QgsAbstractGeometry *g = origGeom->geometryN( i );
2163 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
2164 resGeom->addGeometry( g->clone() );
2165 }
2166
2167 set( resGeom.release() );
2168 return true;
2169}
2170
2171
2173{
2174 if ( !d->geometry )
2175 {
2176 return QgsPointXY();
2177 }
2178 if ( const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
2179 {
2180 return QgsPointXY( pt->x(), pt->y() );
2181 }
2182 else
2183 {
2184 return QgsPointXY();
2185 }
2186}
2187
2189{
2190 QgsPolylineXY polyLine;
2191 if ( !d->geometry )
2192 {
2193 return polyLine;
2194 }
2195
2196 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve || QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CircularString );
2197 std::unique_ptr< QgsLineString > segmentizedLine;
2198 QgsLineString *line = nullptr;
2199 if ( doSegmentation )
2200 {
2201 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
2202 if ( !curve )
2203 {
2204 return polyLine;
2205 }
2206 segmentizedLine.reset( curve->curveToLine() );
2207 line = segmentizedLine.get();
2208 }
2209 else
2210 {
2211 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
2212 if ( !line )
2213 {
2214 return polyLine;
2215 }
2216 }
2217
2218 int nVertices = line->numPoints();
2219 polyLine.resize( nVertices );
2220 QgsPointXY *data = polyLine.data();
2221 const double *xData = line->xData();
2222 const double *yData = line->yData();
2223 for ( int i = 0; i < nVertices; ++i )
2224 {
2225 data->setX( *xData++ );
2226 data->setY( *yData++ );
2227 data++;
2228 }
2229
2230 return polyLine;
2231}
2232
2234{
2235 if ( !d->geometry )
2236 return QgsPolygonXY();
2237
2238 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
2239
2240 QgsPolygon *p = nullptr;
2241 std::unique_ptr< QgsPolygon > segmentized;
2242 if ( doSegmentation )
2243 {
2244 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
2245 if ( !curvePoly )
2246 {
2247 return QgsPolygonXY();
2248 }
2249 segmentized.reset( curvePoly->toPolygon() );
2250 p = segmentized.get();
2251 }
2252 else
2253 {
2254 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
2255 }
2256
2257 if ( !p )
2258 {
2259 return QgsPolygonXY();
2260 }
2261
2262 QgsPolygonXY polygon;
2263 convertPolygon( *p, polygon );
2264
2265 return polygon;
2266}
2267
2269{
2270 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
2271 {
2272 return QgsMultiPointXY();
2273 }
2274
2275 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
2276 if ( !mp )
2277 {
2278 return QgsMultiPointXY();
2279 }
2280
2281 int nPoints = mp->numGeometries();
2282 QgsMultiPointXY multiPoint( nPoints );
2283 for ( int i = 0; i < nPoints; ++i )
2284 {
2285 const QgsPoint *pt = mp->pointN( i );
2286 multiPoint[i].setX( pt->x() );
2287 multiPoint[i].setY( pt->y() );
2288 }
2289 return multiPoint;
2290}
2291
2293{
2294 if ( !d->geometry )
2295 {
2296 return QgsMultiPolylineXY();
2297 }
2298
2299 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2300 if ( !geomCollection )
2301 {
2302 return QgsMultiPolylineXY();
2303 }
2304
2305 int nLines = geomCollection->numGeometries();
2306 if ( nLines < 1 )
2307 {
2308 return QgsMultiPolylineXY();
2309 }
2310
2312 mpl.reserve( nLines );
2313 for ( int i = 0; i < nLines; ++i )
2314 {
2315 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2316 std::unique_ptr< QgsLineString > segmentized;
2317 if ( !line )
2318 {
2319 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2320 if ( !curve )
2321 {
2322 continue;
2323 }
2324 segmentized.reset( curve->curveToLine() );
2325 line = segmentized.get();
2326 }
2327
2328 QgsPolylineXY polyLine;
2329 int nVertices = line->numPoints();
2330 polyLine.resize( nVertices );
2331 QgsPointXY *data = polyLine.data();
2332 const double *xData = line->xData();
2333 const double *yData = line->yData();
2334 for ( int i = 0; i < nVertices; ++i )
2335 {
2336 data->setX( *xData++ );
2337 data->setY( *yData++ );
2338 data++;
2339 }
2340 mpl.append( polyLine );
2341 }
2342 return mpl;
2343}
2344
2346{
2347 if ( !d->geometry )
2348 {
2349 return QgsMultiPolygonXY();
2350 }
2351
2352 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2353 if ( !geomCollection )
2354 {
2355 return QgsMultiPolygonXY();
2356 }
2357
2358 const int nPolygons = geomCollection->numGeometries();
2359 if ( nPolygons < 1 )
2360 {
2361 return QgsMultiPolygonXY();
2362 }
2363
2365 mp.reserve( nPolygons );
2366 for ( int i = 0; i < nPolygons; ++i )
2367 {
2368 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2369 if ( !polygon )
2370 {
2371 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2372 if ( cPolygon )
2373 {
2374 polygon = cPolygon->toPolygon();
2375 }
2376 else
2377 {
2378 continue;
2379 }
2380 }
2381
2382 QgsPolygonXY poly;
2383 convertPolygon( *polygon, poly );
2384 mp.push_back( poly );
2385 }
2386 return mp;
2387}
2388
2389double QgsGeometry::area() const
2390{
2391 if ( !d->geometry )
2392 {
2393 return -1.0;
2394 }
2395
2396 return d->geometry->area();
2397}
2398
2400{
2401 if ( !d->geometry )
2402 {
2403 throw QgsInvalidArgumentException( "Cannot compute 3D area: geometry is null." );
2404 }
2405
2406 return d->geometry->area3D();
2407}
2408
2410{
2411 if ( !d->geometry )
2412 {
2413 return -1.0;
2414 }
2415
2416 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2417 {
2419 return 0.0;
2420
2422 return d->geometry->length();
2423
2425 return d->geometry->perimeter();
2426
2429 return d->geometry->length();
2430 }
2431 return -1;
2432}
2433
2434double QgsGeometry::distance( const QgsGeometry &geom ) const
2435{
2436 if ( !d->geometry || !geom.d->geometry )
2437 {
2438 return -1.0;
2439 }
2440
2441 // avoid calling geos for trivial point-to-point distance calculations
2442 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( geom.wkbType() ) == Qgis::WkbType::Point )
2443 {
2444 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2445 }
2446
2447 QgsGeos g( d->geometry.get() );
2448 mLastError.clear();
2449 return g.distance( geom.d->geometry.get(), &mLastError );
2450}
2451
2453{
2454 if ( !d->geometry || !geom.d->geometry )
2455 {
2456 return -1.0;
2457 }
2458
2459 QgsGeos g( d->geometry.get() );
2460 mLastError.clear();
2461 return g.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2462}
2463
2464double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2465{
2466 if ( !d->geometry || !geom.d->geometry )
2467 {
2468 return -1.0;
2469 }
2470
2471 QgsGeos g( d->geometry.get() );
2472 mLastError.clear();
2473 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2474}
2475
2476
2478{
2479 if ( !d->geometry || !geom.d->geometry )
2480 {
2481 return -1.0;
2482 }
2483
2484 QgsGeos g( d->geometry.get() );
2485 mLastError.clear();
2486 return g.frechetDistance( geom.d->geometry.get(), &mLastError );
2487}
2488
2489double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2490{
2491 if ( !d->geometry || !geom.d->geometry )
2492 {
2493 return -1.0;
2494 }
2495
2496 QgsGeos g( d->geometry.get() );
2497 mLastError.clear();
2498 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2499}
2500
2502{
2503 if ( !d->geometry || d->geometry.get()->isEmpty() )
2505 return d->geometry->vertices_begin();
2506}
2507
2509{
2510 if ( !d->geometry || d->geometry.get()->isEmpty() )
2512 return d->geometry->vertices_end();
2513}
2514
2516{
2517 if ( !d->geometry || d->geometry.get()->isEmpty() )
2518 return QgsVertexIterator();
2519 return QgsVertexIterator( d->geometry.get() );
2520}
2521
2523{
2524 if ( !d->geometry )
2526
2527 detach();
2528 return d->geometry->parts_begin();
2529}
2530
2532{
2533 if ( !d->geometry )
2535 return d->geometry->parts_end();
2536}
2537
2539{
2540 if ( !d->geometry )
2542 return d->geometry->const_parts_begin();
2543}
2544
2546{
2547 if ( !d->geometry )
2549 return d->geometry->const_parts_end();
2550}
2551
2553{
2554 if ( !d->geometry )
2555 return QgsGeometryPartIterator();
2556
2557 detach();
2558 return QgsGeometryPartIterator( d->geometry.get() );
2559}
2560
2562{
2563 if ( !d->geometry )
2565
2566 return QgsGeometryConstPartIterator( d->geometry.get() );
2567}
2568
2569QgsGeometry QgsGeometry::buffer( double distance, int segments, QgsFeedback *feedback ) const
2570{
2571 if ( !d->geometry )
2572 {
2573 return QgsGeometry();
2574 }
2575
2576 QgsGeos g( d->geometry.get() );
2577 mLastError.clear();
2578 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError, feedback ) );
2579 if ( !geom )
2580 {
2581 QgsGeometry result;
2582 result.mLastError = mLastError;
2583 return result;
2584 }
2585 return QgsGeometry( std::move( geom ) );
2586}
2587
2588QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit, QgsFeedback *feedback ) const
2589{
2590 if ( !d->geometry )
2591 {
2592 return QgsGeometry();
2593 }
2594
2595 QgsGeos g( d->geometry.get() );
2596 mLastError.clear();
2597 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError, feedback );
2598 if ( !geom )
2599 {
2600 QgsGeometry result;
2601 result.mLastError = mLastError;
2602 return result;
2603 }
2604 return QgsGeometry( geom );
2605}
2606
2607QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2608{
2609 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2610 {
2611 return QgsGeometry();
2612 }
2613
2614 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2615 {
2616 const QVector<QgsGeometry> parts = asGeometryCollection();
2617 QVector<QgsGeometry> results;
2618 results.reserve( parts.count() );
2619 for ( const QgsGeometry &part : parts )
2620 {
2621 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2622 if ( !result.isNull() )
2623 results << result;
2624 }
2625 if ( results.isEmpty() )
2626 return QgsGeometry();
2627
2628 QgsGeometry first = results.takeAt( 0 );
2629 for ( const QgsGeometry &result : std::as_const( results ) )
2630 {
2631 first.addPart( result );
2632 }
2633 return first;
2634 }
2635 else
2636 {
2637 QgsGeos geos( d->geometry.get() );
2638 mLastError.clear();
2639
2640 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2641 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2642
2643 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2644 if ( !offsetGeom )
2645 {
2646 QgsGeometry result;
2647 result.mLastError = mLastError;
2648 return result;
2649 }
2650
2651 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2652 {
2653 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2654 if ( newOrientation != prevOrientation )
2655 {
2656 // GEOS has flipped line orientation, flip it back
2657 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2658 offsetGeom = std::move( flipped );
2659 }
2660 }
2661 return QgsGeometry( std::move( offsetGeom ) );
2662 }
2663}
2664
2665QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2666{
2667 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2668 {
2669 return QgsGeometry();
2670 }
2671
2672 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2673 {
2674 const QVector<QgsGeometry> parts = asGeometryCollection();
2675 QVector<QgsGeometry> results;
2676 results.reserve( parts.count() );
2677 for ( const QgsGeometry &part : parts )
2678 {
2679 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2680 if ( !result.isNull() )
2681 results << result;
2682 }
2683 if ( results.isEmpty() )
2684 return QgsGeometry();
2685
2686 QgsGeometry first = results.takeAt( 0 );
2687 for ( const QgsGeometry &result : std::as_const( results ) )
2688 {
2689 first.addPart( result );
2690 }
2691 return first;
2692 }
2693 else
2694 {
2695 QgsGeos geos( d->geometry.get() );
2696 mLastError.clear();
2697 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit, &mLastError );
2698 if ( !bufferGeom )
2699 {
2700 QgsGeometry result;
2701 result.mLastError = mLastError;
2702 return result;
2703 }
2704 return QgsGeometry( std::move( bufferGeom ) );
2705 }
2706}
2707
2708QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2709{
2710 QgsInternalGeometryEngine engine( *this );
2711
2712 return engine.taperedBuffer( startWidth, endWidth, segments );
2713}
2714
2716{
2717 QgsInternalGeometryEngine engine( *this );
2718
2719 return engine.variableWidthBufferByM( segments );
2720}
2721
2722QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2723{
2724 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2725 {
2726 return QgsGeometry();
2727 }
2728
2729 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2730 {
2731 const QVector<QgsGeometry> parts = asGeometryCollection();
2732 QVector<QgsGeometry> results;
2733 results.reserve( parts.count() );
2734 for ( const QgsGeometry &part : parts )
2735 {
2736 QgsGeometry result = part.extendLine( startDistance, endDistance );
2737 if ( !result.isNull() )
2738 results << result;
2739 }
2740 if ( results.isEmpty() )
2741 return QgsGeometry();
2742
2743 QgsGeometry first = results.takeAt( 0 );
2744 for ( const QgsGeometry &result : std::as_const( results ) )
2745 {
2746 first.addPart( result );
2747 }
2748 return first;
2749 }
2750 else
2751 {
2752 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2753 if ( !line )
2754 return QgsGeometry();
2755
2756 std::unique_ptr< QgsLineString > newLine( line->clone() );
2757 newLine->extend( startDistance, endDistance );
2758 return QgsGeometry( std::move( newLine ) );
2759 }
2760}
2761
2762QgsGeometry QgsGeometry::simplify( double tolerance, QgsFeedback *feedback ) const
2763{
2764 if ( !d->geometry )
2765 {
2766 return QgsGeometry();
2767 }
2768
2769 QgsGeos geos( d->geometry.get() );
2770 mLastError.clear();
2771 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError, feedback ) );
2772 if ( !simplifiedGeom )
2773 {
2774 QgsGeometry result;
2775 result.mLastError = mLastError;
2776 return result;
2777 }
2778 return QgsGeometry( std::move( simplifiedGeom ) );
2779}
2780
2781QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2782{
2783 QgsInternalGeometryEngine engine( *this );
2784
2785 return engine.densifyByCount( extraNodesPerSegment );
2786}
2787
2789{
2790 QgsInternalGeometryEngine engine( *this );
2791
2792 return engine.densifyByDistance( distance );
2793}
2794
2795QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2796{
2797 QgsInternalGeometryEngine engine( *this );
2798
2799 return engine.convertToCurves( distanceTolerance, angleTolerance );
2800}
2801
2803{
2804 if ( !d->geometry )
2805 {
2806 return QgsGeometry();
2807 }
2808
2809 // avoid calling geos for trivial point centroids
2810 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
2811 {
2812 QgsGeometry c = *this;
2813 c.get()->dropZValue();
2814 c.get()->dropMValue();
2815 return c;
2816 }
2817
2818 QgsGeos geos( d->geometry.get() );
2819
2820 mLastError.clear();
2821 QgsGeometry result( geos.centroid( &mLastError ) );
2822 result.mLastError = mLastError;
2823 return result;
2824}
2825
2827{
2828 if ( !d->geometry )
2829 {
2830 return QgsGeometry();
2831 }
2832
2833 QgsGeos geos( d->geometry.get() );
2834
2835 mLastError.clear();
2836 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2837 result.mLastError = mLastError;
2838 return result;
2839}
2840
2841QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2842{
2843 QgsInternalGeometryEngine engine( *this );
2844
2845 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2846}
2847
2848QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2849{
2850 if ( !d->geometry )
2851 {
2852 return QgsGeometry();
2853 }
2854
2855 QgsGeos geos( d->geometry.get() );
2856
2857 mLastError.clear();
2858 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2859 result.mLastError = mLastError;
2860 return result;
2861}
2862
2864{
2865 if ( !d->geometry )
2866 {
2867 return QgsGeometry();
2868 }
2869
2870 QgsGeos geos( d->geometry.get() );
2871
2872 mLastError.clear();
2873 QgsGeometry result( geos.minimumWidth( &mLastError ) );
2874 result.mLastError = mLastError;
2875 return result;
2876}
2877
2879{
2880 if ( !d->geometry )
2881 {
2882 return std::numeric_limits< double >::quiet_NaN();
2883 }
2884
2885 QgsGeos geos( d->geometry.get() );
2886
2887 mLastError.clear();
2888 return geos.minimumClearance( &mLastError );
2889}
2890
2892{
2893 if ( !d->geometry )
2894 {
2895 return QgsGeometry();
2896 }
2897
2898 QgsGeos geos( d->geometry.get() );
2899
2900 mLastError.clear();
2901 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
2902 result.mLastError = mLastError;
2903 return result;
2904}
2905
2907{
2908 if ( !d->geometry )
2909 {
2910 return QgsGeometry();
2911 }
2912 QgsGeos geos( d->geometry.get() );
2913 mLastError.clear();
2914 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
2915 if ( !cHull )
2916 {
2917 QgsGeometry geom;
2918 geom.mLastError = mLastError;
2919 return geom;
2920 }
2921 return QgsGeometry( std::move( cHull ) );
2922}
2923
2924QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles, QgsFeedback *feedback ) const
2925{
2926 if ( !d->geometry )
2927 {
2928 return QgsGeometry();
2929 }
2930 QgsGeos geos( d->geometry.get() );
2931 mLastError.clear();
2932 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError, feedback ) );
2933 if ( !concaveHull )
2934 {
2935 QgsGeometry geom;
2936 geom.mLastError = mLastError;
2937 return geom;
2938 }
2939 return QgsGeometry( std::move( concaveHull ) );
2940}
2941
2942QgsGeometry QgsGeometry::concaveHullOfPolygons( double lengthRatio, bool allowHoles, bool isTight, QgsFeedback *feedback ) const
2943{
2944 if ( !d->geometry )
2945 {
2946 return QgsGeometry();
2947 }
2948
2950 {
2951 QgsGeometry geom;
2952 geom.mLastError = u"Only Polygon or MultiPolygon geometries are supported"_s;
2953 return geom;
2954 }
2955
2956 QgsGeos geos( d->geometry.get() );
2957 mLastError.clear();
2958 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHullOfPolygons( lengthRatio, allowHoles, isTight, &mLastError, feedback ) );
2959 if ( !concaveHull )
2960 {
2961 QgsGeometry geom;
2962 geom.mLastError = mLastError;
2963 return geom;
2964 }
2965 return QgsGeometry( std::move( concaveHull ) );
2966}
2967
2968QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
2969{
2970 if ( !d->geometry )
2971 {
2972 return QgsGeometry();
2973 }
2974
2975 QgsGeos geos( d->geometry.get() );
2976 mLastError.clear();
2977 QgsGeometry result = QgsGeometry( geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError ) );
2978 result.mLastError = mLastError;
2979 return result;
2980}
2981
2982QgsGeometry QgsGeometry::delaunayTriangulation( double tolerance, bool edgesOnly ) const
2983{
2984 if ( !d->geometry )
2985 {
2986 return QgsGeometry();
2987 }
2988
2989 QgsGeos geos( d->geometry.get() );
2990 mLastError.clear();
2991 QgsGeometry result = QgsGeometry( geos.delaunayTriangulation( tolerance, edgesOnly ) );
2992 result.mLastError = mLastError;
2993 return result;
2994}
2995
2997{
2998 if ( !d->geometry )
2999 {
3000 return QgsGeometry();
3001 }
3002
3003 QgsGeos geos( d->geometry.get() );
3004 mLastError.clear();
3005 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
3006 result.mLastError = mLastError;
3007 return result;
3008}
3009
3011{
3012 if ( !d->geometry )
3013 {
3014 return QgsGeometry();
3015 }
3016
3017 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::GeometryCollection
3018 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPolygon
3019 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::Polygon )
3020 return QgsGeometry();
3021
3022 QgsGeos geos( d->geometry.get() );
3023 mLastError.clear();
3024 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
3025 result.mLastError = mLastError;
3026 return result;
3027}
3028
3030{
3031 if ( !d->geometry )
3032 {
3034 }
3035
3036 QgsGeos geos( d->geometry.get() );
3037 mLastError.clear();
3038 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
3039
3040 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
3041
3042 if ( invalidEdges && invalidEdgesGeom )
3043 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
3044
3045 return result;
3046}
3047
3048QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
3049{
3050 if ( !d->geometry )
3051 {
3052 return QgsGeometry();
3053 }
3054
3055 QgsGeos geos( d->geometry.get() );
3056 mLastError.clear();
3057 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
3058 result.mLastError = mLastError;
3059 return result;
3060}
3061
3063{
3064 if ( !d->geometry )
3065 {
3066 return QgsGeometry();
3067 }
3068
3069 QgsGeos geos( d->geometry.get() );
3070 mLastError.clear();
3071 QgsGeometry result( geos.node( &mLastError ) );
3072 result.mLastError = mLastError;
3073 return result;
3074}
3075
3077{
3078 if ( !d->geometry )
3079 {
3080 return QgsGeometry();
3081 }
3082
3083 QgsGeos geos( d->geometry.get() );
3084 mLastError.clear();
3085 QgsGeometry result( geos.sharedPaths( other.constGet(), &mLastError ) );
3086 result.mLastError = mLastError;
3087 return result;
3088}
3089
3090QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3091{
3092 if ( !d->geometry )
3093 {
3094 return QgsGeometry();
3095 }
3096
3097 const QgsAbstractGeometry *geom = d->geometry.get();
3098 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
3099 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
3100 {
3101 segmentizedCopy.reset( d->geometry->segmentize() );
3102 geom = segmentizedCopy.get();
3103 }
3104
3105 QgsGeos geos( geom );
3106 mLastError.clear();
3107 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters, feedback ) );
3108 if ( !result )
3109 {
3110 QgsGeometry geom;
3111 geom.mLastError = mLastError;
3112 return geom;
3113 }
3114 return QgsGeometry( std::move( result ) );
3115}
3116
3118{
3119 if ( !d->geometry )
3120 {
3121 return QgsGeometry();
3122 }
3123
3124 QgsGeometry line = *this;
3126 return QgsGeometry();
3127 else if ( type() == Qgis::GeometryType::Polygon )
3128 {
3129 line = QgsGeometry( d->geometry->boundary() );
3130 }
3131
3132 const QgsCurve *curve = nullptr;
3134 {
3135 // if multi part, iterate through parts to find target part
3136 for ( int part = 0; part < collection->numGeometries(); ++part )
3137 {
3138 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
3139 if ( !candidate )
3140 continue;
3141 const double candidateLength = candidate->length();
3142 if ( candidateLength >= distance )
3143 {
3144 curve = candidate;
3145 break;
3146 }
3147
3148 distance -= candidateLength;
3149 }
3150 }
3151 else
3152 {
3154 }
3155 if ( !curve )
3156 return QgsGeometry();
3157
3158 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
3159 if ( !result )
3160 {
3161 return QgsGeometry();
3162 }
3163 return QgsGeometry( std::move( result ) );
3164}
3165
3166double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
3167{
3168 if ( type() != Qgis::GeometryType::Line )
3169 return -1;
3170
3172 return -1;
3173
3174 QgsGeometry segmentized = *this;
3176 {
3177 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
3178 }
3179
3180 QgsGeos geos( d->geometry.get() );
3181 mLastError.clear();
3182 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
3183}
3184
3186{
3187 if ( !d->geometry || d->geometry->isEmpty() )
3188 return 0.0;
3189
3190 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
3192 return 0.0;
3193
3194 // always operate on segmentized geometries
3195 QgsGeometry segmentized = *this;
3196 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
3197 {
3198 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
3199 }
3200
3201 QgsVertexId previous;
3202 QgsVertexId next;
3203 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
3204 return 0.0;
3205
3206 if ( previous == next )
3207 {
3208 // distance coincided exactly with a vertex
3209 QgsVertexId v2 = previous;
3210 QgsVertexId v1;
3211 QgsVertexId v3;
3212 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
3213 if ( v1.isValid() && v3.isValid() )
3214 {
3215 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3216 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3217 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
3218 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3219 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
3220 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
3221 }
3222 else if ( v3.isValid() )
3223 {
3224 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
3225 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
3226 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3227 }
3228 else
3229 {
3230 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3231 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3232 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3233 }
3234 }
3235 else
3236 {
3237 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
3238 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
3239 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3240 }
3241}
3242
3243QgsGeometry QgsGeometry::intersection( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3244{
3245 if ( !d->geometry || geometry.isNull() )
3246 {
3247 return QgsGeometry();
3248 }
3249
3250 QgsGeos geos( d->geometry.get() );
3251
3252 mLastError.clear();
3253 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3254
3255 if ( !resultGeom )
3256 {
3257 QgsGeometry geom;
3258 geom.mLastError = mLastError;
3259 return geom;
3260 }
3261
3262 return QgsGeometry( std::move( resultGeom ) );
3263}
3264
3265QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3266{
3267 if ( !d->geometry || geometry.isNull() )
3268 {
3269 return QgsGeometry();
3270 }
3271
3272 QgsGeos geos( d->geometry.get() );
3273 mLastError.clear();
3274 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3275 if ( !resultGeom )
3276 {
3277 QgsGeometry geom;
3278 geom.mLastError = mLastError;
3279 return geom;
3280 }
3281 return QgsGeometry( std::move( resultGeom ) );
3282}
3283
3285{
3286 if ( !d->geometry )
3287 {
3288 return QgsGeometry();
3289 }
3290
3291 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::LineString )
3292 {
3293 // special case - a single linestring was passed
3294 return QgsGeometry( *this );
3295 }
3296
3297 QgsGeos geos( d->geometry.get() );
3298 mLastError.clear();
3299 QgsGeometry result( geos.mergeLines( &mLastError, parameters ) );
3300 result.mLastError = mLastError;
3301 return result;
3302}
3303
3304QgsGeometry QgsGeometry::difference( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3305{
3306 if ( !d->geometry || geometry.isNull() )
3307 {
3308 return QgsGeometry();
3309 }
3310
3311 QgsGeos geos( d->geometry.get() );
3312
3313 mLastError.clear();
3314 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3315 if ( !resultGeom )
3316 {
3317 QgsGeometry geom;
3318 geom.mLastError = mLastError;
3319 return geom;
3320 }
3321 return QgsGeometry( std::move( resultGeom ) );
3322}
3323
3324QgsGeometry QgsGeometry::symDifference( const QgsGeometry &geometry, const QgsGeometryParameters &parameters, QgsFeedback *feedback ) const
3325{
3326 if ( !d->geometry || geometry.isNull() )
3327 {
3328 return QgsGeometry();
3329 }
3330
3331 QgsGeos geos( d->geometry.get() );
3332
3333 mLastError.clear();
3334 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters, feedback ) );
3335 if ( !resultGeom )
3336 {
3337 QgsGeometry geom;
3338 geom.mLastError = mLastError;
3339 return geom;
3340 }
3341 return QgsGeometry( std::move( resultGeom ) );
3342}
3343
3345{
3346 QgsInternalGeometryEngine engine( *this );
3347
3348 return engine.extrude( x, y );
3349}
3350
3352
3353QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3354{
3356 return QVector< QgsPointXY >();
3357
3358 QgsInternalGeometryEngine engine( *this );
3359 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3360 mLastError = engine.lastError();
3361 return res;
3362}
3363
3364QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3365{
3367 return QVector< QgsPointXY >();
3368
3369 QgsInternalGeometryEngine engine( *this );
3370 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3371 mLastError = engine.lastError();
3372 return res;
3373}
3375
3377{
3378 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3379}
3380
3382{
3383 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3384}
3385
3386QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3387{
3388 QVector<QgsGeometry> geometryList;
3389 if ( !d->geometry )
3390 {
3391 return geometryList;
3392 }
3393
3395 if ( gc )
3396 {
3397 int numGeom = gc->numGeometries();
3398 geometryList.reserve( numGeom );
3399 for ( int i = 0; i < numGeom; ++i )
3400 {
3401 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3402 }
3403 }
3404 else //a singlepart geometry
3405 {
3406 geometryList.append( *this );
3407 }
3408
3409 return geometryList;
3410}
3411
3413{
3414 QgsPointXY point = asPoint();
3415 return point.toQPointF();
3416}
3417
3419{
3420 const QgsAbstractGeometry *part = constGet();
3421
3422 // if a geometry collection, get first part only
3424 {
3425 if ( collection->numGeometries() > 0 )
3426 part = collection->geometryN( 0 );
3427 else
3428 return QPolygonF();
3429 }
3430
3431 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3432 return curve->asQPolygonF();
3433 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3434 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3435 return QPolygonF();
3436}
3437
3438bool QgsGeometry::deleteRing( int ringNum, int partNum )
3439{
3440 if ( !d->geometry )
3441 {
3442 return false;
3443 }
3444
3445 detach();
3446 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3447 return ok;
3448}
3449
3450bool QgsGeometry::deletePart( int partNum )
3451{
3452 if ( !d->geometry )
3453 {
3454 return false;
3455 }
3456
3457 if ( !isMultipart() && partNum < 1 )
3458 {
3459 set( nullptr );
3460 return true;
3461 }
3462
3463 detach();
3464 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3465 return ok;
3466}
3467
3468Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3469{
3470 if ( !d->geometry )
3471 {
3473 }
3474
3475 Qgis::WkbType geomTypeBeforeModification = wkbType();
3476
3477 bool haveInvalidGeometry = false;
3478 bool geomModified = false;
3479
3480 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3481 if ( diffGeom )
3482 {
3483 reset( std::move( diffGeom ) );
3484 geomModified = true;
3485 }
3486
3487 if ( geomTypeBeforeModification != wkbType() )
3489 if ( haveInvalidGeometry )
3491 if ( !geomModified )
3493
3495}
3496
3528
3529QgsGeometry QgsGeometry::makeValid( Qgis::MakeValidMethod method, bool keepCollapsed, QgsFeedback *feedback ) const
3530{
3531 if ( !d->geometry )
3532 return QgsGeometry();
3533
3534 mLastError.clear();
3535 QgsGeos geos( d->geometry.get() );
3536 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError, feedback ) );
3537
3538 QgsGeometry result = QgsGeometry( std::move( g ) );
3539 result.mLastError = mLastError;
3540 return result;
3541}
3542
3547
3549{
3550 if ( !d->geometry )
3551 {
3553 }
3554
3555 if ( isMultipart() )
3556 {
3557 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3558 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3560 {
3561 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3562 }
3563 }
3564 else
3565 {
3566 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3567 {
3568 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3569 }
3570 }
3571
3573}
3574
3576{
3577 if ( !d->geometry )
3578 return QgsGeometry();
3579
3580 if ( isMultipart() )
3581 {
3582 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3583 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3584 newCollection->reserve( collection->numGeometries() );
3585 for ( int i = 0; i < collection->numGeometries(); ++i )
3586 {
3587 const QgsAbstractGeometry *g = collection->geometryN( i );
3589 {
3590 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3591 corrected->forceClockwise();
3592 newCollection->addGeometry( corrected.release() );
3593 }
3594 else
3595 {
3596 newCollection->addGeometry( g->clone() );
3597 }
3598 }
3599 return QgsGeometry( std::move( newCollection ) );
3600 }
3601 else
3602 {
3603 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3604 {
3605 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3606 corrected->forceClockwise();
3607 return QgsGeometry( std::move( corrected ) );
3608 }
3609 else
3610 {
3611 // not a curve polygon, so return unchanged
3612 return *this;
3613 }
3614 }
3615}
3616
3618{
3619 if ( !d->geometry )
3620 return QgsGeometry();
3621
3622 if ( isMultipart() )
3623 {
3624 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3625 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3626 newCollection->reserve( collection->numGeometries() );
3627 for ( int i = 0; i < collection->numGeometries(); ++i )
3628 {
3629 const QgsAbstractGeometry *g = collection->geometryN( i );
3631 {
3632 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3633 corrected->forceCounterClockwise();
3634 newCollection->addGeometry( corrected.release() );
3635 }
3636 else
3637 {
3638 newCollection->addGeometry( g->clone() );
3639 }
3640 }
3641 return QgsGeometry( std::move( newCollection ) );
3642 }
3643 else
3644 {
3645 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3646 {
3647 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3648 corrected->forceCounterClockwise();
3649 return QgsGeometry( std::move( corrected ) );
3650 }
3651 else
3652 {
3653 // not a curve polygon, so return unchanged
3654 return *this;
3655 }
3656 }
3657}
3658
3659
3660void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3661{
3662 errors.clear();
3663 if ( !d->geometry )
3664 return;
3665
3666 // avoid expensive calcs for trivial point geometries
3667 if ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) == Qgis::GeometryType::Point )
3668 {
3669 return;
3670 }
3671
3672 switch ( method )
3673 {
3675 QgsGeometryValidator::validateGeometry( *this, errors, method );
3676 return;
3677
3679 {
3680 QgsGeos geos( d->geometry.get(), 0, Qgis::GeosCreationFlags() );
3681 QString error;
3682 QgsGeometry errorLoc;
3683 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3684 {
3685 if ( errorLoc.isNull() )
3686 {
3687 errors.append( QgsGeometry::Error( error ) );
3688 }
3689 else
3690 {
3691 const QgsPointXY point = errorLoc.asPoint();
3692 errors.append( QgsGeometry::Error( error, point ) );
3693 }
3694 return;
3695 }
3696 }
3697 }
3698}
3699
3701{
3702 if ( !d->geometry )
3703 {
3704 return;
3705 }
3706
3707 detach();
3708 d->geometry->normalize();
3709}
3710
3712{
3713 if ( !d->geometry )
3714 {
3715 return false;
3716 }
3717
3718 return d->geometry->isValid( mLastError, flags );
3719}
3720
3722{
3723 if ( !d->geometry )
3724 return false;
3725
3726 QgsGeos geos( d->geometry.get() );
3727 mLastError.clear();
3728 return geos.isSimple( &mLastError );
3729}
3730
3731bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3732{
3733 if ( !d->geometry )
3734 return false;
3735
3736 QgsInternalGeometryEngine engine( *this );
3737 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3738}
3739
3741{
3743}
3744
3746{
3747 if ( !d->geometry || g.isNull() )
3748 {
3749 return false;
3750 }
3751
3752 // fast check - are they shared copies of the same underlying geometry?
3753 if ( d == g.d )
3754 return true;
3755
3756 // fast check - distinct geometry types?
3757 if ( type() != g.type() )
3758 return false;
3759
3760 mLastError.clear();
3761 switch ( backend )
3762 {
3764 {
3765 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3766 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3767 return false;
3768
3769 // avoid calling geos for trivial point case
3770 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3771 return *d->geometry == *g.d->geometry;
3772
3773 QgsGeos geos( d->geometry.get() );
3774 // fuzzy check call, with near zero epsilon, will behave as an exact comparison
3775 return geos.isFuzzyEqual( g.d->geometry.get(), 1e-8, &mLastError );
3776 }
3777
3779 {
3780 // slower check - actually test the geometries
3781 return *d->geometry == *g.d->geometry;
3782 }
3783 }
3785}
3786
3788{
3789 if ( !d->geometry || !g.d->geometry )
3790 {
3791 return false;
3792 }
3793
3794 // fast check - are they shared copies of the same underlying geometry?
3795 if ( d == g.d )
3796 return true;
3797
3798 // fast check - distinct geometry types?
3799 if ( type() != g.type() )
3800 return false;
3801
3802 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3803 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3804 return false;
3805
3806 mLastError.clear();
3807 switch ( backend )
3808 {
3810 {
3811 // avoid calling geos for trivial point case
3812 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3813 return *d->geometry == *g.d->geometry;
3814
3815 QgsGeos geos( d->geometry.get() );
3816 return geos.isEqual( g.d->geometry.get(), &mLastError );
3817 }
3818
3820 throw QgsNotSupportedException( u"Geometry backend '%1' is not supported by this function."_s.arg( qgsEnumValueToKey( backend ) ) );
3821 }
3823}
3824
3825bool QgsGeometry::isFuzzyEqual( const QgsGeometry &g, double epsilon, Qgis::GeometryBackend backend ) const
3826{
3827 if ( !d->geometry || g.isNull() )
3828 {
3829 return false;
3830 }
3831
3832 // fast check - are they shared copies of the same underlying geometry?
3833 if ( d == g.d )
3834 return true;
3835
3836 // fast check - distinct geometry types?
3837 if ( type() != g.type() )
3838 return false;
3839
3840 mLastError.clear();
3841 switch ( backend )
3842 {
3844 {
3845 QgsGeos geos( d->geometry.get() );
3846 return geos.isFuzzyEqual( g.d->geometry.get(), epsilon, &mLastError );
3847 }
3848
3850 {
3851 // slower check - actually test the geometries
3852 return d->geometry->fuzzyEqual( *g.d->geometry, epsilon );
3853 }
3854 }
3856}
3857
3858QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters, QgsFeedback *feedback )
3859{
3860 QgsGeos geos( nullptr );
3861
3862 QString error;
3863 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters, feedback ) );
3864 QgsGeometry result( std::move( geom ) );
3865 result.mLastError = error;
3866 return result;
3867}
3868
3869QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
3870{
3871 QVector<const QgsAbstractGeometry *> geomV2List;
3872 for ( const QgsGeometry &g : geometryList )
3873 {
3874 if ( !( g.isNull() ) )
3875 {
3876 geomV2List.append( g.constGet() );
3877 }
3878 }
3879
3880 QString error;
3881 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
3882 result.mLastError = error;
3883 return result;
3884}
3885
3887{
3888 if ( !d->geometry || !requiresConversionToStraightSegments() )
3889 {
3890 return;
3891 }
3892
3893 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3894 reset( std::move( straightGeom ) );
3895}
3896
3898{
3899 if ( !d->geometry )
3900 {
3901 return false;
3902 }
3903
3904 return d->geometry->hasCurvedSegments();
3905}
3906
3908{
3909 if ( !d->geometry )
3910 {
3912 }
3913
3914 detach();
3915 d->geometry->transform( ct, direction, transformZ );
3917}
3918
3919Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3920{
3921 if ( !d->geometry )
3922 {
3924 }
3925
3926 detach();
3927 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3929}
3930
3932{
3933 if ( d->geometry )
3934 {
3935 detach();
3936 d->geometry->transform( mtp.transform() );
3937 }
3938}
3939
3941{
3942 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
3943 {
3944 return QgsGeometry();
3945 }
3946
3947 QgsGeos geos( d->geometry.get() );
3948 mLastError.clear();
3949 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError, feedback );
3950 if ( !resultGeom )
3951 {
3952 QgsGeometry result;
3953 result.mLastError = mLastError;
3954 return result;
3955 }
3956 return QgsGeometry( std::move( resultGeom ) );
3957}
3958
3959void QgsGeometry::draw( QPainter &p ) const
3960{
3961 if ( d->geometry )
3962 {
3963 d->geometry->draw( p );
3964 }
3965}
3966
3967static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
3968{
3969 if ( vertexIndex < 0 )
3970 return false; // clearly something wrong
3971
3973 {
3974 partIndex = 0;
3975 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3976 {
3977 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3978
3979 // count total number of vertices in the part
3980 int numPoints = 0;
3981 for ( int k = 0; k < part->ringCount(); ++k )
3982 numPoints += part->vertexCount( 0, k );
3983
3984 if ( vertexIndex < numPoints )
3985 {
3986 int nothing;
3987 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3988 }
3989 vertexIndex -= numPoints;
3990 partIndex++;
3991 }
3992 }
3993 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
3994 {
3995 // PolyhedralSurface: patches are the parts
3996 partIndex = 0;
3997 for ( int i = 0; i < polySurface->numPatches(); ++i )
3998 {
3999 const QgsPolygon *patch = polySurface->patchN( i );
4000 // count total number of vertices in the patch
4001 int numPoints = 0;
4002 for ( int k = 0; k < patch->ringCount(); ++k )
4003 numPoints += patch->vertexCount( 0, k );
4004
4005 if ( vertexIndex < numPoints )
4006 {
4007 int nothing;
4008 return vertexIndexInfo( patch, vertexIndex, nothing, ringIndex, vertex );
4009 }
4010 vertexIndex -= numPoints;
4011 partIndex++;
4012 }
4013 }
4014 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4015 {
4016 const QgsCurve *ring = curvePolygon->exteriorRing();
4017 if ( vertexIndex < ring->numPoints() )
4018 {
4019 partIndex = 0;
4020 ringIndex = 0;
4021 vertex = vertexIndex;
4022 return true;
4023 }
4024 vertexIndex -= ring->numPoints();
4025 ringIndex = 1;
4026 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
4027 {
4028 const QgsCurve *ring = curvePolygon->interiorRing( i );
4029 if ( vertexIndex < ring->numPoints() )
4030 {
4031 partIndex = 0;
4032 vertex = vertexIndex;
4033 return true;
4034 }
4035 vertexIndex -= ring->numPoints();
4036 ringIndex += 1;
4037 }
4038 }
4039 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4040 {
4041 if ( vertexIndex < curve->numPoints() )
4042 {
4043 partIndex = 0;
4044 ringIndex = 0;
4045 vertex = vertexIndex;
4046 return true;
4047 }
4048 }
4049 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
4050 {
4051 if ( vertexIndex == 0 )
4052 {
4053 partIndex = 0;
4054 ringIndex = 0;
4055 vertex = 0;
4056 return true;
4057 }
4058 }
4059
4060 return false;
4061}
4062
4064{
4065 if ( !d->geometry )
4066 {
4067 return false;
4068 }
4069
4070 id.type = Qgis::VertexType::Segment;
4071
4072 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
4073 if ( !res )
4074 return false;
4075
4076 // now let's find out if it is a straight or circular segment
4077 const QgsAbstractGeometry *g = d->geometry.get();
4079 {
4080 g = geomCollection->geometryN( id.part );
4081 }
4082 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
4083 {
4084 g = polySurface->patchN( id.part );
4085 }
4086
4087 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
4088 {
4089 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
4090 }
4091
4092 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
4093 {
4094 QgsPoint p;
4095 res = curve->pointAt( id.vertex, p, id.type );
4096 if ( !res )
4097 return false;
4098 }
4099
4100 return true;
4101}
4102
4104{
4105 if ( !d->geometry )
4106 {
4107 return -1;
4108 }
4109 return d->geometry->vertexNumberFromVertexId( id );
4110}
4111
4113{
4114 return mLastError;
4115}
4116
4117void QgsGeometry::filterVertices( const std::function<bool( const QgsPoint & )> &filter )
4118{
4119 if ( !d->geometry )
4120 return;
4121
4122 detach();
4123
4124 d->geometry->filterVertices( filter );
4125}
4126
4127void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
4128{
4129 if ( !d->geometry )
4130 return;
4131
4132 detach();
4133
4134 d->geometry->transformVertices( transform );
4135}
4136
4137void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
4138{
4139 output.clear();
4140 for ( const QgsPointXY &p : input )
4141 {
4142 output.append( QgsPoint( p ) );
4143 }
4144}
4145
4146void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
4147{
4148 output.clear();
4149 for ( const QgsPoint &p : input )
4150 {
4151 output.append( QgsPointXY( p.x(), p.y() ) );
4152 }
4153}
4154
4155void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
4156{
4157 output.clear();
4158
4159 auto convertRing = []( const QgsCurve *ring ) -> QgsPolylineXY {
4160 QgsPolylineXY res;
4162 std::unique_ptr< QgsLineString > segmentizedLine;
4163 const QgsLineString *line = nullptr;
4164 if ( doSegmentation )
4165 {
4166 segmentizedLine.reset( ring->curveToLine() );
4167 line = segmentizedLine.get();
4168 }
4169 else
4170 {
4172 if ( !line )
4173 {
4174 return res;
4175 }
4176 }
4177
4178 int nVertices = line->numPoints();
4179 res.resize( nVertices );
4180 QgsPointXY *data = res.data();
4181 const double *xData = line->xData();
4182 const double *yData = line->yData();
4183 for ( int i = 0; i < nVertices; ++i )
4184 {
4185 data->setX( *xData++ );
4186 data->setY( *yData++ );
4187 data++;
4188 }
4189 return res;
4190 };
4191
4192 if ( const QgsCurve *exterior = input.exteriorRing() )
4193 {
4194 output.push_back( convertRing( exterior ) );
4195 }
4196
4197 const int interiorRingCount = input.numInteriorRings();
4198 output.reserve( output.size() + interiorRingCount );
4199 for ( int n = 0; n < interiorRingCount; ++n )
4200 {
4201 output.push_back( convertRing( input.interiorRing( n ) ) );
4202 }
4203}
4204
4206{
4207 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
4208}
4209
4210QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
4211{
4212 std::unique_ptr< QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
4213
4214 if ( polygon.isClosed() )
4215 {
4216 auto poly = std::make_unique< QgsPolygon >();
4217 poly->setExteriorRing( ring.release() );
4218 return QgsGeometry( std::move( poly ) );
4219 }
4220 else
4221 {
4222 return QgsGeometry( std::move( ring ) );
4223 }
4224}
4225
4227{
4229 QgsPolygonXY result;
4230 result << createPolylineFromQPolygonF( polygon );
4231 return result;
4233}
4234
4236{
4237 QgsPolylineXY result;
4238 result.reserve( polygon.count() );
4239 for ( const QPointF &p : polygon )
4240 {
4241 result.append( QgsPointXY( p ) );
4242 }
4243 return result;
4244}
4245
4246bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
4247{
4248 if ( p1.count() != p2.count() )
4249 return false;
4250
4251 for ( int i = 0; i < p1.count(); ++i )
4252 {
4253 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
4254 return false;
4255 }
4256 return true;
4257}
4258
4259bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
4260{
4261 if ( p1.count() != p2.count() )
4262 return false;
4263
4264 for ( int i = 0; i < p1.count(); ++i )
4265 {
4266 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4267 return false;
4268 }
4269 return true;
4270}
4271
4272
4273bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
4274{
4275 if ( p1.count() != p2.count() )
4276 return false;
4277
4278 for ( int i = 0; i < p1.count(); ++i )
4279 {
4280 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4281 return false;
4282 }
4283 return true;
4284}
4285
4286QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4287{
4288 if ( !d->geometry || d->geometry->isEmpty() )
4289 return QgsGeometry();
4290
4291 QgsGeometry geom = *this;
4293 geom = QgsGeometry( d->geometry->segmentize() );
4294
4295 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
4296 {
4299 //can't smooth a point based geometry
4300 return geom;
4301
4303 {
4305 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
4306 }
4307
4309 {
4311
4312 auto resultMultiline = std::make_unique< QgsMultiLineString>();
4313 resultMultiline->reserve( inputMultiLine->numGeometries() );
4314 for ( int i = 0; i < inputMultiLine->numGeometries(); ++i )
4315 {
4316 resultMultiline->addGeometry( smoothLine( *( inputMultiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4317 }
4318 return QgsGeometry( std::move( resultMultiline ) );
4319 }
4320
4322 {
4324 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
4325 }
4326
4328 {
4330
4331 auto resultMultiPoly = std::make_unique< QgsMultiPolygon >();
4332 resultMultiPoly->reserve( inputMultiPoly->numGeometries() );
4333 for ( int i = 0; i < inputMultiPoly->numGeometries(); ++i )
4334 {
4335 resultMultiPoly->addGeometry( smoothPolygon( *( inputMultiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4336 }
4337 return QgsGeometry( std::move( resultMultiPoly ) );
4338 }
4339
4341 default:
4342 return QgsGeometry( *this );
4343 }
4344}
4345
4346std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing )
4347{
4348 auto result = std::make_unique< QgsLineString >( line );
4349 QgsPointSequence outputLine;
4350 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
4351 {
4352 outputLine.resize( 0 );
4353 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
4354 bool skipFirst = false;
4355 bool skipLast = false;
4356 if ( isRing )
4357 {
4358 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
4359 QgsPoint p2 = result->pointN( 0 );
4360 QgsPoint p3 = result->pointN( 1 );
4361 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4362 angle = std::fabs( M_PI - angle );
4363 skipFirst = angle > maxAngleRads;
4364 }
4365 for ( int i = 0; i < result->numPoints() - 1; i++ )
4366 {
4367 QgsPoint p1 = result->pointN( i );
4368 QgsPoint p2 = result->pointN( i + 1 );
4369
4370 double angle = M_PI;
4371 if ( i == 0 && isRing )
4372 {
4373 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
4374 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4375 }
4376 else if ( i < result->numPoints() - 2 )
4377 {
4378 QgsPoint p3 = result->pointN( i + 2 );
4379 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4380 }
4381 else if ( i == result->numPoints() - 2 && isRing )
4382 {
4383 QgsPoint p3 = result->pointN( 1 );
4384 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4385 }
4386
4387 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
4388
4389 // don't apply distance threshold to first or last segment
4390 if ( i == 0 || i >= result->numPoints() - 2 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
4391 {
4392 if ( !isRing )
4393 {
4394 if ( !skipFirst )
4395 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
4396 if ( !skipLast )
4397 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
4398 else
4399 outputLine << p2;
4400 }
4401 else
4402 {
4403 // ring
4404 if ( !skipFirst )
4405 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
4406 else if ( i == 0 )
4407 outputLine << p1;
4408 if ( !skipLast )
4409 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
4410 else
4411 outputLine << p2;
4412 }
4413 }
4414 skipFirst = skipLast;
4415 }
4416
4417 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4418 outputLine << outputLine.at( 0 );
4419
4420 result->setPoints( outputLine );
4421 }
4422 return result;
4423}
4424
4425std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4426{
4427 double maxAngleRads = maxAngle * M_PI / 180.0;
4428 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4429 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4430}
4431
4432std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4433{
4434 double maxAngleRads = maxAngle * M_PI / 180.0;
4435 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4436 auto resultPoly = std::make_unique< QgsPolygon >();
4437
4438 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4439
4440 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4441 {
4442 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4443 }
4444 return resultPoly;
4445}
4446
4447QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4448{
4449 switch ( type() )
4450 {
4452 {
4453 bool srcIsMultipart = isMultipart();
4454
4455 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4456 {
4457 // return a copy of the same geom
4458 return QgsGeometry( *this );
4459 }
4460 if ( destMultipart )
4461 {
4462 // layer is multipart => make a multipoint with a single point
4463 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4464 }
4465 else
4466 {
4467 // destination is singlepart => make a single part if possible
4468 QgsMultiPointXY multiPoint = asMultiPoint();
4469 if ( multiPoint.count() == 1 )
4470 {
4471 return fromPointXY( multiPoint[0] );
4472 }
4473 }
4474 return QgsGeometry();
4475 }
4476
4478 {
4479 // only possible if destination is multipart
4480 if ( !destMultipart )
4481 return QgsGeometry();
4482
4483 // input geometry is multipart
4484 if ( isMultipart() )
4485 {
4486 const QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4487 QgsMultiPointXY multiPoint;
4488 for ( const QgsPolylineXY &l : inputMultiLine )
4489 for ( const QgsPointXY &p : l )
4490 multiPoint << p;
4491 return fromMultiPointXY( multiPoint );
4492 }
4493 // input geometry is not multipart: copy directly the line into a multipoint
4494 else
4495 {
4496 QgsPolylineXY line = asPolyline();
4497 if ( !line.isEmpty() )
4498 return fromMultiPointXY( line );
4499 }
4500 return QgsGeometry();
4501 }
4502
4504 {
4505 // can only transform if destination is multipoint
4506 if ( !destMultipart )
4507 return QgsGeometry();
4508
4509 // input geometry is multipart: make a multipoint from multipolygon
4510 if ( isMultipart() )
4511 {
4512 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4513 QgsMultiPointXY multiPoint;
4514 for ( const QgsPolygonXY &poly : multiPolygon )
4515 for ( const QgsPolylineXY &line : poly )
4516 for ( const QgsPointXY &pt : line )
4517 multiPoint << pt;
4518 return fromMultiPointXY( multiPoint );
4519 }
4520 // input geometry is not multipart: make a multipoint from polygon
4521 else
4522 {
4523 const QgsPolygonXY polygon = asPolygon();
4524 QgsMultiPointXY multiPoint;
4525 for ( const QgsPolylineXY &line : polygon )
4526 for ( const QgsPointXY &pt : line )
4527 multiPoint << pt;
4528 return fromMultiPointXY( multiPoint );
4529 }
4530 }
4531
4532 default:
4533 return QgsGeometry();
4534 }
4535}
4536
4537QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4538{
4539 switch ( type() )
4540 {
4542 {
4543 if ( !isMultipart() )
4544 return QgsGeometry();
4545
4546 QgsMultiPointXY multiPoint = asMultiPoint();
4547 if ( multiPoint.count() < 2 )
4548 return QgsGeometry();
4549
4550 if ( destMultipart )
4551 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4552 else
4553 return fromPolylineXY( multiPoint );
4554 }
4555
4557 {
4558 bool srcIsMultipart = isMultipart();
4559
4560 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4561 {
4562 // return a copy of the same geom
4563 return QgsGeometry( *this );
4564 }
4565 if ( destMultipart )
4566 {
4567 // destination is multipart => makes a multipoint with a single line
4568 QgsPolylineXY line = asPolyline();
4569 if ( !line.isEmpty() )
4570 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4571 }
4572 else
4573 {
4574 // destination is singlepart => make a single part if possible
4575 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4576 if ( inputMultiLine.count() == 1 )
4577 return fromPolylineXY( inputMultiLine[0] );
4578 }
4579 return QgsGeometry();
4580 }
4581
4583 {
4584 // input geometry is multipolygon
4585 if ( isMultipart() )
4586 {
4587 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4588 QgsMultiPolylineXY inputMultiLine;
4589 for ( const QgsPolygonXY &poly : multiPolygon )
4590 for ( const QgsPolylineXY &line : poly )
4591 inputMultiLine << line;
4592
4593 if ( destMultipart )
4594 {
4595 // destination is multipart
4596 return fromMultiPolylineXY( inputMultiLine );
4597 }
4598 else if ( inputMultiLine.count() == 1 )
4599 {
4600 // destination is singlepart => make a single part if possible
4601 return fromPolylineXY( inputMultiLine[0] );
4602 }
4603 }
4604 // input geometry is single polygon
4605 else
4606 {
4607 QgsPolygonXY polygon = asPolygon();
4608 // if polygon has rings
4609 if ( polygon.count() > 1 )
4610 {
4611 // cannot fit a polygon with rings in a single line layer
4612 // TODO: would it be better to remove rings?
4613 if ( destMultipart )
4614 {
4615 const QgsPolygonXY polygon = asPolygon();
4616 QgsMultiPolylineXY inputMultiLine;
4617 inputMultiLine.reserve( polygon.count() );
4618 for ( const QgsPolylineXY &line : polygon )
4619 inputMultiLine << line;
4620 return fromMultiPolylineXY( inputMultiLine );
4621 }
4622 }
4623 // no rings
4624 else if ( polygon.count() == 1 )
4625 {
4626 if ( destMultipart )
4627 {
4628 return fromMultiPolylineXY( polygon );
4629 }
4630 else
4631 {
4632 return fromPolylineXY( polygon[0] );
4633 }
4634 }
4635 }
4636 return QgsGeometry();
4637 }
4638
4639 default:
4640 return QgsGeometry();
4641 }
4642}
4643
4644QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4645{
4646 switch ( type() )
4647 {
4649 {
4650 if ( !isMultipart() )
4651 return QgsGeometry();
4652
4653 QgsMultiPointXY multiPoint = asMultiPoint();
4654 if ( multiPoint.count() < 3 )
4655 return QgsGeometry();
4656
4657 if ( multiPoint.last() != multiPoint.first() )
4658 multiPoint << multiPoint.first();
4659
4660 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4661 if ( destMultipart )
4662 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4663 else
4664 return fromPolygonXY( polygon );
4665 }
4666
4668 {
4669 // input geometry is multiline
4670 if ( isMultipart() )
4671 {
4672 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4673 QgsMultiPolygonXY multiPolygon;
4674 for ( QgsMultiPolylineXY::iterator multiLineIt = inputMultiLine.begin(); multiLineIt != inputMultiLine.end(); ++multiLineIt )
4675 {
4676 // do not create polygon for a 1 segment line
4677 if ( ( *multiLineIt ).count() < 3 )
4678 return QgsGeometry();
4679 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4680 return QgsGeometry();
4681
4682 // add closing node
4683 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4684 *multiLineIt << ( *multiLineIt ).first();
4685 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4686 }
4687 // check that polygons were inserted
4688 if ( !multiPolygon.isEmpty() )
4689 {
4690 if ( destMultipart )
4691 {
4692 return fromMultiPolygonXY( multiPolygon );
4693 }
4694 else if ( multiPolygon.count() == 1 )
4695 {
4696 // destination is singlepart => make a single part if possible
4697 return fromPolygonXY( multiPolygon[0] );
4698 }
4699 }
4700 }
4701 // input geometry is single line
4702 else
4703 {
4704 QgsPolylineXY line = asPolyline();
4705
4706 // do not create polygon for a 1 segment line
4707 if ( line.count() < 3 )
4708 return QgsGeometry();
4709 if ( line.count() == 3 && line.first() == line.last() )
4710 return QgsGeometry();
4711
4712 // add closing node
4713 if ( line.first() != line.last() )
4714 line << line.first();
4715
4716 // destination is multipart
4717 if ( destMultipart )
4718 {
4719 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4720 }
4721 else
4722 {
4723 return fromPolygonXY( QgsPolygonXY() << line );
4724 }
4725 }
4726 return QgsGeometry();
4727 }
4728
4730 {
4731 bool srcIsMultipart = isMultipart();
4732
4733 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4734 {
4735 // return a copy of the same geom
4736 return QgsGeometry( *this );
4737 }
4738 if ( destMultipart )
4739 {
4740 // destination is multipart => makes a multipoint with a single polygon
4741 QgsPolygonXY polygon = asPolygon();
4742 if ( !polygon.isEmpty() )
4743 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4744 }
4745 else
4746 {
4747 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4748 if ( multiPolygon.count() == 1 )
4749 {
4750 // destination is singlepart => make a single part if possible
4751 return fromPolygonXY( multiPolygon[0] );
4752 }
4753 }
4754 return QgsGeometry();
4755 }
4756
4757 default:
4758 return QgsGeometry();
4759 }
4760}
4761
4763{
4764 return new QgsGeos( geometry, precision, flags );
4765}
4766
4767QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
4768{
4769 out << geometry.asWkb();
4770 return out;
4771}
4772
4773QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4774{
4775 QByteArray byteArray;
4776 in >> byteArray;
4777 if ( byteArray.isEmpty() )
4778 {
4779 geometry.set( nullptr );
4780 return in;
4781 }
4782
4783 geometry.fromWkb( byteArray );
4784 return in;
4785}
4786
4787
4789{
4790 return mMessage;
4791}
4792
4794{
4795 return mLocation;
4796}
4797
4799{
4800 return mHasLocation;
4801}
4802
4803QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int vertexIndex, double distance1, double distance2, int segments ) const
4804{
4805 QgsDebugMsgLevel( u"%1 starts: %2"_s.arg( qgsEnumValueToKey( op ) ).arg( asWkt( 2 ) ), 3 );
4806 if ( isNull() )
4807 {
4808 mLastError = u"Operation '%1' needs non-null geometry."_s.arg( qgsEnumValueToKey( op ) );
4809 return QgsGeometry();
4810 }
4811
4812 QgsCurve *curve = nullptr;
4813
4814 int modifiedPart = -1;
4815 int modifiedRing = -1;
4816 QgsVertexId vertexId;
4817 if ( !vertexIdFromVertexNr( vertexIndex, vertexId ) )
4818 {
4819 mLastError = u"Invalid vertex index"_s;
4820 return QgsGeometry();
4821 }
4822 int resolvedVertexIndex = vertexId.vertex;
4823 QgsMultiLineString *inputMultiLine = nullptr;
4824 QgsMultiPolygon *inputMultiPoly = nullptr;
4826
4827 if ( geomType == Qgis::GeometryType::Line )
4828 {
4829 if ( isMultipart() )
4830 {
4831 modifiedPart = vertexId.part;
4832
4833 inputMultiLine = qgsgeometry_cast<QgsMultiLineString *>( d->geometry.get() );
4834 curve = dynamic_cast<QgsCurve *>( inputMultiLine->lineStringN( modifiedPart ) );
4835 }
4836 else
4837 {
4838 curve = dynamic_cast<QgsCurve *>( d->geometry.get() );
4839 }
4840 }
4841 else if ( geomType == Qgis::GeometryType::Polygon )
4842 {
4843 QgsPolygon *poly = nullptr;
4844 if ( isMultipart() )
4845 {
4846 modifiedPart = vertexId.part;
4847 // get part, get ring
4848 inputMultiPoly = qgsgeometry_cast<QgsMultiPolygon *>( d->geometry.get() );
4849 poly = inputMultiPoly->polygonN( modifiedPart );
4850 }
4851 else
4852 {
4853 poly = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
4854 }
4855 if ( !poly )
4856 {
4857 mLastError = u"Could not get polygon geometry."_s;
4858 return QgsGeometry();
4859 }
4860
4861 // if has rings
4862 modifiedRing = vertexId.ring;
4863 if ( modifiedRing == 0 )
4864 curve = qgsgeometry_cast<QgsCurve *>( poly->exteriorRing() );
4865 else
4866 curve = qgsgeometry_cast<QgsCurve *>( poly->interiorRing( modifiedRing - 1 ) );
4867 }
4868 else
4869 curve = nullptr;
4870
4871 if ( !curve )
4872 {
4873 mLastError = u"Operation '%1' needs curve geometry."_s.arg( qgsEnumValueToKey( op ) );
4874 return QgsGeometry();
4875 }
4876
4877 std::unique_ptr<QgsAbstractGeometry> result;
4878 try
4879 {
4881 result = QgsGeometryUtils::chamferVertex( curve, resolvedVertexIndex, distance1, distance2 );
4882 else
4883 result = QgsGeometryUtils::filletVertex( curve, resolvedVertexIndex, distance1, segments );
4884 }
4885 catch ( QgsInvalidArgumentException &e )
4886 {
4887 mLastError = u"%1 Requested vertex: %2 was resolved as: [part: %3, ring: %4, vertex: %5]"_s //
4888 .arg( e.what() )
4889 .arg( vertexIndex )
4890 .arg( modifiedPart )
4891 .arg( modifiedRing )
4892 .arg( resolvedVertexIndex );
4893 return QgsGeometry();
4894 }
4895
4896 if ( !result )
4897 {
4898 mLastError = u"Operation '%1' generates a null geometry."_s.arg( qgsEnumValueToKey( op ) );
4899 return QgsGeometry();
4900 }
4901
4902 if ( result->isEmpty() )
4903 return QgsGeometry( std::move( result ) );
4904
4905 // insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon
4906 auto updatePolygon = []( const QgsPolygon *inputPoly, QgsAbstractGeometry *result, int modifiedRing ) -> std::unique_ptr<QgsPolygon> {
4907 auto newPoly = std::make_unique<QgsPolygon>();
4908 for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex )
4909 {
4910 if ( ringIndex == modifiedRing )
4911 {
4912 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
4913 {
4914 if ( ringIndex == 0 && resPartIte == result->parts_begin() )
4915 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
4916 else
4917 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
4918 }
4919 }
4920 else
4921 {
4922 if ( ringIndex == 0 )
4923 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->exteriorRing()->clone() ) );
4924 else
4925 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->interiorRing( ringIndex - 1 )->clone() ) );
4926 }
4927 }
4928 return newPoly;
4929 };
4930
4931 std::unique_ptr<QgsAbstractGeometry> finalGeom;
4932 if ( geomType == Qgis::GeometryType::Line )
4933 {
4934 if ( modifiedPart >= 0 )
4935 {
4936 auto newMultiLine = std::make_unique<QgsMultiLineString>();
4937 int partIndex = 0;
4938 for ( QgsMultiLineString::part_iterator partIte = inputMultiLine->parts_begin(); partIte != inputMultiLine->parts_end(); ++partIte )
4939 {
4940 if ( partIndex == modifiedPart )
4941 {
4942 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
4943 {
4944 newMultiLine->addGeometry( ( *resPartIte )->clone() );
4945 }
4946 }
4947 else
4948 {
4949 newMultiLine->addGeometry( ( *partIte )->clone() );
4950 }
4951 partIndex++;
4952 }
4953 finalGeom = std::move( newMultiLine );
4954 }
4955 else
4956 {
4957 // resultGeom is already the correct result!
4958 finalGeom = std::move( result );
4959 }
4960 }
4961 else
4962 {
4963 // geomType == Qgis::GeometryType::Polygon
4964 if ( modifiedPart >= 0 )
4965 {
4966 auto newMultiPoly = std::make_unique<QgsMultiPolygon>();
4967 int partIndex = 0;
4968 for ( QgsAbstractGeometry::part_iterator partIte = inputMultiPoly->parts_begin(); partIte != inputMultiPoly->parts_end(); ++partIte )
4969 {
4970 if ( partIndex == modifiedPart )
4971 {
4972 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( *partIte ), result.get(), modifiedRing );
4973 newMultiPoly->addGeometry( newPoly.release() );
4974 }
4975 else
4976 {
4977 newMultiPoly->addGeometry( ( *partIte )->clone() );
4978 }
4979 partIndex++;
4980 }
4981 finalGeom.reset( dynamic_cast<QgsAbstractGeometry *>( newMultiPoly.release() ) );
4982 }
4983 else
4984 {
4985 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( d->geometry.get() ), result.get(), modifiedRing );
4986 finalGeom = std::move( newPoly );
4987 }
4988 }
4989
4990 QgsGeometry finalResult( std::move( finalGeom ) );
4991
4992 QgsDebugMsgLevel( u"Final result Wkt: %1"_s.arg( finalResult.asWkt( 2 ) ), 3 );
4993
4994 return finalResult;
4995}
4996
4997
4998QgsGeometry QgsGeometry::chamfer( int vertexIndex, double distance1, double distance2 ) const
4999{
5000 return doChamferFillet( ChamferFilletOperationType::Chamfer, vertexIndex, distance1, distance2, 0 );
5001}
5002
5003QgsGeometry QgsGeometry::fillet( int vertexIndex, double radius, int segments ) const
5004{
5005 return doChamferFillet( ChamferFilletOperationType::Fillet, vertexIndex, radius, 0.0, segments );
5006}
5007
5008QgsGeometry QgsGeometry::chamfer( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2 )
5009{
5010 std::unique_ptr<QgsLineString> result( QgsGeometryUtils::createChamferGeometry( segment1Start, segment1End, segment2Start, segment2End, distance1, distance2 ) );
5011
5012 if ( !result )
5013 {
5014 return QgsGeometry();
5015 }
5016
5017 return QgsGeometry( std::move( result ) );
5018}
5019
5020QgsGeometry QgsGeometry::fillet( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments )
5021{
5022 std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::createFilletGeometry( segment1Start, segment1End, segment2Start, segment2End, radius, segments ) );
5023
5024 if ( !result )
5025 {
5026 return QgsGeometry();
5027 }
5028
5029 return QgsGeometry( std::move( result ) );
5030}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2267
@ GEOS
Use GEOS implementation.
Definition qgis.h:2269
@ QGIS
Use internal implementation.
Definition qgis.h:2268
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2191
BufferSide
Side of line to buffer.
Definition qgis.h:2216
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3457
AngularDirection
Angular directions.
Definition qgis.h:3598
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3601
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2161
@ AddPartSelectedGeometryNotFound
The selected geometry cannot be found.
Definition qgis.h:2171
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Definition qgis.h:2165
@ Success
Operation succeeded.
Definition qgis.h:2162
@ SelectionIsEmpty
No features were selected.
Definition qgis.h:2166
@ GeometryTypeHasChanged
Operation has changed geometry type.
Definition qgis.h:2180
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
Definition qgis.h:2177
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint).
Definition qgis.h:2176
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition qgis.h:2172
@ AddRingNotClosed
The input ring is not closed.
Definition qgis.h:2174
@ SelectionIsGreaterThanOne
More than one features were selected.
Definition qgis.h:2167
@ SplitCannotSplitPoint
Cannot split points.
Definition qgis.h:2179
@ GeometryEngineError
Geometry engine misses a method implemented or an error occurred in the geometry engine.
Definition qgis.h:2168
@ NothingHappened
Nothing happened, without any error.
Definition qgis.h:2163
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition qgis.h:2164
@ LayerNotEditable
Cannot edit layer.
Definition qgis.h:2169
@ AddRingNotValid
The input ring is not valid.
Definition qgis.h:2175
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2195
@ Segment
The actual start or end point of a segment.
Definition qgis.h:3232
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:2204
@ QgisInternal
Use internal QgsGeometryValidator method.
Definition qgis.h:2205
@ Geos
Use GEOS validation methods.
Definition qgis.h:2206
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
Definition qgis.h:2290
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
JoinStyle
Join styles for buffers.
Definition qgis.h:2241
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2228
CoverageValidityResult
Coverage validity results.
Definition qgis.h:2299
@ Error
An exception occurred while determining validity.
Definition qgis.h:2302
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3442
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2312
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ CompoundCurve
CompoundCurve.
Definition qgis.h:305
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ TIN
TIN.
Definition qgis.h:310
@ MultiPoint
MultiPoint.
Definition qgis.h:300
@ Polygon
Polygon.
Definition qgis.h:298
@ MultiPolygon
MultiPolygon.
Definition qgis.h:302
@ Triangle
Triangle.
Definition qgis.h:299
@ NoGeometry
No geometry.
Definition qgis.h:312
@ MultiLineString
MultiLineString.
Definition qgis.h:301
@ Unknown
Unknown.
Definition qgis.h:295
@ CircularString
CircularString.
Definition qgis.h:304
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ MultiCurve
MultiCurve.
Definition qgis.h:307
@ CurvePolygon
CurvePolygon.
Definition qgis.h:306
@ PolyhedralSurface
PolyhedralSurface.
Definition qgis.h:309
@ MultiSurface
MultiSurface.
Definition qgis.h:308
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2816
The part_iterator class provides an STL-style iterator for const references to geometry parts.
The part_iterator class provides an STL-style iterator for geometry parts.
The vertex_iterator class provides an STL-style iterator for vertices.
Abstract base class for all geometries.
virtual int ringCount(int part=0) const =0
Returns the number of rings of which this geometry is built.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual int vertexNumberFromVertexId(QgsVertexId id) const =0
Returns the vertex number corresponding to a vertex id.
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 const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
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.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part 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.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of 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:45
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:240
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:205
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:268
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:212
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:388
bool is2d() const
Returns true if the box can be considered a 2-dimensional box, i.e.
Definition qgsbox3d.cpp:137
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:233
Circle geometry type.
Definition qgscircle.h:46
static QgsCircle from2Points(const QgsPoint &pt1, const QgsPoint &pt2)
Constructs a circle by 2 points on the circle.
Definition qgscircle.cpp:39
double radius() const
Returns the radius of the circle.
Definition qgscircle.h:303
std::unique_ptr< QgsCircularString > toCircularString(bool oriented=false) const
Returns a circular string from the circle.
bool contains(const QgsPoint &point, double epsilon=1E-8) const
Returns true if the circle contains the point.
static QgsCircle minimalCircleFrom3Points(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon=1E-8)
Constructs the smallest circle from 3 points.
Circular string geometry type.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
Compound curve geometry type.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
A const WKB pointer.
Definition qgswkbptr.h:211
Handles coordinate transforms between two coordinate systems.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
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).
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsPoint * interpolatePoint(double distance) const =0
Returns an interpolated point on the curve at the specified distance.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
QgsPoint center() const
Returns the center point.
Definition qgsellipse.h:122
QString what() const
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
virtual bool insertGeometry(QgsAbstractGeometry *g, int index)
Inserts a geometry before a specified index and takes ownership.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Java-style iterator for const traversal of parts of a geometry.
static Qgis::GeometryOperationResult addRing(QgsAbstractGeometry *geometry, std::unique_ptr< QgsCurve > ring)
Add an interior ring to a geometry.
static std::unique_ptr< QgsAbstractGeometry > avoidIntersections(const QgsAbstractGeometry &geom, const QList< QgsVectorLayer * > &avoidIntersectionsLayers, bool &haveInvalidGeometry, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Alters a geometry so that it avoids intersections with features from all open vector layers.
static bool deletePart(QgsAbstractGeometry *geom, int partNum)
Deletes a part from a geometry.
static bool deleteRing(QgsAbstractGeometry *geom, int ringNum, int partNum=0)
Deletes a ring from a geometry.
static Qgis::GeometryOperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
A geometry engine is a low-level representation of a QgsAbstractGeometry object, optimised for use wi...
EngineOperationResult
Success or failure of a geometry operation.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
@ InvalidInput
The input is not valid.
@ NodedGeometryError
Error occurred while creating a noded geometry.
@ EngineError
Error occurred in the geometry engine.
@ SplitCannotSplitPoint
Points cannot be split.
@ Success
Operation succeeded.
@ MethodNotImplemented
Method not implemented in geometry engine.
static std::unique_ptr< QgsMultiPolygon > fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Construct geometry from a multipolygon.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(Qgis::WkbType type)
Returns a new geometry collection matching a specified WKB type.
static std::unique_ptr< QgsAbstractGeometry > fromPolylineXY(const QgsPolylineXY &polyline)
Construct geometry from a polyline.
static std::unique_ptr< QgsMultiPoint > fromMultiPointXY(const QgsMultiPointXY &multipoint)
Construct geometry from a multipoint.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkt(const QString &text)
Construct geometry from a WKT string.
static std::unique_ptr< QgsMultiLineString > fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Construct geometry from a multipolyline.
static std::unique_ptr< QgsAbstractGeometry > fromPointXY(const QgsPointXY &point)
Construct geometry from a point.
static std::unique_ptr< QgsPolygon > fromPolygonXY(const QgsPolygonXY &polygon)
Construct geometry from a polygon.
static std::unique_ptr< QgsAbstractGeometry > geomFromWkbType(Qgis::WkbType t)
Returns empty geometry from wkb type.
Encapsulates parameters under which a geometry operation is performed.
Java-style iterator for traversal of parts of a geometry.
static double angleBetweenThreePoints(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the angle between the lines AB and BC, where AB and BC described by points a,...
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
static std::unique_ptr< QgsLineString > createChamferGeometry(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2)
Creates a complete chamfer geometry connecting two segments.
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 std::unique_ptr< QgsAbstractGeometry > createFilletGeometry(const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments)
Creates a complete fillet geometry connecting two segments.
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 std::unique_ptr< QgsAbstractGeometry > chamferVertex(const QgsCurve *curve, int vertexIndex, double distance1, double distance2)
Applies chamfer to a vertex in a curve geometry.
static std::unique_ptr< QgsAbstractGeometry > filletVertex(const QgsCurve *curve, int vertexIndex, double radius, int segments)
Applies fillet to a vertex in a curve geometry.
static void validateGeometry(const QgsGeometry &geometry, QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal)
Validate geometry and produce a list of geometry errors.
A geometry error.
bool hasWhere() const
true if the location available from
QgsPointXY where() const
The coordinates at which the error is located and should be visualized.
QString what() const
A human readable error message containing details about the error.
A geometry is the spatial representation of a feature.
QgsGeometry makeDifference(const QgsGeometry &other, QgsFeedback *feedback=nullptr) const
Returns the geometry formed by modifying this geometry such that it does not intersect the other geom...
QPolygonF asQPolygonF() const
Returns contents of the geometry as a QPolygonF.
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=Qgis::DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
bool deleteRing(int ringNum, int partNum=0)
Deletes a ring in polygon or multipolygon.
QVector< QgsPointXY > randomPointsInPolygon(int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr, int maxTriesPerPoint=0) const
Returns a list of count random points generated inside a (multi)polygon geometry (if acceptPoint is s...
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
double area3D() const
Returns the 3-dimensional surface area of the geometry.
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...
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points shared by this geometry and other.
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 concaveHullOfPolygons(double lengthRatio, bool allowHoles=false, bool isTight=false, QgsFeedback *feedback=nullptr) const
Constructs a concave hull of a set of polygons, respecting the polygons as constraints.
QgsGeometry chamfer(int vertexIndex, double distance1, double distance2=-1.0) const
Creates a chamfer (angled corner) at the specified vertex.
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...
ChamferFilletOperationType
Privatly used in chamfer/fillet functions.
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...
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false, QgsFeedback *feedback=nullptr) const
Returns a possibly concave polygon that contains all the points in the geometry.
static QgsGeometry fromQPointF(QPointF point)
Construct geometry from a QPointF.
static QgsGeometry collectTinPatches(const QVector< QgsGeometry > &geometries)
Collects all patches from a list of TIN or Triangle geometries into a single TIN geometry.
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.
int makeDifferenceInPlace(const QgsGeometry &other, QgsFeedback *feedback=nullptr)
Changes this geometry such that it does not intersect the other 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.
bool isExactlyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry using the specified backend.
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 simplifyCoverageVW(double tolerance, bool preserveBoundary) const
Operates on a coverage (represented as a list of polygonal geometry with exactly matching edge geomet...
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry fillet(int vertexIndex, double radius, int segments=8) const
Creates a fillet (rounded corner) at the specified vertex.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
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 clipped(const QgsRectangle &rectangle, QgsFeedback *feedback=nullptr)
Clips the geometry using the specified rectangle.
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
void normalize()
Reorganizes the geometry into a normalized form (or "canonical" form).
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Returns the length of the QByteArray returned by asWkb().
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static Q_DECL_DEPRECATED QgsPolylineXY createPolylineFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolylineXY from a QPolygonF.
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
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.
friend class QgsInternalGeometryEngine
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
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...
Q_DECL_DEPRECATED 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.
QgsGeometry subdivide(int maxNodes=256, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Subdivides the geometry.
static Q_DECL_DEPRECATED QgsPolygonXY createPolygonFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolygonXYfrom a QPolygonF.
bool convertToSingleType()
Converts multi type geometry into single type geometry e.g.
Qgis::GeometryOperationResult addRing(const QVector< QgsPointXY > &ring)
Adds a new ring to this geometry.
Qgis::GeometryType type
bool requiresConversionToStraightSegments() const
Returns true if the geometry is a curved geometry type which requires conversion to display as straig...
bool isSimple() const
Determines whether the geometry is simple (according to OGC definition), i.e.
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new LineString geometry from a list of QgsPoint points.
void validateGeometry(QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Validates geometry and produces a list of geometry errors.
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
Qgis::GeometryOperationResult avoidIntersectionsV2(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QPointF asQPointF() const
Returns contents of the geometry as a QPointF if wkbType is WKBPoint, otherwise returns a null QPoint...
void convertToStraightSegment(double tolerance=M_PI/180., QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle)
Converts the geometry to straight line segments, if it is a curved geometry type.
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
QgsGeometry & operator=(QgsGeometry const &rhs)
Creates a shallow copy of the geometry.
QgsGeometry orthogonalize(double tolerance=1.0E-8, int maxIterations=1000, double angleThreshold=15.0) const
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries angl...
Qgis::AngularDirection polygonOrientation() const
Returns the orientation of the polygon.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QgsFeedback *feedback=nullptr) const
Attempts to make an invalid geometry valid without losing vertices.
QgsGeometry largestEmptyCircle(double tolerance, const QgsGeometry &boundary=QgsGeometry()) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult addPart(const QVector< QgsPointXY > &points, Qgis::GeometryType geomType=Qgis::GeometryType::Unknown)
Adds a new part to a the geometry.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr)
Compute the unary union on a list of geometries.
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 voronoiDiagram(const QgsGeometry &extent=QgsGeometry(), double tolerance=0.0, bool edgesOnly=false) const
Creates a Voronoi diagram for the nodes contained within the geometry.
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry minimumClearanceLine() const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
virtual ~QgsGeometry()
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygonXY.
double sqrDistToVertexAt(QgsPointXY &point, int atVertex) const
Returns the squared Cartesian distance between the given point to the given vertex index (vertex at t...
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments, QgsFeedback *feedback=nullptr) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
QVector< QgsGeometry > coerceToType(Qgis::WkbType type, double defaultZ=0, double defaultM=0, bool avoidDuplicates=true) const
Attempts to coerce this geometry into the specified destination type.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsGeometry node() const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false)
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
bool convertGeometryCollectionToSubclass(Qgis::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
QgsAbstractGeometry::part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QString asJson(int precision=17) const
Exports the geometry to a GeoJSON string.
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
double frechetDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
QgsGeometry unionCoverage() const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
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 ...
bool isTopologicallyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::GEOS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometryConstPartIterator constParts() const
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry simplify(double tolerance, QgsFeedback *feedback=nullptr) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult addPartV2(const QVector< QgsPointXY > &points, Qgis::WkbType wkbType=Qgis::WkbType::Unknown)
Adds a new part to a the geometry.
double minimumClearance() const
Computes the minimum clearance of a geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
Qgis::GeometryOperationResult reshapeGeometry(const QgsLineString &reshapeLineString)
Replaces a part of this geometry with another line.
bool isFuzzyEqual(const QgsGeometry &geometry, double epsilon, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry within the tolerance epsilon using the specified backend.
double closestVertexWithContext(const QgsPointXY &point, int &atVertex) const
Searches for the closest vertex in this geometry to the given point.
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false) const
Returns the Delaunay triangulation for the vertices of the geometry.
void draw(QPainter &p) const
Draws the geometry onto a QPainter.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
bool toggleCircularAtVertex(int atVertex)
Converts the vertex at the given position from/to circular.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
bool moveVertex(double x, double y, int atVertex)
Moves the vertex at the given position number and item (first number is index 0) to the given coordin...
QgsGeometry constrainedDelaunayTriangulation() const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
Q_DECL_DEPRECATED bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
static QgsGeometry fromBox3D(const QgsBox3D &box)
Creates a new geometry from a QgsBox3D object Returns a 2D polygon geometry if the box is purely 2d,...
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
static QgsGeometry createWedgeBufferFromAngles(const QgsPoint &center, double startAngle, double endAngle, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
bool deletePart(int partNum)
Deletes part identified by the part number.
QgsGeometry removeInteriorRings(double minimumAllowedArea=-1) const
Removes the interior rings from a (multi)polygon geometry.
static QgsGeometry fromPoint(const QgsPoint &point)
Creates a new geometry from a QgsPoint object.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
Q_DECL_DEPRECATED int avoidIntersections(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:175
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:587
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Buffers the geometry.
Definition qgsgeos.cpp:2106
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:796
double frechetDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
Definition qgsgeos.cpp:844
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:772
double frechetDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
Definition qgsgeos.cpp:820
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
Definition qgsgeos.cpp:3345
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.
Custom exception class when argument are invalid.
Line string geometry type, with support for z-dimension and m-values.
const double * yData() const
Returns a const pointer to the y vertex data.
const double * xData() const
Returns a const pointer to the x vertex data.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
static std::unique_ptr< QgsLineString > fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
int numPoints() const override
Returns the number of points in the curve.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Perform transforms between map coordinates and device coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Multi point geometry collection.
QgsPoint * pointN(int index)
Returns the point with the specified index.
Multi polygon geometry collection.
QgsPolygon * polygonN(int index)
Returns the polygon with the specified index.
Custom exception class which is raised when an operation is not supported.
Represents a 2D point.
Definition qgspointxy.h:62
void setY(double y)
Sets the y value of the point.
Definition qgspointxy.h:132
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
void setX(double x)
Sets the x value of the point.
Definition qgspointxy.h:122
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:168
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:138
double x
Definition qgspoint.h:56
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:734
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
Polyhedral surface geometry type.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
Triangle geometry type.
Definition qgstriangle.h:33
Triangulated surface geometry type.
Represents a vector layer which manages a vector based dataset.
Java-style iterator for traversal of vertices of a geometry.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition qgswkbtypes.h:53
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Q_INVOKABLE bool isNurbsType(Qgis::WkbType type)
Returns true if the WKB type is a NURBS curve type.
static Q_INVOKABLE bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static Qgis::WkbType curveType(Qgis::WkbType type)
Returns the curve type for a WKB type.
Contains geos related utilities and functions.
Definition qgsgeos.h:112
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:7621
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7259
#define BUILTIN_UNREACHABLE
Definition qgis.h:7657
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7620
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7077
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
Q_GLOBAL_STATIC_WITH_ARGS(PalPropertyList, palHiddenProperties,({ static_cast< int >(QgsPalLayerSettings::Property::PositionX), static_cast< int >(QgsPalLayerSettings::Property::PositionY), static_cast< int >(QgsPalLayerSettings::Property::Show), static_cast< int >(QgsPalLayerSettings::Property::LabelRotation), static_cast< int >(QgsPalLayerSettings::Property::Family), static_cast< int >(QgsPalLayerSettings::Property::FontStyle), static_cast< int >(QgsPalLayerSettings::Property::Size), static_cast< int >(QgsPalLayerSettings::Property::Bold), static_cast< int >(QgsPalLayerSettings::Property::Italic), static_cast< int >(QgsPalLayerSettings::Property::Underline), static_cast< int >(QgsPalLayerSettings::Property::Color), static_cast< int >(QgsPalLayerSettings::Property::Strikeout), static_cast< int >(QgsPalLayerSettings::Property::MultiLineAlignment), static_cast< int >(QgsPalLayerSettings::Property::BufferSize), static_cast< int >(QgsPalLayerSettings::Property::BufferDraw), static_cast< int >(QgsPalLayerSettings::Property::BufferColor), static_cast< int >(QgsPalLayerSettings::Property::LabelDistance), static_cast< int >(QgsPalLayerSettings::Property::Hali), static_cast< int >(QgsPalLayerSettings::Property::Vali), static_cast< int >(QgsPalLayerSettings::Property::ScaleVisibility), static_cast< int >(QgsPalLayerSettings::Property::MinScale), static_cast< int >(QgsPalLayerSettings::Property::MaxScale), static_cast< int >(QgsPalLayerSettings::Property::AlwaysShow), static_cast< int >(QgsPalLayerSettings::Property::CalloutDraw), static_cast< int >(QgsPalLayerSettings::Property::LabelAllParts) })) Q_GLOBAL_STATIC_WITH_ARGS(SymbolPropertyList
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QDataStream & operator<<(QDataStream &out, const QgsGeometry &geometry)
Writes the geometry to stream out. QGIS version compatibility is not guaranteed.
std::unique_ptr< QgsLineString > smoothCurve(const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing)
QDataStream & operator>>(QDataStream &in, QgsGeometry &geometry)
Reads a geometry from stream in into geometry. QGIS version compatibility is not guaranteed.
QCache< QString, QgsGeometry > WktCache
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item.
Definition qgsgeometry.h:92
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:98
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:63
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition qgsgeometry.h:72
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
std::unique_ptr< QgsAbstractGeometry > geometry
QgsGeometryPrivate(std::unique_ptr< QgsAbstractGeometry > geometry)
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:34
int vertex
Vertex number.
Definition qgsvertexid.h:99
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:50
int part
Part number.
Definition qgsvertexid.h:93
int ring
Ring number.
Definition qgsvertexid.h:96