QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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() );
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() );
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 ) );
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 ) );
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 // Workaround for issue issue GH #51429
1540 // in case of multi polygon, intersection with an empty rect fails
1541 if ( flatType == Qgis::WkbType::MultiPolygon && r.isEmpty() )
1542 {
1543 const QgsPointXY center { r.xMinimum(), r.yMinimum() };
1544 return contains( QgsGeometry::fromPointXY( center ) );
1545 }
1546
1547 QgsGeometry g = fromRect( r );
1548 return intersects( g );
1549}
1550
1551bool QgsGeometry::intersects( const QgsGeometry &geometry ) const
1552{
1553 if ( !d->geometry || geometry.isNull() )
1554 {
1555 return false;
1556 }
1557
1558 QgsGeos geos( d->geometry.get() );
1559 mLastError.clear();
1560 return geos.intersects( geometry.d->geometry.get(), &mLastError );
1561}
1562
1564{
1565 if ( !d->geometry )
1566 {
1567 return false;
1568 }
1569
1570 return d->geometry->boundingBoxIntersects( rectangle );
1571}
1572
1574{
1575 if ( !d->geometry || geometry.isNull() )
1576 {
1577 return false;
1578 }
1579
1580 return d->geometry->boundingBoxIntersects( geometry.constGet()->boundingBox() );
1581}
1582
1583bool QgsGeometry::contains( const QgsPointXY *p ) const
1584{
1585 if ( !d->geometry || !p )
1586 {
1587 return false;
1588 }
1589
1590 QgsGeos geos( d->geometry.get() );
1591 mLastError.clear();
1592 return geos.contains( p->x(), p->y(), &mLastError );
1593}
1594
1595bool QgsGeometry::contains( double x, double y ) const
1596{
1597 if ( !d->geometry )
1598 {
1599 return false;
1600 }
1601
1602 QgsGeos geos( d->geometry.get() );
1603 mLastError.clear();
1604 return geos.contains( x, y, &mLastError );
1605}
1606
1607bool QgsGeometry::contains( const QgsGeometry &geometry ) const
1608{
1609 if ( !d->geometry || geometry.isNull() )
1610 {
1611 return false;
1612 }
1613
1614 QgsGeos geos( d->geometry.get() );
1615 mLastError.clear();
1616 return geos.contains( geometry.d->geometry.get(), &mLastError );
1617}
1618
1619bool QgsGeometry::disjoint( const QgsGeometry &geometry ) const
1620{
1621 if ( !d->geometry || geometry.isNull() )
1622 {
1623 return false;
1624 }
1625
1626 QgsGeos geos( d->geometry.get() );
1627 mLastError.clear();
1628 return geos.disjoint( geometry.d->geometry.get(), &mLastError );
1629}
1630
1631bool QgsGeometry::equals( const QgsGeometry &geometry ) const
1632{
1633 if ( !d->geometry || geometry.isNull() )
1634 {
1635 return false;
1636 }
1637
1638 // fast check - are they shared copies of the same underlying geometry?
1639 if ( d == geometry.d )
1640 return true;
1641
1642 // fast check - distinct geometry types?
1643 if ( type() != geometry.type() )
1644 return false;
1645
1646 // slower check - actually test the geometries
1647 return *d->geometry == *geometry.d->geometry;
1648}
1649
1650bool QgsGeometry::touches( 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.touches( geometry.d->geometry.get(), &mLastError );
1660}
1661
1662bool QgsGeometry::overlaps( 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.overlaps( geometry.d->geometry.get(), &mLastError );
1672}
1673
1674bool QgsGeometry::within( 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.within( geometry.d->geometry.get(), &mLastError );
1684}
1685
1686bool QgsGeometry::crosses( const QgsGeometry &geometry ) const
1687{
1688 if ( !d->geometry || geometry.isNull() )
1689 {
1690 return false;
1691 }
1692
1693 QgsGeos geos( d->geometry.get() );
1694 mLastError.clear();
1695 return geos.crosses( geometry.d->geometry.get(), &mLastError );
1696}
1697
1698QString QgsGeometry::asWkt( int precision ) const
1699{
1700 if ( !d->geometry )
1701 {
1702 return QString();
1703 }
1704 return d->geometry->asWkt( precision );
1705}
1706
1707QString QgsGeometry::asJson( int precision ) const
1708{
1709 return QString::fromStdString( asJsonObject( precision ).dump() );
1710}
1711
1712json QgsGeometry::asJsonObject( int precision ) const
1713{
1714 if ( !d->geometry )
1715 {
1716 return nullptr;
1717 }
1718 return d->geometry->asJsonObject( precision );
1719}
1720
1721QVector<QgsGeometry> QgsGeometry::coerceToType( const Qgis::WkbType type, double defaultZ, double defaultM, bool avoidDuplicates ) const
1722{
1723 mLastError.clear();
1724 QVector< QgsGeometry > res;
1725 if ( isNull() )
1726 return res;
1727
1728 if ( wkbType() == type || type == Qgis::WkbType::Unknown )
1729 {
1730 res << *this;
1731 return res;
1732 }
1733
1735 {
1736 return res;
1737 }
1738
1739 QgsGeometry newGeom = *this;
1740
1741 // Curved -> straight
1743 {
1744 newGeom = QgsGeometry( d->geometry.get()->segmentize() );
1745 }
1746
1747 // Handle NurbsCurve: if target is curved but NOT NurbsCurve, and source contains NurbsCurve,
1748 // we need to segmentize the NURBS parts first
1750 {
1751 // Check if geometry contains NurbsCurve that needs conversion
1752 bool hasNurbs = false;
1753 if ( QgsWkbTypes::isNurbsType( newGeom.wkbType() ) )
1754 {
1755 hasNurbs = true;
1756 }
1757 else if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( newGeom.constGet() ) )
1758 {
1759 for ( int i = 0; i < collection->numGeometries(); ++i )
1760 {
1761 if ( QgsWkbTypes::isNurbsType( collection->geometryN( i )->wkbType() ) )
1762 {
1763 hasNurbs = true;
1764 break;
1765 }
1766 }
1767 }
1768 else if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( newGeom.constGet() ) )
1769 {
1770 if ( cp->exteriorRing() && QgsWkbTypes::isNurbsType( cp->exteriorRing()->wkbType() ) )
1771 hasNurbs = true;
1772 for ( int i = 0; !hasNurbs && i < cp->numInteriorRings(); ++i )
1773 {
1774 if ( QgsWkbTypes::isNurbsType( cp->interiorRing( i )->wkbType() ) )
1775 hasNurbs = true;
1776 }
1777 }
1779 {
1780 for ( int i = 0; i < cc->nCurves(); ++i )
1781 {
1782 if ( QgsWkbTypes::isNurbsType( cc->curveAt( i )->wkbType() ) )
1783 {
1784 hasNurbs = true;
1785 break;
1786 }
1787 }
1788 }
1789
1790 if ( hasNurbs )
1791 {
1792 // Segmentize to remove NURBS, then we'll convert back to curve type below
1793 newGeom = QgsGeometry( newGeom.constGet()->segmentize() );
1794 }
1795 }
1796
1797 // polygon -> line
1799 {
1800 // boundary gives us a (multi)line string of exterior + interior rings
1801 newGeom = QgsGeometry( newGeom.constGet()->boundary() );
1802 }
1803 // line -> polygon
1805 {
1806 std::unique_ptr< QgsGeometryCollection > gc( QgsGeometryFactory::createCollectionOfType( type ) );
1807 const QgsGeometry source = newGeom;
1808 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1809 {
1810 std::unique_ptr< QgsAbstractGeometry > exterior( ( *part )->clone() );
1811 if ( QgsCurve *curve = qgsgeometry_cast< QgsCurve * >( exterior.get() ) )
1812 {
1814 {
1815 auto cp = std::make_unique< QgsCurvePolygon >();
1816 cp->setExteriorRing( curve );
1817 ( void ) exterior.release();
1818 gc->addGeometry( cp.release() );
1819 }
1820 else
1821 {
1822 auto p = std::make_unique< QgsPolygon >();
1823 p->setExteriorRing( qgsgeometry_cast< QgsLineString * >( curve ) );
1824 ( void ) exterior.release();
1825 gc->addGeometry( p.release() );
1826 }
1827 }
1828 }
1829 newGeom = QgsGeometry( std::move( gc ) );
1830 }
1831
1832 // line/polygon -> points
1834 {
1835 // lines/polygons to a point layer, extract all vertices
1836 auto mp = std::make_unique< QgsMultiPoint >();
1837 const QgsGeometry source = newGeom;
1838 QSet< QgsPoint > added;
1839 for ( auto vertex = source.vertices_begin(); vertex != source.vertices_end(); ++vertex )
1840 {
1841 if ( avoidDuplicates && added.contains( *vertex ) )
1842 continue; // avoid duplicate points, e.g. start/end of rings
1843 mp->addGeometry( ( *vertex ).clone() );
1844 added.insert( *vertex );
1845 }
1846 newGeom = QgsGeometry( std::move( mp ) );
1847 }
1848
1849 //(Multi)Polygon to PolyhedralSurface
1851 {
1852 auto polySurface = std::make_unique< QgsPolyhedralSurface >();
1853 const QgsGeometry source = newGeom;
1854 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1855 {
1856 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1857 {
1858 polySurface->addPatch( polygon->clone() );
1859 }
1860 }
1861 newGeom = QgsGeometry( std::move( polySurface ) );
1862 }
1863
1864 //(Multi)Polygon/Triangle to TIN
1867 {
1868 auto tin = std::make_unique< QgsTriangulatedSurface >();
1869 const QgsGeometry source = newGeom;
1870 for ( auto part = source.const_parts_begin(); part != source.const_parts_end(); ++part )
1871 {
1872 if ( const QgsTriangle *triangle = qgsgeometry_cast< const QgsTriangle * >( *part ) )
1873 {
1874 tin->addPatch( triangle->clone() );
1875 }
1876 else if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *part ) )
1877 {
1878 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
1879 if ( polygon->exteriorRing() )
1880 {
1881 const int numPoints = polygon->exteriorRing()->numPoints();
1882 if ( numPoints != 4 )
1883 {
1884 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
1885 return res;
1886 }
1887 auto triangle = std::make_unique< QgsTriangle >();
1888 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
1889 tin->addPatch( triangle.release() );
1890 }
1891 }
1892 }
1893 newGeom = QgsGeometry( std::move( tin ) );
1894 }
1895
1896 // PolyhedralSurface/TIN to (Multi)Polygon
1899 {
1900 auto multiPolygon = std::make_unique< QgsMultiPolygon >();
1902 {
1903 for ( int i = 0; i < polySurface->numPatches(); ++i )
1904 {
1905 const QgsPolygon *patch = polySurface->patchN( i );
1906 auto polygon = std::make_unique< QgsPolygon >();
1907 polygon->setExteriorRing( patch->exteriorRing()->clone() );
1908 for ( int j = 0; j < patch->numInteriorRings(); ++j )
1909 {
1910 polygon->addInteriorRing( patch->interiorRing( j )->clone() );
1911 }
1912 multiPolygon->addGeometry( polygon.release() );
1913 }
1914 }
1915 newGeom = QgsGeometry( std::move( multiPolygon ) );
1916 }
1917
1918 // Polygon -> Triangle
1920 {
1921 if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( newGeom.constGet() ) )
1922 {
1923 // Validate that the polygon can be converted to a triangle (must have exactly 3 vertices + closing point)
1924 if ( polygon->exteriorRing() )
1925 {
1926 const int numPoints = polygon->exteriorRing()->numPoints();
1927 if ( numPoints != 4 )
1928 {
1929 mLastError = QObject::tr( "Cannot convert polygon with %1 vertices to a triangle. A triangle requires exactly 3 vertices." ).arg( numPoints > 0 ? numPoints - 1 : 0 );
1930 return res;
1931 }
1932 auto triangle = std::make_unique< QgsTriangle >();
1933 triangle->setExteriorRing( polygon->exteriorRing()->clone() );
1934 newGeom = QgsGeometry( std::move( triangle ) );
1935 }
1936 }
1937 }
1938
1939
1940 // Single -> multi
1941 if ( QgsWkbTypes::isMultiType( type ) && !newGeom.isMultipart() )
1942 {
1943 newGeom.convertToMultiType();
1944 }
1945 // Drop Z/M
1946 if ( newGeom.constGet()->is3D() && !QgsWkbTypes::hasZ( type ) )
1947 {
1948 newGeom.get()->dropZValue();
1949 }
1950 if ( newGeom.constGet()->isMeasure() && !QgsWkbTypes::hasM( type ) )
1951 {
1952 newGeom.get()->dropMValue();
1953 }
1954 // Add Z/M back, set to 0
1955 if ( !newGeom.constGet()->is3D() && QgsWkbTypes::hasZ( type ) )
1956 {
1957 newGeom.get()->addZValue( defaultZ );
1958 }
1959 if ( !newGeom.constGet()->isMeasure() && QgsWkbTypes::hasM( type ) )
1960 {
1961 newGeom.get()->addMValue( defaultM );
1962 }
1963
1964 // Straight -> curve
1966 {
1967 newGeom.convertToCurvedMultiType();
1968 }
1969
1970 // Multi -> single
1971 if ( !QgsWkbTypes::isMultiType( type ) && newGeom.isMultipart() )
1972 {
1973 const QgsGeometryCollection *parts( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1974 res.reserve( parts->partCount() );
1975 for ( int i = 0; i < parts->partCount(); i++ )
1976 {
1977 res << QgsGeometry( parts->geometryN( i )->clone() );
1978 }
1979 }
1980 // GeometryCollection (of Point/LineString/Polygon) -> MultiPoint/MultiLineString/MultiPolygon
1982 {
1984 const QgsGeometryCollection *geomColl( static_cast< const QgsGeometryCollection * >( newGeom.constGet() ) );
1985
1986 bool allExpectedType = true;
1987 for ( int i = 0; i < geomColl->numGeometries(); ++i )
1988 {
1989 if ( geomColl->geometryN( i )->wkbType() != singleType )
1990 {
1991 allExpectedType = false;
1992 break;
1993 }
1994 }
1995 if ( allExpectedType )
1996 {
1997 std::unique_ptr< QgsGeometryCollection > newGeomCol;
1999 {
2000 newGeomCol = std::make_unique< QgsMultiPoint >();
2001 }
2003 {
2004 newGeomCol = std::make_unique< QgsMultiLineString >();
2005 }
2006 else
2007 {
2008 newGeomCol = std::make_unique< QgsMultiPolygon >();
2009 }
2010 newGeomCol->reserve( geomColl->numGeometries() );
2011 for ( int i = 0; i < geomColl->numGeometries(); ++i )
2012 {
2013 newGeomCol->addGeometry( geomColl->geometryN( i )->clone() );
2014 }
2015 res << QgsGeometry( std::move( newGeomCol ) );
2016 }
2017 else
2018 {
2019 res << newGeom;
2020 }
2021 }
2022 else
2023 {
2024 res << newGeom;
2025 }
2026 return res;
2027}
2028
2029QgsGeometry QgsGeometry::convertToType( Qgis::GeometryType destType, bool destMultipart ) const
2030{
2031 switch ( destType )
2032 {
2034 return convertToPoint( destMultipart );
2035
2037 return convertToLine( destMultipart );
2038
2040 return convertToPolygon( destMultipart );
2041
2042 default:
2043 return QgsGeometry();
2044 }
2045}
2046
2048{
2049 if ( !d->geometry )
2050 {
2051 return false;
2052 }
2053
2054 if ( isMultipart() ) //already multitype, no need to convert
2055 {
2056 return true;
2057 }
2058
2059 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::multiType( d->geometry->wkbType() ) );
2061 if ( !multiGeom )
2062 {
2063 return false;
2064 }
2065
2066 //try to avoid cloning existing geometry whenever we can
2067
2068 //want to see a magic trick?... gather round kiddies...
2069 detach(); // maybe a clone, hopefully not if we're the only ref to the private data
2070 // now we cheat a bit and steal the private geometry and add it direct to the multigeom
2071 // we can do this because we're the only ref to this geometry, guaranteed by the detach call above
2072 multiGeom->addGeometry( d->geometry.release() );
2073 // and replace it with the multi geometry.
2074 // TADA! a clone free conversion in some cases
2075 d->geometry = std::move( geom );
2076 return true;
2077}
2078
2080{
2081 if ( !d->geometry )
2082 {
2083 return false;
2084 }
2085
2086 switch ( QgsWkbTypes::flatType( d->geometry->wkbType() ) )
2087 {
2092 {
2093 return true;
2094 }
2095 default:
2096 break;
2097 }
2098
2099 std::unique_ptr< QgsAbstractGeometry > geom = QgsGeometryFactory::geomFromWkbType( QgsWkbTypes::curveType( QgsWkbTypes::multiType( d->geometry->wkbType() ) ) );
2101 if ( !multiGeom )
2102 {
2103 return false;
2104 }
2105
2106 QgsGeometryCollection *sourceMultiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2107 if ( sourceMultiGeom )
2108 {
2109 for ( int i = 0; i < sourceMultiGeom->numGeometries(); ++i )
2110 {
2111 if ( !multiGeom->addGeometry( sourceMultiGeom->geometryN( i )->clone() ) )
2112 return false;
2113 }
2114 }
2115 else
2116 {
2117 if ( !multiGeom->addGeometry( d->geometry->clone() ) )
2118 return false;
2119 }
2120
2121 reset( std::move( geom ) );
2122 return true;
2123}
2124
2126{
2127 if ( !d->geometry )
2128 {
2129 return false;
2130 }
2131
2132 if ( !isMultipart() ) //already single part, no need to convert
2133 {
2134 return true;
2135 }
2136
2137 QgsGeometryCollection *multiGeom = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2138 if ( !multiGeom || multiGeom->partCount() < 1 )
2139 return false;
2140
2141 std::unique_ptr< QgsAbstractGeometry > firstPart( multiGeom->geometryN( 0 )->clone() );
2142 reset( std::move( firstPart ) );
2143 return true;
2144}
2145
2146
2148{
2150 if ( !origGeom )
2151 return false;
2152
2153 std::unique_ptr<QgsGeometryCollection> resGeom;
2154 switch ( geomType )
2155 {
2157 resGeom = std::make_unique<QgsMultiPoint>();
2158 break;
2160 resGeom = std::make_unique<QgsMultiLineString>();
2161 break;
2163 resGeom = std::make_unique<QgsMultiPolygon>();
2164 break;
2165 default:
2166 break;
2167 }
2168 if ( !resGeom )
2169 return false;
2170
2171 resGeom->reserve( origGeom->numGeometries() );
2172 for ( int i = 0; i < origGeom->numGeometries(); ++i )
2173 {
2174 const QgsAbstractGeometry *g = origGeom->geometryN( i );
2175 if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
2176 resGeom->addGeometry( g->clone() );
2177 }
2178
2179 set( resGeom.release() );
2180 return true;
2181}
2182
2183
2185{
2186 if ( !d->geometry )
2187 {
2188 return QgsPointXY();
2189 }
2190 if ( const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( d->geometry->simplifiedTypeRef() ) )
2191 {
2192 return QgsPointXY( pt->x(), pt->y() );
2193 }
2194 else
2195 {
2196 return QgsPointXY();
2197 }
2198}
2199
2201{
2202 QgsPolylineXY polyLine;
2203 if ( !d->geometry )
2204 {
2205 return polyLine;
2206 }
2207
2208 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CompoundCurve || QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CircularString );
2209 std::unique_ptr< QgsLineString > segmentizedLine;
2210 QgsLineString *line = nullptr;
2211 if ( doSegmentation )
2212 {
2213 QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( d->geometry.get() );
2214 if ( !curve )
2215 {
2216 return polyLine;
2217 }
2218 segmentizedLine.reset( curve->curveToLine() );
2219 line = segmentizedLine.get();
2220 }
2221 else
2222 {
2223 line = qgsgeometry_cast<QgsLineString *>( d->geometry.get() );
2224 if ( !line )
2225 {
2226 return polyLine;
2227 }
2228 }
2229
2230 int nVertices = line->numPoints();
2231 polyLine.resize( nVertices );
2232 QgsPointXY *data = polyLine.data();
2233 const double *xData = line->xData();
2234 const double *yData = line->yData();
2235 for ( int i = 0; i < nVertices; ++i )
2236 {
2237 data->setX( *xData++ );
2238 data->setY( *yData++ );
2239 data++;
2240 }
2241
2242 return polyLine;
2243}
2244
2246{
2247 if ( !d->geometry )
2248 return QgsPolygonXY();
2249
2250 bool doSegmentation = ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::CurvePolygon );
2251
2252 QgsPolygon *p = nullptr;
2253 std::unique_ptr< QgsPolygon > segmentized;
2254 if ( doSegmentation )
2255 {
2256 QgsCurvePolygon *curvePoly = qgsgeometry_cast<QgsCurvePolygon *>( d->geometry.get() );
2257 if ( !curvePoly )
2258 {
2259 return QgsPolygonXY();
2260 }
2261 segmentized.reset( curvePoly->toPolygon() );
2262 p = segmentized.get();
2263 }
2264 else
2265 {
2266 p = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
2267 }
2268
2269 if ( !p )
2270 {
2271 return QgsPolygonXY();
2272 }
2273
2274 QgsPolygonXY polygon;
2275 convertPolygon( *p, polygon );
2276
2277 return polygon;
2278}
2279
2281{
2282 if ( !d->geometry || QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPoint )
2283 {
2284 return QgsMultiPointXY();
2285 }
2286
2287 const QgsMultiPoint *mp = qgsgeometry_cast<QgsMultiPoint *>( d->geometry.get() );
2288 if ( !mp )
2289 {
2290 return QgsMultiPointXY();
2291 }
2292
2293 int nPoints = mp->numGeometries();
2294 QgsMultiPointXY multiPoint( nPoints );
2295 for ( int i = 0; i < nPoints; ++i )
2296 {
2297 const QgsPoint *pt = mp->pointN( i );
2298 multiPoint[i].setX( pt->x() );
2299 multiPoint[i].setY( pt->y() );
2300 }
2301 return multiPoint;
2302}
2303
2305{
2306 if ( !d->geometry )
2307 {
2308 return QgsMultiPolylineXY();
2309 }
2310
2311 QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( d->geometry.get() );
2312 if ( !geomCollection )
2313 {
2314 return QgsMultiPolylineXY();
2315 }
2316
2317 int nLines = geomCollection->numGeometries();
2318 if ( nLines < 1 )
2319 {
2320 return QgsMultiPolylineXY();
2321 }
2322
2324 mpl.reserve( nLines );
2325 for ( int i = 0; i < nLines; ++i )
2326 {
2327 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( geomCollection->geometryN( i ) );
2328 std::unique_ptr< QgsLineString > segmentized;
2329 if ( !line )
2330 {
2331 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geomCollection->geometryN( i ) );
2332 if ( !curve )
2333 {
2334 continue;
2335 }
2336 segmentized.reset( curve->curveToLine() );
2337 line = segmentized.get();
2338 }
2339
2340 QgsPolylineXY polyLine;
2341 int nVertices = line->numPoints();
2342 polyLine.resize( nVertices );
2343 QgsPointXY *data = polyLine.data();
2344 const double *xData = line->xData();
2345 const double *yData = line->yData();
2346 for ( int i = 0; i < nVertices; ++i )
2347 {
2348 data->setX( *xData++ );
2349 data->setY( *yData++ );
2350 data++;
2351 }
2352 mpl.append( polyLine );
2353 }
2354 return mpl;
2355}
2356
2358{
2359 if ( !d->geometry )
2360 {
2361 return QgsMultiPolygonXY();
2362 }
2363
2364 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( d->geometry.get() );
2365 if ( !geomCollection )
2366 {
2367 return QgsMultiPolygonXY();
2368 }
2369
2370 const int nPolygons = geomCollection->numGeometries();
2371 if ( nPolygons < 1 )
2372 {
2373 return QgsMultiPolygonXY();
2374 }
2375
2377 mp.reserve( nPolygons );
2378 for ( int i = 0; i < nPolygons; ++i )
2379 {
2380 const QgsPolygon *polygon = qgsgeometry_cast<const QgsPolygon *>( geomCollection->geometryN( i ) );
2381 if ( !polygon )
2382 {
2383 const QgsCurvePolygon *cPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( geomCollection->geometryN( i ) );
2384 if ( cPolygon )
2385 {
2386 polygon = cPolygon->toPolygon();
2387 }
2388 else
2389 {
2390 continue;
2391 }
2392 }
2393
2394 QgsPolygonXY poly;
2395 convertPolygon( *polygon, poly );
2396 mp.push_back( poly );
2397 }
2398 return mp;
2399}
2400
2401double QgsGeometry::area() const
2402{
2403 if ( !d->geometry )
2404 {
2405 return -1.0;
2406 }
2407
2408 return d->geometry->area();
2409}
2410
2412{
2413 if ( !d->geometry )
2414 {
2415 throw QgsInvalidArgumentException( "Cannot compute 3D area: geometry is null." );
2416 }
2417
2418 return d->geometry->area3D();
2419}
2420
2422{
2423 if ( !d->geometry )
2424 {
2425 return -1.0;
2426 }
2427
2428 switch ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) )
2429 {
2431 return 0.0;
2432
2434 return d->geometry->length();
2435
2437 return d->geometry->perimeter();
2438
2441 return d->geometry->length();
2442 }
2443 return -1;
2444}
2445
2446double QgsGeometry::distance( const QgsGeometry &geom ) const
2447{
2448 if ( !d->geometry || !geom.d->geometry )
2449 {
2450 return -1.0;
2451 }
2452
2453 // avoid calling geos for trivial point-to-point distance calculations
2454 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( geom.wkbType() ) == Qgis::WkbType::Point )
2455 {
2456 return qgsgeometry_cast< const QgsPoint * >( d->geometry.get() )->distance( *qgsgeometry_cast< const QgsPoint * >( geom.constGet() ) );
2457 }
2458
2459 QgsGeos g( d->geometry.get() );
2460 mLastError.clear();
2461 return g.distance( geom.d->geometry.get(), &mLastError );
2462}
2463
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.hausdorffDistance( geom.d->geometry.get(), &mLastError );
2474}
2475
2476double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2477{
2478 if ( !d->geometry || !geom.d->geometry )
2479 {
2480 return -1.0;
2481 }
2482
2483 QgsGeos g( d->geometry.get() );
2484 mLastError.clear();
2485 return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2486}
2487
2488
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.frechetDistance( geom.d->geometry.get(), &mLastError );
2499}
2500
2501double QgsGeometry::frechetDistanceDensify( const QgsGeometry &geom, double densifyFraction ) const
2502{
2503 if ( !d->geometry || !geom.d->geometry )
2504 {
2505 return -1.0;
2506 }
2507
2508 QgsGeos g( d->geometry.get() );
2509 mLastError.clear();
2510 return g.frechetDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
2511}
2512
2514{
2515 if ( !d->geometry || d->geometry.get()->isEmpty() )
2517 return d->geometry->vertices_begin();
2518}
2519
2521{
2522 if ( !d->geometry || d->geometry.get()->isEmpty() )
2524 return d->geometry->vertices_end();
2525}
2526
2528{
2529 if ( !d->geometry || d->geometry.get()->isEmpty() )
2530 return QgsVertexIterator();
2531 return QgsVertexIterator( d->geometry.get() );
2532}
2533
2535{
2536 if ( !d->geometry )
2538
2539 detach();
2540 return d->geometry->parts_begin();
2541}
2542
2544{
2545 if ( !d->geometry )
2547 return d->geometry->parts_end();
2548}
2549
2551{
2552 if ( !d->geometry )
2554 return d->geometry->const_parts_begin();
2555}
2556
2558{
2559 if ( !d->geometry )
2561 return d->geometry->const_parts_end();
2562}
2563
2565{
2566 if ( !d->geometry )
2567 return QgsGeometryPartIterator();
2568
2569 detach();
2570 return QgsGeometryPartIterator( d->geometry.get() );
2571}
2572
2574{
2575 if ( !d->geometry )
2577
2578 return QgsGeometryConstPartIterator( d->geometry.get() );
2579}
2580
2581QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
2582{
2583 if ( !d->geometry )
2584 {
2585 return QgsGeometry();
2586 }
2587
2588 QgsGeos g( d->geometry.get() );
2589 mLastError.clear();
2590 std::unique_ptr<QgsAbstractGeometry> geom( g.buffer( distance, segments, &mLastError ) );
2591 if ( !geom )
2592 {
2593 QgsGeometry result;
2594 result.mLastError = mLastError;
2595 return result;
2596 }
2597 return QgsGeometry( std::move( geom ) );
2598}
2599
2600QgsGeometry QgsGeometry::buffer( double distance, int segments, Qgis::EndCapStyle endCapStyle, Qgis::JoinStyle joinStyle, double miterLimit ) const
2601{
2602 if ( !d->geometry )
2603 {
2604 return QgsGeometry();
2605 }
2606
2607 QgsGeos g( d->geometry.get() );
2608 mLastError.clear();
2609 QgsAbstractGeometry *geom = g.buffer( distance, segments, endCapStyle, joinStyle, miterLimit, &mLastError );
2610 if ( !geom )
2611 {
2612 QgsGeometry result;
2613 result.mLastError = mLastError;
2614 return result;
2615 }
2616 return QgsGeometry( geom );
2617}
2618
2619QgsGeometry QgsGeometry::offsetCurve( double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit ) const
2620{
2621 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2622 {
2623 return QgsGeometry();
2624 }
2625
2626 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2627 {
2628 const QVector<QgsGeometry> parts = asGeometryCollection();
2629 QVector<QgsGeometry> results;
2630 results.reserve( parts.count() );
2631 for ( const QgsGeometry &part : parts )
2632 {
2633 QgsGeometry result = part.offsetCurve( distance, segments, joinStyle, miterLimit );
2634 if ( !result.isNull() )
2635 results << result;
2636 }
2637 if ( results.isEmpty() )
2638 return QgsGeometry();
2639
2640 QgsGeometry first = results.takeAt( 0 );
2641 for ( const QgsGeometry &result : std::as_const( results ) )
2642 {
2643 first.addPart( result );
2644 }
2645 return first;
2646 }
2647 else
2648 {
2649 QgsGeos geos( d->geometry.get() );
2650 mLastError.clear();
2651
2652 // GEOS can flip the curve orientation in some circumstances. So record previous orientation and correct if required
2653 const Qgis::AngularDirection prevOrientation = qgsgeometry_cast< const QgsCurve * >( d->geometry.get() )->orientation();
2654
2655 std::unique_ptr< QgsAbstractGeometry > offsetGeom( geos.offsetCurve( distance, segments, joinStyle, miterLimit, &mLastError ) );
2656 if ( !offsetGeom )
2657 {
2658 QgsGeometry result;
2659 result.mLastError = mLastError;
2660 return result;
2661 }
2662
2663 if ( const QgsCurve *offsetCurve = qgsgeometry_cast< const QgsCurve * >( offsetGeom.get() ) )
2664 {
2665 const Qgis::AngularDirection newOrientation = offsetCurve->orientation();
2666 if ( newOrientation != prevOrientation )
2667 {
2668 // GEOS has flipped line orientation, flip it back
2669 std::unique_ptr< QgsAbstractGeometry > flipped( offsetCurve->reversed() );
2670 offsetGeom = std::move( flipped );
2671 }
2672 }
2673 return QgsGeometry( std::move( offsetGeom ) );
2674 }
2675}
2676
2677QgsGeometry QgsGeometry::singleSidedBuffer( double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle, double miterLimit ) const
2678{
2679 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2680 {
2681 return QgsGeometry();
2682 }
2683
2684 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2685 {
2686 const QVector<QgsGeometry> parts = asGeometryCollection();
2687 QVector<QgsGeometry> results;
2688 results.reserve( parts.count() );
2689 for ( const QgsGeometry &part : parts )
2690 {
2691 QgsGeometry result = part.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit );
2692 if ( !result.isNull() )
2693 results << result;
2694 }
2695 if ( results.isEmpty() )
2696 return QgsGeometry();
2697
2698 QgsGeometry first = results.takeAt( 0 );
2699 for ( const QgsGeometry &result : std::as_const( results ) )
2700 {
2701 first.addPart( result );
2702 }
2703 return first;
2704 }
2705 else
2706 {
2707 QgsGeos geos( d->geometry.get() );
2708 mLastError.clear();
2709 std::unique_ptr< QgsAbstractGeometry > bufferGeom = geos.singleSidedBuffer( distance, segments, side, joinStyle, miterLimit, &mLastError );
2710 if ( !bufferGeom )
2711 {
2712 QgsGeometry result;
2713 result.mLastError = mLastError;
2714 return result;
2715 }
2716 return QgsGeometry( std::move( bufferGeom ) );
2717 }
2718}
2719
2720QgsGeometry QgsGeometry::taperedBuffer( double startWidth, double endWidth, int segments ) const
2721{
2722 QgsInternalGeometryEngine engine( *this );
2723
2724 return engine.taperedBuffer( startWidth, endWidth, segments );
2725}
2726
2728{
2729 QgsInternalGeometryEngine engine( *this );
2730
2731 return engine.variableWidthBufferByM( segments );
2732}
2733
2734QgsGeometry QgsGeometry::extendLine( double startDistance, double endDistance ) const
2735{
2736 if ( !d->geometry || type() != Qgis::GeometryType::Line )
2737 {
2738 return QgsGeometry();
2739 }
2740
2741 if ( QgsWkbTypes::isMultiType( d->geometry->wkbType() ) )
2742 {
2743 const QVector<QgsGeometry> parts = asGeometryCollection();
2744 QVector<QgsGeometry> results;
2745 results.reserve( parts.count() );
2746 for ( const QgsGeometry &part : parts )
2747 {
2748 QgsGeometry result = part.extendLine( startDistance, endDistance );
2749 if ( !result.isNull() )
2750 results << result;
2751 }
2752 if ( results.isEmpty() )
2753 return QgsGeometry();
2754
2755 QgsGeometry first = results.takeAt( 0 );
2756 for ( const QgsGeometry &result : std::as_const( results ) )
2757 {
2758 first.addPart( result );
2759 }
2760 return first;
2761 }
2762 else
2763 {
2764 QgsLineString *line = qgsgeometry_cast< QgsLineString * >( d->geometry.get() );
2765 if ( !line )
2766 return QgsGeometry();
2767
2768 std::unique_ptr< QgsLineString > newLine( line->clone() );
2769 newLine->extend( startDistance, endDistance );
2770 return QgsGeometry( std::move( newLine ) );
2771 }
2772}
2773
2774QgsGeometry QgsGeometry::simplify( double tolerance ) const
2775{
2776 if ( !d->geometry )
2777 {
2778 return QgsGeometry();
2779 }
2780
2781 QgsGeos geos( d->geometry.get() );
2782 mLastError.clear();
2783 std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( tolerance, &mLastError ) );
2784 if ( !simplifiedGeom )
2785 {
2786 QgsGeometry result;
2787 result.mLastError = mLastError;
2788 return result;
2789 }
2790 return QgsGeometry( std::move( simplifiedGeom ) );
2791}
2792
2793QgsGeometry QgsGeometry::densifyByCount( int extraNodesPerSegment ) const
2794{
2795 QgsInternalGeometryEngine engine( *this );
2796
2797 return engine.densifyByCount( extraNodesPerSegment );
2798}
2799
2801{
2802 QgsInternalGeometryEngine engine( *this );
2803
2804 return engine.densifyByDistance( distance );
2805}
2806
2807QgsGeometry QgsGeometry::convertToCurves( double distanceTolerance, double angleTolerance ) const
2808{
2809 QgsInternalGeometryEngine engine( *this );
2810
2811 return engine.convertToCurves( distanceTolerance, angleTolerance );
2812}
2813
2815{
2816 if ( !d->geometry )
2817 {
2818 return QgsGeometry();
2819 }
2820
2821 // avoid calling geos for trivial point centroids
2822 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point )
2823 {
2824 QgsGeometry c = *this;
2825 c.get()->dropZValue();
2826 c.get()->dropMValue();
2827 return c;
2828 }
2829
2830 QgsGeos geos( d->geometry.get() );
2831
2832 mLastError.clear();
2833 QgsGeometry result( geos.centroid( &mLastError ) );
2834 result.mLastError = mLastError;
2835 return result;
2836}
2837
2839{
2840 if ( !d->geometry )
2841 {
2842 return QgsGeometry();
2843 }
2844
2845 QgsGeos geos( d->geometry.get() );
2846
2847 mLastError.clear();
2848 QgsGeometry result( geos.pointOnSurface( &mLastError ) );
2849 result.mLastError = mLastError;
2850 return result;
2851}
2852
2853QgsGeometry QgsGeometry::poleOfInaccessibility( double precision, double *distanceToBoundary ) const
2854{
2855 QgsInternalGeometryEngine engine( *this );
2856
2857 return engine.poleOfInaccessibility( precision, distanceToBoundary );
2858}
2859
2860QgsGeometry QgsGeometry::largestEmptyCircle( double tolerance, const QgsGeometry &boundary ) const
2861{
2862 if ( !d->geometry )
2863 {
2864 return QgsGeometry();
2865 }
2866
2867 QgsGeos geos( d->geometry.get() );
2868
2869 mLastError.clear();
2870 QgsGeometry result( geos.largestEmptyCircle( tolerance, boundary.constGet(), &mLastError ) );
2871 result.mLastError = mLastError;
2872 return result;
2873}
2874
2876{
2877 if ( !d->geometry )
2878 {
2879 return QgsGeometry();
2880 }
2881
2882 QgsGeos geos( d->geometry.get() );
2883
2884 mLastError.clear();
2885 QgsGeometry result( geos.minimumWidth( &mLastError ) );
2886 result.mLastError = mLastError;
2887 return result;
2888}
2889
2891{
2892 if ( !d->geometry )
2893 {
2894 return std::numeric_limits< double >::quiet_NaN();
2895 }
2896
2897 QgsGeos geos( d->geometry.get() );
2898
2899 mLastError.clear();
2900 return geos.minimumClearance( &mLastError );
2901}
2902
2904{
2905 if ( !d->geometry )
2906 {
2907 return QgsGeometry();
2908 }
2909
2910 QgsGeos geos( d->geometry.get() );
2911
2912 mLastError.clear();
2913 QgsGeometry result( geos.minimumClearanceLine( &mLastError ) );
2914 result.mLastError = mLastError;
2915 return result;
2916}
2917
2919{
2920 if ( !d->geometry )
2921 {
2922 return QgsGeometry();
2923 }
2924 QgsGeos geos( d->geometry.get() );
2925 mLastError.clear();
2926 std::unique_ptr< QgsAbstractGeometry > cHull( geos.convexHull( &mLastError ) );
2927 if ( !cHull )
2928 {
2929 QgsGeometry geom;
2930 geom.mLastError = mLastError;
2931 return geom;
2932 }
2933 return QgsGeometry( std::move( cHull ) );
2934}
2935
2936QgsGeometry QgsGeometry::concaveHull( double targetPercent, bool allowHoles ) const
2937{
2938 if ( !d->geometry )
2939 {
2940 return QgsGeometry();
2941 }
2942 QgsGeos geos( d->geometry.get() );
2943 mLastError.clear();
2944 std::unique_ptr< QgsAbstractGeometry > concaveHull( geos.concaveHull( targetPercent, allowHoles, &mLastError ) );
2945 if ( !concaveHull )
2946 {
2947 QgsGeometry geom;
2948 geom.mLastError = mLastError;
2949 return geom;
2950 }
2951 return QgsGeometry( std::move( concaveHull ) );
2952}
2953
2954QgsGeometry QgsGeometry::voronoiDiagram( const QgsGeometry &extent, double tolerance, bool edgesOnly ) const
2955{
2956 if ( !d->geometry )
2957 {
2958 return QgsGeometry();
2959 }
2960
2961 QgsGeos geos( d->geometry.get() );
2962 mLastError.clear();
2963 QgsGeometry result = QgsGeometry( geos.voronoiDiagram( extent.constGet(), tolerance, edgesOnly, &mLastError ) );
2964 result.mLastError = mLastError;
2965 return result;
2966}
2967
2968QgsGeometry QgsGeometry::delaunayTriangulation( 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.delaunayTriangulation( tolerance, edgesOnly ) );
2978 result.mLastError = mLastError;
2979 return result;
2980}
2981
2983{
2984 if ( !d->geometry )
2985 {
2986 return QgsGeometry();
2987 }
2988
2989 QgsGeos geos( d->geometry.get() );
2990 mLastError.clear();
2991 QgsGeometry result( geos.constrainedDelaunayTriangulation() );
2992 result.mLastError = mLastError;
2993 return result;
2994}
2995
2997{
2998 if ( !d->geometry )
2999 {
3000 return QgsGeometry();
3001 }
3002
3003 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::GeometryCollection
3004 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::MultiPolygon
3005 && QgsWkbTypes::flatType( d->geometry->wkbType() ) != Qgis::WkbType::Polygon )
3006 return QgsGeometry();
3007
3008 QgsGeos geos( d->geometry.get() );
3009 mLastError.clear();
3010 const QgsGeometry result = QgsGeometry( geos.unionCoverage( &mLastError ) );
3011 result.mLastError = mLastError;
3012 return result;
3013}
3014
3016{
3017 if ( !d->geometry )
3018 {
3020 }
3021
3022 QgsGeos geos( d->geometry.get() );
3023 mLastError.clear();
3024 std::unique_ptr< QgsAbstractGeometry > invalidEdgesGeom;
3025
3026 const Qgis::CoverageValidityResult result = geos.validateCoverage( gapWidth, invalidEdges ? &invalidEdgesGeom : nullptr, &mLastError );
3027
3028 if ( invalidEdges && invalidEdgesGeom )
3029 *invalidEdges = QgsGeometry( std::move( invalidEdgesGeom ) );
3030
3031 return result;
3032}
3033
3034QgsGeometry QgsGeometry::simplifyCoverageVW( double tolerance, bool preserveBoundary ) const
3035{
3036 if ( !d->geometry )
3037 {
3038 return QgsGeometry();
3039 }
3040
3041 QgsGeos geos( d->geometry.get() );
3042 mLastError.clear();
3043 QgsGeometry result( geos.simplifyCoverageVW( tolerance, preserveBoundary, &mLastError ) );
3044 result.mLastError = mLastError;
3045 return result;
3046}
3047
3049{
3050 if ( !d->geometry )
3051 {
3052 return QgsGeometry();
3053 }
3054
3055 QgsGeos geos( d->geometry.get() );
3056 mLastError.clear();
3057 QgsGeometry result( geos.node( &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.sharedPaths( other.constGet(), &mLastError ) );
3072 result.mLastError = mLastError;
3073 return result;
3074}
3075
3076QgsGeometry QgsGeometry::subdivide( int maxNodes, const QgsGeometryParameters &parameters ) const
3077{
3078 if ( !d->geometry )
3079 {
3080 return QgsGeometry();
3081 }
3082
3083 const QgsAbstractGeometry *geom = d->geometry.get();
3084 std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
3085 if ( QgsWkbTypes::isCurvedType( d->geometry->wkbType() ) )
3086 {
3087 segmentizedCopy.reset( d->geometry->segmentize() );
3088 geom = segmentizedCopy.get();
3089 }
3090
3091 QgsGeos geos( geom );
3092 mLastError.clear();
3093 std::unique_ptr< QgsAbstractGeometry > result( geos.subdivide( maxNodes, &mLastError, parameters ) );
3094 if ( !result )
3095 {
3096 QgsGeometry geom;
3097 geom.mLastError = mLastError;
3098 return geom;
3099 }
3100 return QgsGeometry( std::move( result ) );
3101}
3102
3104{
3105 if ( !d->geometry )
3106 {
3107 return QgsGeometry();
3108 }
3109
3110 QgsGeometry line = *this;
3112 return QgsGeometry();
3113 else if ( type() == Qgis::GeometryType::Polygon )
3114 {
3115 line = QgsGeometry( d->geometry->boundary() );
3116 }
3117
3118 const QgsCurve *curve = nullptr;
3120 {
3121 // if multi part, iterate through parts to find target part
3122 for ( int part = 0; part < collection->numGeometries(); ++part )
3123 {
3124 const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
3125 if ( !candidate )
3126 continue;
3127 const double candidateLength = candidate->length();
3128 if ( candidateLength >= distance )
3129 {
3130 curve = candidate;
3131 break;
3132 }
3133
3134 distance -= candidateLength;
3135 }
3136 }
3137 else
3138 {
3140 }
3141 if ( !curve )
3142 return QgsGeometry();
3143
3144 std::unique_ptr< QgsPoint > result( curve->interpolatePoint( distance ) );
3145 if ( !result )
3146 {
3147 return QgsGeometry();
3148 }
3149 return QgsGeometry( std::move( result ) );
3150}
3151
3152double QgsGeometry::lineLocatePoint( const QgsGeometry &point ) const
3153{
3154 if ( type() != Qgis::GeometryType::Line )
3155 return -1;
3156
3158 return -1;
3159
3160 QgsGeometry segmentized = *this;
3162 {
3163 segmentized = QgsGeometry( static_cast< QgsCurve * >( d->geometry.get() )->segmentize() );
3164 }
3165
3166 QgsGeos geos( d->geometry.get() );
3167 mLastError.clear();
3168 return geos.lineLocatePoint( *( static_cast< QgsPoint * >( point.d->geometry.get() ) ), &mLastError );
3169}
3170
3172{
3173 if ( !d->geometry || d->geometry->isEmpty() )
3174 return 0.0;
3175
3176 const QgsAbstractGeometry *geom = d->geometry->simplifiedTypeRef();
3178 return 0.0;
3179
3180 // always operate on segmentized geometries
3181 QgsGeometry segmentized = *this;
3182 if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
3183 {
3184 segmentized = QgsGeometry( static_cast< const QgsCurve * >( geom )->segmentize() );
3185 }
3186
3187 QgsVertexId previous;
3188 QgsVertexId next;
3189 if ( !QgsGeometryUtils::verticesAtDistance( *segmentized.constGet(), distance, previous, next ) )
3190 return 0.0;
3191
3192 if ( previous == next )
3193 {
3194 // distance coincided exactly with a vertex
3195 QgsVertexId v2 = previous;
3196 QgsVertexId v1;
3197 QgsVertexId v3;
3198 segmentized.constGet()->adjacentVertices( v2, v1, v3 );
3199 if ( v1.isValid() && v3.isValid() )
3200 {
3201 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3202 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3203 QgsPoint p3 = segmentized.constGet()->vertexAt( v3 );
3204 double angle1 = QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3205 double angle2 = QgsGeometryUtilsBase::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
3206 return QgsGeometryUtilsBase::averageAngle( angle1, angle2 );
3207 }
3208 else if ( v3.isValid() )
3209 {
3210 QgsPoint p1 = segmentized.constGet()->vertexAt( v2 );
3211 QgsPoint p2 = segmentized.constGet()->vertexAt( v3 );
3212 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3213 }
3214 else
3215 {
3216 QgsPoint p1 = segmentized.constGet()->vertexAt( v1 );
3217 QgsPoint p2 = segmentized.constGet()->vertexAt( v2 );
3218 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3219 }
3220 }
3221 else
3222 {
3223 QgsPoint p1 = segmentized.constGet()->vertexAt( previous );
3224 QgsPoint p2 = segmentized.constGet()->vertexAt( next );
3225 return QgsGeometryUtilsBase::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
3226 }
3227}
3228
3230{
3231 if ( !d->geometry || geometry.isNull() )
3232 {
3233 return QgsGeometry();
3234 }
3235
3236 QgsGeos geos( d->geometry.get() );
3237
3238 mLastError.clear();
3239 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.intersection( geometry.d->geometry.get(), &mLastError, parameters ) );
3240
3241 if ( !resultGeom )
3242 {
3243 QgsGeometry geom;
3244 geom.mLastError = mLastError;
3245 return geom;
3246 }
3247
3248 return QgsGeometry( std::move( resultGeom ) );
3249}
3250
3251QgsGeometry QgsGeometry::combine( const QgsGeometry &geometry, const QgsGeometryParameters &parameters ) const
3252{
3253 if ( !d->geometry || geometry.isNull() )
3254 {
3255 return QgsGeometry();
3256 }
3257
3258 QgsGeos geos( d->geometry.get() );
3259 mLastError.clear();
3260 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.combine( geometry.d->geometry.get(), &mLastError, parameters ) );
3261 if ( !resultGeom )
3262 {
3263 QgsGeometry geom;
3264 geom.mLastError = mLastError;
3265 return geom;
3266 }
3267 return QgsGeometry( std::move( resultGeom ) );
3268}
3269
3271{
3272 if ( !d->geometry )
3273 {
3274 return QgsGeometry();
3275 }
3276
3277 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::LineString )
3278 {
3279 // special case - a single linestring was passed
3280 return QgsGeometry( *this );
3281 }
3282
3283 QgsGeos geos( d->geometry.get() );
3284 mLastError.clear();
3285 QgsGeometry result( geos.mergeLines( &mLastError, parameters ) );
3286 result.mLastError = mLastError;
3287 return result;
3288}
3289
3291{
3292 if ( !d->geometry || geometry.isNull() )
3293 {
3294 return QgsGeometry();
3295 }
3296
3297 QgsGeos geos( d->geometry.get() );
3298
3299 mLastError.clear();
3300 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.difference( geometry.d->geometry.get(), &mLastError, parameters ) );
3301 if ( !resultGeom )
3302 {
3303 QgsGeometry geom;
3304 geom.mLastError = mLastError;
3305 return geom;
3306 }
3307 return QgsGeometry( std::move( resultGeom ) );
3308}
3309
3311{
3312 if ( !d->geometry || geometry.isNull() )
3313 {
3314 return QgsGeometry();
3315 }
3316
3317 QgsGeos geos( d->geometry.get() );
3318
3319 mLastError.clear();
3320 std::unique_ptr< QgsAbstractGeometry > resultGeom( geos.symDifference( geometry.d->geometry.get(), &mLastError, parameters ) );
3321 if ( !resultGeom )
3322 {
3323 QgsGeometry geom;
3324 geom.mLastError = mLastError;
3325 return geom;
3326 }
3327 return QgsGeometry( std::move( resultGeom ) );
3328}
3329
3331{
3332 QgsInternalGeometryEngine engine( *this );
3333
3334 return engine.extrude( x, y );
3335}
3336
3338
3339QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint ) const
3340{
3342 return QVector< QgsPointXY >();
3343
3344 QgsInternalGeometryEngine engine( *this );
3345 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, acceptPoint, seed, feedback, maxTriesPerPoint );
3346 mLastError = engine.lastError();
3347 return res;
3348}
3349
3350QVector<QgsPointXY> QgsGeometry::randomPointsInPolygon( int count, unsigned long seed, QgsFeedback *feedback ) const
3351{
3353 return QVector< QgsPointXY >();
3354
3355 QgsInternalGeometryEngine engine( *this );
3356 const QVector<QgsPointXY> res = engine.randomPointsInPolygon( count, []( const QgsPointXY & ) { return true; }, seed, feedback, 0 );
3357 mLastError = engine.lastError();
3358 return res;
3359}
3361
3363{
3364 return d->geometry ? d->geometry->wkbSize( flags ) : 0;
3365}
3366
3368{
3369 return d->geometry ? d->geometry->asWkb( flags ) : QByteArray();
3370}
3371
3372QVector<QgsGeometry> QgsGeometry::asGeometryCollection() const
3373{
3374 QVector<QgsGeometry> geometryList;
3375 if ( !d->geometry )
3376 {
3377 return geometryList;
3378 }
3379
3381 if ( gc )
3382 {
3383 int numGeom = gc->numGeometries();
3384 geometryList.reserve( numGeom );
3385 for ( int i = 0; i < numGeom; ++i )
3386 {
3387 geometryList.append( QgsGeometry( gc->geometryN( i )->clone() ) );
3388 }
3389 }
3390 else //a singlepart geometry
3391 {
3392 geometryList.append( *this );
3393 }
3394
3395 return geometryList;
3396}
3397
3399{
3400 QgsPointXY point = asPoint();
3401 return point.toQPointF();
3402}
3403
3405{
3406 const QgsAbstractGeometry *part = constGet();
3407
3408 // if a geometry collection, get first part only
3410 {
3411 if ( collection->numGeometries() > 0 )
3412 part = collection->geometryN( 0 );
3413 else
3414 return QPolygonF();
3415 }
3416
3417 if ( const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( part ) )
3418 return curve->asQPolygonF();
3419 else if ( const QgsCurvePolygon *polygon = qgsgeometry_cast< const QgsCurvePolygon * >( part ) )
3420 return polygon->exteriorRing() ? polygon->exteriorRing()->asQPolygonF() : QPolygonF();
3421 return QPolygonF();
3422}
3423
3424bool QgsGeometry::deleteRing( int ringNum, int partNum )
3425{
3426 if ( !d->geometry )
3427 {
3428 return false;
3429 }
3430
3431 detach();
3432 bool ok = QgsGeometryEditUtils::deleteRing( d->geometry.get(), ringNum, partNum );
3433 return ok;
3434}
3435
3436bool QgsGeometry::deletePart( int partNum )
3437{
3438 if ( !d->geometry )
3439 {
3440 return false;
3441 }
3442
3443 if ( !isMultipart() && partNum < 1 )
3444 {
3445 set( nullptr );
3446 return true;
3447 }
3448
3449 detach();
3450 bool ok = QgsGeometryEditUtils::deletePart( d->geometry.get(), partNum );
3451 return ok;
3452}
3453
3454Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures )
3455{
3456 if ( !d->geometry )
3457 {
3459 }
3460
3461 Qgis::WkbType geomTypeBeforeModification = wkbType();
3462
3463 bool haveInvalidGeometry = false;
3464 bool geomModified = false;
3465
3466 std::unique_ptr< QgsAbstractGeometry > diffGeom = QgsGeometryEditUtils::avoidIntersections( *( d->geometry ), avoidIntersectionsLayers, haveInvalidGeometry, ignoreFeatures );
3467 if ( diffGeom )
3468 {
3469 reset( std::move( diffGeom ) );
3470 geomModified = true;
3471 }
3472
3473 if ( geomTypeBeforeModification != wkbType() )
3475 if ( haveInvalidGeometry )
3477 if ( !geomModified )
3479
3481}
3482
3514
3516{
3517 if ( !d->geometry )
3518 return QgsGeometry();
3519
3520 mLastError.clear();
3521 QgsGeos geos( d->geometry.get() );
3522 std::unique_ptr< QgsAbstractGeometry > g( geos.makeValid( method, keepCollapsed, &mLastError ) );
3523
3524 QgsGeometry result = QgsGeometry( std::move( g ) );
3525 result.mLastError = mLastError;
3526 return result;
3527}
3528
3533
3535{
3536 if ( !d->geometry )
3537 {
3539 }
3540
3541 if ( isMultipart() )
3542 {
3543 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3544 const QgsAbstractGeometry *g = collection->geometryN( 0 );
3546 {
3547 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3548 }
3549 }
3550 else
3551 {
3552 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3553 {
3554 return cp->exteriorRing() ? cp->exteriorRing()->orientation() : Qgis::AngularDirection::NoOrientation;
3555 }
3556 }
3557
3559}
3560
3562{
3563 if ( !d->geometry )
3564 return QgsGeometry();
3565
3566 if ( isMultipart() )
3567 {
3568 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3569 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3570 newCollection->reserve( collection->numGeometries() );
3571 for ( int i = 0; i < collection->numGeometries(); ++i )
3572 {
3573 const QgsAbstractGeometry *g = collection->geometryN( i );
3575 {
3576 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3577 corrected->forceClockwise();
3578 newCollection->addGeometry( corrected.release() );
3579 }
3580 else
3581 {
3582 newCollection->addGeometry( g->clone() );
3583 }
3584 }
3585 return QgsGeometry( std::move( newCollection ) );
3586 }
3587 else
3588 {
3589 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3590 {
3591 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3592 corrected->forceClockwise();
3593 return QgsGeometry( std::move( corrected ) );
3594 }
3595 else
3596 {
3597 // not a curve polygon, so return unchanged
3598 return *this;
3599 }
3600 }
3601}
3602
3604{
3605 if ( !d->geometry )
3606 return QgsGeometry();
3607
3608 if ( isMultipart() )
3609 {
3610 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
3611 std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
3612 newCollection->reserve( collection->numGeometries() );
3613 for ( int i = 0; i < collection->numGeometries(); ++i )
3614 {
3615 const QgsAbstractGeometry *g = collection->geometryN( i );
3617 {
3618 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3619 corrected->forceCounterClockwise();
3620 newCollection->addGeometry( corrected.release() );
3621 }
3622 else
3623 {
3624 newCollection->addGeometry( g->clone() );
3625 }
3626 }
3627 return QgsGeometry( std::move( newCollection ) );
3628 }
3629 else
3630 {
3631 if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
3632 {
3633 std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
3634 corrected->forceCounterClockwise();
3635 return QgsGeometry( std::move( corrected ) );
3636 }
3637 else
3638 {
3639 // not a curve polygon, so return unchanged
3640 return *this;
3641 }
3642 }
3643}
3644
3645
3646void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, const Qgis::GeometryValidationEngine method, const Qgis::GeometryValidityFlags flags ) const
3647{
3648 errors.clear();
3649 if ( !d->geometry )
3650 return;
3651
3652 // avoid expensive calcs for trivial point geometries
3653 if ( QgsWkbTypes::geometryType( d->geometry->wkbType() ) == Qgis::GeometryType::Point )
3654 {
3655 return;
3656 }
3657
3658 switch ( method )
3659 {
3661 QgsGeometryValidator::validateGeometry( *this, errors, method );
3662 return;
3663
3665 {
3666 QgsGeos geos( d->geometry.get(), 0, Qgis::GeosCreationFlags() );
3667 QString error;
3668 QgsGeometry errorLoc;
3669 if ( !geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, &errorLoc ) )
3670 {
3671 if ( errorLoc.isNull() )
3672 {
3673 errors.append( QgsGeometry::Error( error ) );
3674 }
3675 else
3676 {
3677 const QgsPointXY point = errorLoc.asPoint();
3678 errors.append( QgsGeometry::Error( error, point ) );
3679 }
3680 return;
3681 }
3682 }
3683 }
3684}
3685
3687{
3688 if ( !d->geometry )
3689 {
3690 return;
3691 }
3692
3693 detach();
3694 d->geometry->normalize();
3695}
3696
3698{
3699 if ( !d->geometry )
3700 {
3701 return false;
3702 }
3703
3704 return d->geometry->isValid( mLastError, flags );
3705}
3706
3708{
3709 if ( !d->geometry )
3710 return false;
3711
3712 QgsGeos geos( d->geometry.get() );
3713 mLastError.clear();
3714 return geos.isSimple( &mLastError );
3715}
3716
3717bool QgsGeometry::isAxisParallelRectangle( double maximumDeviation, bool simpleRectanglesOnly ) const
3718{
3719 if ( !d->geometry )
3720 return false;
3721
3722 QgsInternalGeometryEngine engine( *this );
3723 return engine.isAxisParallelRectangle( maximumDeviation, simpleRectanglesOnly );
3724}
3725
3727{
3728 if ( !d->geometry || !g.d->geometry )
3729 {
3730 return false;
3731 }
3732
3733 // fast check - are they shared copies of the same underlying geometry?
3734 if ( d == g.d )
3735 return true;
3736
3737 // fast check - distinct geometry types?
3738 if ( type() != g.type() )
3739 return false;
3740
3741 // avoid calling geos for trivial point case
3742 if ( QgsWkbTypes::flatType( d->geometry->wkbType() ) == Qgis::WkbType::Point && QgsWkbTypes::flatType( g.d->geometry->wkbType() ) == Qgis::WkbType::Point )
3743 {
3744 return equals( g );
3745 }
3746
3747 // another nice fast check upfront -- if the bounding boxes aren't equal, the geometries themselves can't be equal!
3748 if ( d->geometry->boundingBox() != g.d->geometry->boundingBox() )
3749 return false;
3750
3751 QgsGeos geos( d->geometry.get() );
3752 mLastError.clear();
3753 return geos.isEqual( g.d->geometry.get(), &mLastError );
3754}
3755
3756QgsGeometry QgsGeometry::unaryUnion( const QVector<QgsGeometry> &geometries, const QgsGeometryParameters &parameters )
3757{
3758 QgsGeos geos( nullptr );
3759
3760 QString error;
3761 std::unique_ptr< QgsAbstractGeometry > geom( geos.combine( geometries, &error, parameters ) );
3762 QgsGeometry result( std::move( geom ) );
3763 result.mLastError = error;
3764 return result;
3765}
3766
3767QgsGeometry QgsGeometry::polygonize( const QVector<QgsGeometry> &geometryList )
3768{
3769 QVector<const QgsAbstractGeometry *> geomV2List;
3770 for ( const QgsGeometry &g : geometryList )
3771 {
3772 if ( !( g.isNull() ) )
3773 {
3774 geomV2List.append( g.constGet() );
3775 }
3776 }
3777
3778 QString error;
3779 QgsGeometry result = QgsGeos::polygonize( geomV2List, &error );
3780 result.mLastError = error;
3781 return result;
3782}
3783
3785{
3786 if ( !d->geometry || !requiresConversionToStraightSegments() )
3787 {
3788 return;
3789 }
3790
3791 std::unique_ptr< QgsAbstractGeometry > straightGeom( d->geometry->segmentize( tolerance, toleranceType ) );
3792 reset( std::move( straightGeom ) );
3793}
3794
3796{
3797 if ( !d->geometry )
3798 {
3799 return false;
3800 }
3801
3802 return d->geometry->hasCurvedSegments();
3803}
3804
3806{
3807 if ( !d->geometry )
3808 {
3810 }
3811
3812 detach();
3813 d->geometry->transform( ct, direction, transformZ );
3815}
3816
3817Qgis::GeometryOperationResult QgsGeometry::transform( const QTransform &ct, double zTranslate, double zScale, double mTranslate, double mScale )
3818{
3819 if ( !d->geometry )
3820 {
3822 }
3823
3824 detach();
3825 d->geometry->transform( ct, zTranslate, zScale, mTranslate, mScale );
3827}
3828
3830{
3831 if ( d->geometry )
3832 {
3833 detach();
3834 d->geometry->transform( mtp.transform() );
3835 }
3836}
3837
3839{
3840 if ( !d->geometry || rectangle.isNull() || rectangle.isEmpty() )
3841 {
3842 return QgsGeometry();
3843 }
3844
3845 QgsGeos geos( d->geometry.get() );
3846 mLastError.clear();
3847 std::unique_ptr< QgsAbstractGeometry > resultGeom = geos.clip( rectangle, &mLastError );
3848 if ( !resultGeom )
3849 {
3850 QgsGeometry result;
3851 result.mLastError = mLastError;
3852 return result;
3853 }
3854 return QgsGeometry( std::move( resultGeom ) );
3855}
3856
3857void QgsGeometry::draw( QPainter &p ) const
3858{
3859 if ( d->geometry )
3860 {
3861 d->geometry->draw( p );
3862 }
3863}
3864
3865static bool vertexIndexInfo( const QgsAbstractGeometry *g, int vertexIndex, int &partIndex, int &ringIndex, int &vertex )
3866{
3867 if ( vertexIndex < 0 )
3868 return false; // clearly something wrong
3869
3871 {
3872 partIndex = 0;
3873 for ( int i = 0; i < geomCollection->numGeometries(); ++i )
3874 {
3875 const QgsAbstractGeometry *part = geomCollection->geometryN( i );
3876
3877 // count total number of vertices in the part
3878 int numPoints = 0;
3879 for ( int k = 0; k < part->ringCount(); ++k )
3880 numPoints += part->vertexCount( 0, k );
3881
3882 if ( vertexIndex < numPoints )
3883 {
3884 int nothing;
3885 return vertexIndexInfo( part, vertexIndex, nothing, ringIndex, vertex ); // set ring_index + index
3886 }
3887 vertexIndex -= numPoints;
3888 partIndex++;
3889 }
3890 }
3891 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
3892 {
3893 // PolyhedralSurface: patches are the parts
3894 partIndex = 0;
3895 for ( int i = 0; i < polySurface->numPatches(); ++i )
3896 {
3897 const QgsPolygon *patch = polySurface->patchN( i );
3898 // count total number of vertices in the patch
3899 int numPoints = 0;
3900 for ( int k = 0; k < patch->ringCount(); ++k )
3901 numPoints += patch->vertexCount( 0, k );
3902
3903 if ( vertexIndex < numPoints )
3904 {
3905 int nothing;
3906 return vertexIndexInfo( patch, vertexIndex, nothing, ringIndex, vertex );
3907 }
3908 vertexIndex -= numPoints;
3909 partIndex++;
3910 }
3911 }
3912 else if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3913 {
3914 const QgsCurve *ring = curvePolygon->exteriorRing();
3915 if ( vertexIndex < ring->numPoints() )
3916 {
3917 partIndex = 0;
3918 ringIndex = 0;
3919 vertex = vertexIndex;
3920 return true;
3921 }
3922 vertexIndex -= ring->numPoints();
3923 ringIndex = 1;
3924 for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
3925 {
3926 const QgsCurve *ring = curvePolygon->interiorRing( i );
3927 if ( vertexIndex < ring->numPoints() )
3928 {
3929 partIndex = 0;
3930 vertex = vertexIndex;
3931 return true;
3932 }
3933 vertexIndex -= ring->numPoints();
3934 ringIndex += 1;
3935 }
3936 }
3937 else if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3938 {
3939 if ( vertexIndex < curve->numPoints() )
3940 {
3941 partIndex = 0;
3942 ringIndex = 0;
3943 vertex = vertexIndex;
3944 return true;
3945 }
3946 }
3947 else if ( qgsgeometry_cast<const QgsPoint *>( g ) )
3948 {
3949 if ( vertexIndex == 0 )
3950 {
3951 partIndex = 0;
3952 ringIndex = 0;
3953 vertex = 0;
3954 return true;
3955 }
3956 }
3957
3958 return false;
3959}
3960
3962{
3963 if ( !d->geometry )
3964 {
3965 return false;
3966 }
3967
3968 id.type = Qgis::VertexType::Segment;
3969
3970 bool res = vertexIndexInfo( d->geometry.get(), nr, id.part, id.ring, id.vertex );
3971 if ( !res )
3972 return false;
3973
3974 // now let's find out if it is a straight or circular segment
3975 const QgsAbstractGeometry *g = d->geometry.get();
3977 {
3978 g = geomCollection->geometryN( id.part );
3979 }
3980 else if ( const QgsPolyhedralSurface *polySurface = qgsgeometry_cast<const QgsPolyhedralSurface *>( g ) )
3981 {
3982 g = polySurface->patchN( id.part );
3983 }
3984
3985 if ( const QgsCurvePolygon *curvePolygon = qgsgeometry_cast<const QgsCurvePolygon *>( g ) )
3986 {
3987 g = id.ring == 0 ? curvePolygon->exteriorRing() : curvePolygon->interiorRing( id.ring - 1 );
3988 }
3989
3990 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( g ) )
3991 {
3992 QgsPoint p;
3993 res = curve->pointAt( id.vertex, p, id.type );
3994 if ( !res )
3995 return false;
3996 }
3997
3998 return true;
3999}
4000
4002{
4003 if ( !d->geometry )
4004 {
4005 return -1;
4006 }
4007 return d->geometry->vertexNumberFromVertexId( id );
4008}
4009
4011{
4012 return mLastError;
4013}
4014
4015void QgsGeometry::filterVertices( const std::function<bool( const QgsPoint & )> &filter )
4016{
4017 if ( !d->geometry )
4018 return;
4019
4020 detach();
4021
4022 d->geometry->filterVertices( filter );
4023}
4024
4025void QgsGeometry::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
4026{
4027 if ( !d->geometry )
4028 return;
4029
4030 detach();
4031
4032 d->geometry->transformVertices( transform );
4033}
4034
4035void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
4036{
4037 output.clear();
4038 for ( const QgsPointXY &p : input )
4039 {
4040 output.append( QgsPoint( p ) );
4041 }
4042}
4043
4044void QgsGeometry::convertPointList( const QgsPointSequence &input, QVector<QgsPointXY> &output )
4045{
4046 output.clear();
4047 for ( const QgsPoint &p : input )
4048 {
4049 output.append( QgsPointXY( p.x(), p.y() ) );
4050 }
4051}
4052
4053void QgsGeometry::convertPolygon( const QgsPolygon &input, QgsPolygonXY &output )
4054{
4055 output.clear();
4056
4057 auto convertRing = []( const QgsCurve *ring ) -> QgsPolylineXY {
4058 QgsPolylineXY res;
4060 std::unique_ptr< QgsLineString > segmentizedLine;
4061 const QgsLineString *line = nullptr;
4062 if ( doSegmentation )
4063 {
4064 segmentizedLine.reset( ring->curveToLine() );
4065 line = segmentizedLine.get();
4066 }
4067 else
4068 {
4070 if ( !line )
4071 {
4072 return res;
4073 }
4074 }
4075
4076 int nVertices = line->numPoints();
4077 res.resize( nVertices );
4078 QgsPointXY *data = res.data();
4079 const double *xData = line->xData();
4080 const double *yData = line->yData();
4081 for ( int i = 0; i < nVertices; ++i )
4082 {
4083 data->setX( *xData++ );
4084 data->setY( *yData++ );
4085 data++;
4086 }
4087 return res;
4088 };
4089
4090 if ( const QgsCurve *exterior = input.exteriorRing() )
4091 {
4092 output.push_back( convertRing( exterior ) );
4093 }
4094
4095 const int interiorRingCount = input.numInteriorRings();
4096 output.reserve( output.size() + interiorRingCount );
4097 for ( int n = 0; n < interiorRingCount; ++n )
4098 {
4099 output.push_back( convertRing( input.interiorRing( n ) ) );
4100 }
4101}
4102
4104{
4105 return QgsGeometry( std::make_unique< QgsPoint >( point.x(), point.y() ) );
4106}
4107
4108QgsGeometry QgsGeometry::fromQPolygonF( const QPolygonF &polygon )
4109{
4110 std::unique_ptr< QgsLineString > ring( QgsLineString::fromQPolygonF( polygon ) );
4111
4112 if ( polygon.isClosed() )
4113 {
4114 auto poly = std::make_unique< QgsPolygon >();
4115 poly->setExteriorRing( ring.release() );
4116 return QgsGeometry( std::move( poly ) );
4117 }
4118 else
4119 {
4120 return QgsGeometry( std::move( ring ) );
4121 }
4122}
4123
4125{
4127 QgsPolygonXY result;
4128 result << createPolylineFromQPolygonF( polygon );
4129 return result;
4131}
4132
4134{
4135 QgsPolylineXY result;
4136 result.reserve( polygon.count() );
4137 for ( const QPointF &p : polygon )
4138 {
4139 result.append( QgsPointXY( p ) );
4140 }
4141 return result;
4142}
4143
4144bool QgsGeometry::compare( const QgsPolylineXY &p1, const QgsPolylineXY &p2, double epsilon )
4145{
4146 if ( p1.count() != p2.count() )
4147 return false;
4148
4149 for ( int i = 0; i < p1.count(); ++i )
4150 {
4151 if ( !p1.at( i ).compare( p2.at( i ), epsilon ) )
4152 return false;
4153 }
4154 return true;
4155}
4156
4157bool QgsGeometry::compare( const QgsPolygonXY &p1, const QgsPolygonXY &p2, double epsilon )
4158{
4159 if ( p1.count() != p2.count() )
4160 return false;
4161
4162 for ( int i = 0; i < p1.count(); ++i )
4163 {
4164 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4165 return false;
4166 }
4167 return true;
4168}
4169
4170
4171bool QgsGeometry::compare( const QgsMultiPolygonXY &p1, const QgsMultiPolygonXY &p2, double epsilon )
4172{
4173 if ( p1.count() != p2.count() )
4174 return false;
4175
4176 for ( int i = 0; i < p1.count(); ++i )
4177 {
4178 if ( !QgsGeometry::compare( p1.at( i ), p2.at( i ), epsilon ) )
4179 return false;
4180 }
4181 return true;
4182}
4183
4184QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4185{
4186 if ( !d->geometry || d->geometry->isEmpty() )
4187 return QgsGeometry();
4188
4189 QgsGeometry geom = *this;
4191 geom = QgsGeometry( d->geometry->segmentize() );
4192
4193 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
4194 {
4197 //can't smooth a point based geometry
4198 return geom;
4199
4201 {
4203 return QgsGeometry( smoothLine( *lineString, iterations, offset, minimumDistance, maxAngle ) );
4204 }
4205
4207 {
4209
4210 auto resultMultiline = std::make_unique< QgsMultiLineString>();
4211 resultMultiline->reserve( inputMultiLine->numGeometries() );
4212 for ( int i = 0; i < inputMultiLine->numGeometries(); ++i )
4213 {
4214 resultMultiline->addGeometry( smoothLine( *( inputMultiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4215 }
4216 return QgsGeometry( std::move( resultMultiline ) );
4217 }
4218
4220 {
4222 return QgsGeometry( smoothPolygon( *poly, iterations, offset, minimumDistance, maxAngle ) );
4223 }
4224
4226 {
4228
4229 auto resultMultiPoly = std::make_unique< QgsMultiPolygon >();
4230 resultMultiPoly->reserve( inputMultiPoly->numGeometries() );
4231 for ( int i = 0; i < inputMultiPoly->numGeometries(); ++i )
4232 {
4233 resultMultiPoly->addGeometry( smoothPolygon( *( inputMultiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() );
4234 }
4235 return QgsGeometry( std::move( resultMultiPoly ) );
4236 }
4237
4239 default:
4240 return QgsGeometry( *this );
4241 }
4242}
4243
4244std::unique_ptr< QgsLineString > smoothCurve( const QgsLineString &line, const unsigned int iterations, const double offset, double squareDistThreshold, double maxAngleRads, bool isRing )
4245{
4246 auto result = std::make_unique< QgsLineString >( line );
4247 QgsPointSequence outputLine;
4248 for ( unsigned int iteration = 0; iteration < iterations; ++iteration )
4249 {
4250 outputLine.resize( 0 );
4251 outputLine.reserve( 2 * ( result->numPoints() - 1 ) );
4252 bool skipFirst = false;
4253 bool skipLast = false;
4254 if ( isRing )
4255 {
4256 QgsPoint p1 = result->pointN( result->numPoints() - 2 );
4257 QgsPoint p2 = result->pointN( 0 );
4258 QgsPoint p3 = result->pointN( 1 );
4259 double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4260 angle = std::fabs( M_PI - angle );
4261 skipFirst = angle > maxAngleRads;
4262 }
4263 for ( int i = 0; i < result->numPoints() - 1; i++ )
4264 {
4265 QgsPoint p1 = result->pointN( i );
4266 QgsPoint p2 = result->pointN( i + 1 );
4267
4268 double angle = M_PI;
4269 if ( i == 0 && isRing )
4270 {
4271 QgsPoint p3 = result->pointN( result->numPoints() - 2 );
4272 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4273 }
4274 else if ( i < result->numPoints() - 2 )
4275 {
4276 QgsPoint p3 = result->pointN( i + 2 );
4277 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4278 }
4279 else if ( i == result->numPoints() - 2 && isRing )
4280 {
4281 QgsPoint p3 = result->pointN( 1 );
4282 angle = QgsGeometryUtilsBase::angleBetweenThreePoints( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() );
4283 }
4284
4285 skipLast = angle < M_PI - maxAngleRads || angle > M_PI + maxAngleRads;
4286
4287 // don't apply distance threshold to first or last segment
4288 if ( i == 0 || i >= result->numPoints() - 2 || QgsGeometryUtils::sqrDistance2D( p1, p2 ) > squareDistThreshold )
4289 {
4290 if ( !isRing )
4291 {
4292 if ( !skipFirst )
4293 outputLine << ( i == 0 ? result->pointN( i ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset ) );
4294 if ( !skipLast )
4295 outputLine << ( i == result->numPoints() - 2 ? result->pointN( i + 1 ) : QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset ) );
4296 else
4297 outputLine << p2;
4298 }
4299 else
4300 {
4301 // ring
4302 if ( !skipFirst )
4303 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, offset );
4304 else if ( i == 0 )
4305 outputLine << p1;
4306 if ( !skipLast )
4307 outputLine << QgsGeometryUtils::interpolatePointOnLine( p1, p2, 1.0 - offset );
4308 else
4309 outputLine << p2;
4310 }
4311 }
4312 skipFirst = skipLast;
4313 }
4314
4315 if ( isRing && outputLine.at( 0 ) != outputLine.at( outputLine.count() - 1 ) )
4316 outputLine << outputLine.at( 0 );
4317
4318 result->setPoints( outputLine );
4319 }
4320 return result;
4321}
4322
4323std::unique_ptr<QgsLineString> QgsGeometry::smoothLine( const QgsLineString &line, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4324{
4325 double maxAngleRads = maxAngle * M_PI / 180.0;
4326 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4327 return smoothCurve( line, iterations, offset, squareDistThreshold, maxAngleRads, false );
4328}
4329
4330std::unique_ptr<QgsPolygon> QgsGeometry::smoothPolygon( const QgsPolygon &polygon, const unsigned int iterations, const double offset, double minimumDistance, double maxAngle ) const
4331{
4332 double maxAngleRads = maxAngle * M_PI / 180.0;
4333 double squareDistThreshold = minimumDistance > 0 ? minimumDistance * minimumDistance : -1;
4334 auto resultPoly = std::make_unique< QgsPolygon >();
4335
4336 resultPoly->setExteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.exteriorRing() ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4337
4338 for ( int i = 0; i < polygon.numInteriorRings(); ++i )
4339 {
4340 resultPoly->addInteriorRing( smoothCurve( *( static_cast< const QgsLineString *>( polygon.interiorRing( i ) ) ), iterations, offset, squareDistThreshold, maxAngleRads, true ).release() );
4341 }
4342 return resultPoly;
4343}
4344
4345QgsGeometry QgsGeometry::convertToPoint( bool destMultipart ) const
4346{
4347 switch ( type() )
4348 {
4350 {
4351 bool srcIsMultipart = isMultipart();
4352
4353 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4354 {
4355 // return a copy of the same geom
4356 return QgsGeometry( *this );
4357 }
4358 if ( destMultipart )
4359 {
4360 // layer is multipart => make a multipoint with a single point
4361 return fromMultiPointXY( QgsMultiPointXY() << asPoint() );
4362 }
4363 else
4364 {
4365 // destination is singlepart => make a single part if possible
4366 QgsMultiPointXY multiPoint = asMultiPoint();
4367 if ( multiPoint.count() == 1 )
4368 {
4369 return fromPointXY( multiPoint[0] );
4370 }
4371 }
4372 return QgsGeometry();
4373 }
4374
4376 {
4377 // only possible if destination is multipart
4378 if ( !destMultipart )
4379 return QgsGeometry();
4380
4381 // input geometry is multipart
4382 if ( isMultipart() )
4383 {
4384 const QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4385 QgsMultiPointXY multiPoint;
4386 for ( const QgsPolylineXY &l : inputMultiLine )
4387 for ( const QgsPointXY &p : l )
4388 multiPoint << p;
4389 return fromMultiPointXY( multiPoint );
4390 }
4391 // input geometry is not multipart: copy directly the line into a multipoint
4392 else
4393 {
4394 QgsPolylineXY line = asPolyline();
4395 if ( !line.isEmpty() )
4396 return fromMultiPointXY( line );
4397 }
4398 return QgsGeometry();
4399 }
4400
4402 {
4403 // can only transform if destination is multipoint
4404 if ( !destMultipart )
4405 return QgsGeometry();
4406
4407 // input geometry is multipart: make a multipoint from multipolygon
4408 if ( isMultipart() )
4409 {
4410 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4411 QgsMultiPointXY multiPoint;
4412 for ( const QgsPolygonXY &poly : multiPolygon )
4413 for ( const QgsPolylineXY &line : poly )
4414 for ( const QgsPointXY &pt : line )
4415 multiPoint << pt;
4416 return fromMultiPointXY( multiPoint );
4417 }
4418 // input geometry is not multipart: make a multipoint from polygon
4419 else
4420 {
4421 const QgsPolygonXY polygon = asPolygon();
4422 QgsMultiPointXY multiPoint;
4423 for ( const QgsPolylineXY &line : polygon )
4424 for ( const QgsPointXY &pt : line )
4425 multiPoint << pt;
4426 return fromMultiPointXY( multiPoint );
4427 }
4428 }
4429
4430 default:
4431 return QgsGeometry();
4432 }
4433}
4434
4435QgsGeometry QgsGeometry::convertToLine( bool destMultipart ) const
4436{
4437 switch ( type() )
4438 {
4440 {
4441 if ( !isMultipart() )
4442 return QgsGeometry();
4443
4444 QgsMultiPointXY multiPoint = asMultiPoint();
4445 if ( multiPoint.count() < 2 )
4446 return QgsGeometry();
4447
4448 if ( destMultipart )
4449 return fromMultiPolylineXY( QgsMultiPolylineXY() << multiPoint );
4450 else
4451 return fromPolylineXY( multiPoint );
4452 }
4453
4455 {
4456 bool srcIsMultipart = isMultipart();
4457
4458 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4459 {
4460 // return a copy of the same geom
4461 return QgsGeometry( *this );
4462 }
4463 if ( destMultipart )
4464 {
4465 // destination is multipart => makes a multipoint with a single line
4466 QgsPolylineXY line = asPolyline();
4467 if ( !line.isEmpty() )
4468 return fromMultiPolylineXY( QgsMultiPolylineXY() << line );
4469 }
4470 else
4471 {
4472 // destination is singlepart => make a single part if possible
4473 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4474 if ( inputMultiLine.count() == 1 )
4475 return fromPolylineXY( inputMultiLine[0] );
4476 }
4477 return QgsGeometry();
4478 }
4479
4481 {
4482 // input geometry is multipolygon
4483 if ( isMultipart() )
4484 {
4485 const QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4486 QgsMultiPolylineXY inputMultiLine;
4487 for ( const QgsPolygonXY &poly : multiPolygon )
4488 for ( const QgsPolylineXY &line : poly )
4489 inputMultiLine << line;
4490
4491 if ( destMultipart )
4492 {
4493 // destination is multipart
4494 return fromMultiPolylineXY( inputMultiLine );
4495 }
4496 else if ( inputMultiLine.count() == 1 )
4497 {
4498 // destination is singlepart => make a single part if possible
4499 return fromPolylineXY( inputMultiLine[0] );
4500 }
4501 }
4502 // input geometry is single polygon
4503 else
4504 {
4505 QgsPolygonXY polygon = asPolygon();
4506 // if polygon has rings
4507 if ( polygon.count() > 1 )
4508 {
4509 // cannot fit a polygon with rings in a single line layer
4510 // TODO: would it be better to remove rings?
4511 if ( destMultipart )
4512 {
4513 const QgsPolygonXY polygon = asPolygon();
4514 QgsMultiPolylineXY inputMultiLine;
4515 inputMultiLine.reserve( polygon.count() );
4516 for ( const QgsPolylineXY &line : polygon )
4517 inputMultiLine << line;
4518 return fromMultiPolylineXY( inputMultiLine );
4519 }
4520 }
4521 // no rings
4522 else if ( polygon.count() == 1 )
4523 {
4524 if ( destMultipart )
4525 {
4526 return fromMultiPolylineXY( polygon );
4527 }
4528 else
4529 {
4530 return fromPolylineXY( polygon[0] );
4531 }
4532 }
4533 }
4534 return QgsGeometry();
4535 }
4536
4537 default:
4538 return QgsGeometry();
4539 }
4540}
4541
4542QgsGeometry QgsGeometry::convertToPolygon( bool destMultipart ) const
4543{
4544 switch ( type() )
4545 {
4547 {
4548 if ( !isMultipart() )
4549 return QgsGeometry();
4550
4551 QgsMultiPointXY multiPoint = asMultiPoint();
4552 if ( multiPoint.count() < 3 )
4553 return QgsGeometry();
4554
4555 if ( multiPoint.last() != multiPoint.first() )
4556 multiPoint << multiPoint.first();
4557
4558 QgsPolygonXY polygon = QgsPolygonXY() << multiPoint;
4559 if ( destMultipart )
4560 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4561 else
4562 return fromPolygonXY( polygon );
4563 }
4564
4566 {
4567 // input geometry is multiline
4568 if ( isMultipart() )
4569 {
4570 QgsMultiPolylineXY inputMultiLine = asMultiPolyline();
4571 QgsMultiPolygonXY multiPolygon;
4572 for ( QgsMultiPolylineXY::iterator multiLineIt = inputMultiLine.begin(); multiLineIt != inputMultiLine.end(); ++multiLineIt )
4573 {
4574 // do not create polygon for a 1 segment line
4575 if ( ( *multiLineIt ).count() < 3 )
4576 return QgsGeometry();
4577 if ( ( *multiLineIt ).count() == 3 && ( *multiLineIt ).first() == ( *multiLineIt ).last() )
4578 return QgsGeometry();
4579
4580 // add closing node
4581 if ( ( *multiLineIt ).first() != ( *multiLineIt ).last() )
4582 *multiLineIt << ( *multiLineIt ).first();
4583 multiPolygon << ( QgsPolygonXY() << *multiLineIt );
4584 }
4585 // check that polygons were inserted
4586 if ( !multiPolygon.isEmpty() )
4587 {
4588 if ( destMultipart )
4589 {
4590 return fromMultiPolygonXY( multiPolygon );
4591 }
4592 else if ( multiPolygon.count() == 1 )
4593 {
4594 // destination is singlepart => make a single part if possible
4595 return fromPolygonXY( multiPolygon[0] );
4596 }
4597 }
4598 }
4599 // input geometry is single line
4600 else
4601 {
4602 QgsPolylineXY line = asPolyline();
4603
4604 // do not create polygon for a 1 segment line
4605 if ( line.count() < 3 )
4606 return QgsGeometry();
4607 if ( line.count() == 3 && line.first() == line.last() )
4608 return QgsGeometry();
4609
4610 // add closing node
4611 if ( line.first() != line.last() )
4612 line << line.first();
4613
4614 // destination is multipart
4615 if ( destMultipart )
4616 {
4617 return fromMultiPolygonXY( QgsMultiPolygonXY() << ( QgsPolygonXY() << line ) );
4618 }
4619 else
4620 {
4621 return fromPolygonXY( QgsPolygonXY() << line );
4622 }
4623 }
4624 return QgsGeometry();
4625 }
4626
4628 {
4629 bool srcIsMultipart = isMultipart();
4630
4631 if ( ( destMultipart && srcIsMultipart ) || ( !destMultipart && !srcIsMultipart ) )
4632 {
4633 // return a copy of the same geom
4634 return QgsGeometry( *this );
4635 }
4636 if ( destMultipart )
4637 {
4638 // destination is multipart => makes a multipoint with a single polygon
4639 QgsPolygonXY polygon = asPolygon();
4640 if ( !polygon.isEmpty() )
4641 return fromMultiPolygonXY( QgsMultiPolygonXY() << polygon );
4642 }
4643 else
4644 {
4645 QgsMultiPolygonXY multiPolygon = asMultiPolygon();
4646 if ( multiPolygon.count() == 1 )
4647 {
4648 // destination is singlepart => make a single part if possible
4649 return fromPolygonXY( multiPolygon[0] );
4650 }
4651 }
4652 return QgsGeometry();
4653 }
4654
4655 default:
4656 return QgsGeometry();
4657 }
4658}
4659
4661{
4662 return new QgsGeos( geometry, precision, flags );
4663}
4664
4665QDataStream &operator<<( QDataStream &out, const QgsGeometry &geometry )
4666{
4667 out << geometry.asWkb();
4668 return out;
4669}
4670
4671QDataStream &operator>>( QDataStream &in, QgsGeometry &geometry )
4672{
4673 QByteArray byteArray;
4674 in >> byteArray;
4675 if ( byteArray.isEmpty() )
4676 {
4677 geometry.set( nullptr );
4678 return in;
4679 }
4680
4681 geometry.fromWkb( byteArray );
4682 return in;
4683}
4684
4685
4687{
4688 return mMessage;
4689}
4690
4692{
4693 return mLocation;
4694}
4695
4697{
4698 return mHasLocation;
4699}
4700
4701QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int vertexIndex, double distance1, double distance2, int segments ) const
4702{
4703 QgsDebugMsgLevel( u"%1 starts: %2"_s.arg( qgsEnumValueToKey( op ) ).arg( asWkt( 2 ) ), 3 );
4704 if ( isNull() )
4705 {
4706 mLastError = u"Operation '%1' needs non-null geometry."_s.arg( qgsEnumValueToKey( op ) );
4707 return QgsGeometry();
4708 }
4709
4710 QgsCurve *curve = nullptr;
4711
4712 int modifiedPart = -1;
4713 int modifiedRing = -1;
4714 QgsVertexId vertexId;
4715 if ( !vertexIdFromVertexNr( vertexIndex, vertexId ) )
4716 {
4717 mLastError = u"Invalid vertex index"_s;
4718 return QgsGeometry();
4719 }
4720 int resolvedVertexIndex = vertexId.vertex;
4721 QgsMultiLineString *inputMultiLine = nullptr;
4722 QgsMultiPolygon *inputMultiPoly = nullptr;
4724
4725 if ( geomType == Qgis::GeometryType::Line )
4726 {
4727 if ( isMultipart() )
4728 {
4729 modifiedPart = vertexId.part;
4730
4731 inputMultiLine = qgsgeometry_cast<QgsMultiLineString *>( d->geometry.get() );
4732 curve = dynamic_cast<QgsCurve *>( inputMultiLine->lineStringN( modifiedPart ) );
4733 }
4734 else
4735 {
4736 curve = dynamic_cast<QgsCurve *>( d->geometry.get() );
4737 }
4738 }
4739 else if ( geomType == Qgis::GeometryType::Polygon )
4740 {
4741 QgsPolygon *poly = nullptr;
4742 if ( isMultipart() )
4743 {
4744 modifiedPart = vertexId.part;
4745 // get part, get ring
4746 inputMultiPoly = qgsgeometry_cast<QgsMultiPolygon *>( d->geometry.get() );
4747 poly = inputMultiPoly->polygonN( modifiedPart );
4748 }
4749 else
4750 {
4751 poly = qgsgeometry_cast<QgsPolygon *>( d->geometry.get() );
4752 }
4753 if ( !poly )
4754 {
4755 mLastError = u"Could not get polygon geometry."_s;
4756 return QgsGeometry();
4757 }
4758
4759 // if has rings
4760 modifiedRing = vertexId.ring;
4761 if ( modifiedRing == 0 )
4762 curve = qgsgeometry_cast<QgsCurve *>( poly->exteriorRing() );
4763 else
4764 curve = qgsgeometry_cast<QgsCurve *>( poly->interiorRing( modifiedRing - 1 ) );
4765 }
4766 else
4767 curve = nullptr;
4768
4769 if ( !curve )
4770 {
4771 mLastError = u"Operation '%1' needs curve geometry."_s.arg( qgsEnumValueToKey( op ) );
4772 return QgsGeometry();
4773 }
4774
4775 std::unique_ptr<QgsAbstractGeometry> result;
4776 try
4777 {
4779 result = QgsGeometryUtils::chamferVertex( curve, resolvedVertexIndex, distance1, distance2 );
4780 else
4781 result = QgsGeometryUtils::filletVertex( curve, resolvedVertexIndex, distance1, segments );
4782 }
4783 catch ( QgsInvalidArgumentException &e )
4784 {
4785 mLastError = u"%1 Requested vertex: %2 was resolved as: [part: %3, ring: %4, vertex: %5]"_s //
4786 .arg( e.what() )
4787 .arg( vertexIndex )
4788 .arg( modifiedPart )
4789 .arg( modifiedRing )
4790 .arg( resolvedVertexIndex );
4791 return QgsGeometry();
4792 }
4793
4794 if ( !result )
4795 {
4796 mLastError = u"Operation '%1' generates a null geometry."_s.arg( qgsEnumValueToKey( op ) );
4797 return QgsGeometry();
4798 }
4799
4800 if ( result->isEmpty() )
4801 return QgsGeometry( std::move( result ) );
4802
4803 // insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon
4804 auto updatePolygon = []( const QgsPolygon *inputPoly, QgsAbstractGeometry *result, int modifiedRing ) -> std::unique_ptr<QgsPolygon> {
4805 auto newPoly = std::make_unique<QgsPolygon>();
4806 for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex )
4807 {
4808 if ( ringIndex == modifiedRing )
4809 {
4810 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
4811 {
4812 if ( ringIndex == 0 && resPartIte == result->parts_begin() )
4813 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
4814 else
4815 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( ( *resPartIte )->clone() ) );
4816 }
4817 }
4818 else
4819 {
4820 if ( ringIndex == 0 )
4821 newPoly->setExteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->exteriorRing()->clone() ) );
4822 else
4823 newPoly->addInteriorRing( qgsgeometry_cast<QgsCurve *>( inputPoly->interiorRing( ringIndex - 1 )->clone() ) );
4824 }
4825 }
4826 return newPoly;
4827 };
4828
4829 std::unique_ptr<QgsAbstractGeometry> finalGeom;
4830 if ( geomType == Qgis::GeometryType::Line )
4831 {
4832 if ( modifiedPart >= 0 )
4833 {
4834 auto newMultiLine = std::make_unique<QgsMultiLineString>();
4835 int partIndex = 0;
4836 for ( QgsMultiLineString::part_iterator partIte = inputMultiLine->parts_begin(); partIte != inputMultiLine->parts_end(); ++partIte )
4837 {
4838 if ( partIndex == modifiedPart )
4839 {
4840 for ( QgsAbstractGeometry::part_iterator resPartIte = result->parts_begin(); resPartIte != result->parts_end(); ++resPartIte )
4841 {
4842 newMultiLine->addGeometry( ( *resPartIte )->clone() );
4843 }
4844 }
4845 else
4846 {
4847 newMultiLine->addGeometry( ( *partIte )->clone() );
4848 }
4849 partIndex++;
4850 }
4851 finalGeom = std::move( newMultiLine );
4852 }
4853 else
4854 {
4855 // resultGeom is already the correct result!
4856 finalGeom = std::move( result );
4857 }
4858 }
4859 else
4860 {
4861 // geomType == Qgis::GeometryType::Polygon
4862 if ( modifiedPart >= 0 )
4863 {
4864 auto newMultiPoly = std::make_unique<QgsMultiPolygon>();
4865 int partIndex = 0;
4866 for ( QgsAbstractGeometry::part_iterator partIte = inputMultiPoly->parts_begin(); partIte != inputMultiPoly->parts_end(); ++partIte )
4867 {
4868 if ( partIndex == modifiedPart )
4869 {
4870 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( *partIte ), result.get(), modifiedRing );
4871 newMultiPoly->addGeometry( newPoly.release() );
4872 }
4873 else
4874 {
4875 newMultiPoly->addGeometry( ( *partIte )->clone() );
4876 }
4877 partIndex++;
4878 }
4879 finalGeom.reset( dynamic_cast<QgsAbstractGeometry *>( newMultiPoly.release() ) );
4880 }
4881 else
4882 {
4883 std::unique_ptr<QgsPolygon> newPoly = updatePolygon( qgsgeometry_cast<const QgsPolygon *>( d->geometry.get() ), result.get(), modifiedRing );
4884 finalGeom = std::move( newPoly );
4885 }
4886 }
4887
4888 QgsGeometry finalResult( std::move( finalGeom ) );
4889
4890 QgsDebugMsgLevel( u"Final result Wkt: %1"_s.arg( finalResult.asWkt( 2 ) ), 3 );
4891
4892 return finalResult;
4893}
4894
4895
4896QgsGeometry QgsGeometry::chamfer( int vertexIndex, double distance1, double distance2 ) const
4897{
4898 return doChamferFillet( ChamferFilletOperationType::Chamfer, vertexIndex, distance1, distance2, 0 );
4899}
4900
4901QgsGeometry QgsGeometry::fillet( int vertexIndex, double radius, int segments ) const
4902{
4903 return doChamferFillet( ChamferFilletOperationType::Fillet, vertexIndex, radius, 0.0, segments );
4904}
4905
4906QgsGeometry QgsGeometry::chamfer( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double distance1, double distance2 )
4907{
4908 std::unique_ptr<QgsLineString> result( QgsGeometryUtils::createChamferGeometry( segment1Start, segment1End, segment2Start, segment2End, distance1, distance2 ) );
4909
4910 if ( !result )
4911 {
4912 return QgsGeometry();
4913 }
4914
4915 return QgsGeometry( std::move( result ) );
4916}
4917
4918QgsGeometry QgsGeometry::fillet( const QgsPoint &segment1Start, const QgsPoint &segment1End, const QgsPoint &segment2Start, const QgsPoint &segment2End, double radius, int segments )
4919{
4920 std::unique_ptr<QgsAbstractGeometry> result( QgsGeometryUtils::createFilletGeometry( segment1Start, segment1End, segment2Start, segment2End, radius, segments ) );
4921
4922 if ( !result )
4923 {
4924 return QgsGeometry();
4925 }
4926
4927 return QgsGeometry( std::move( result ) );
4928}
@ AllowSelfTouchingHoles
Indicates that self-touching holes are permitted. OGC validity states that self-touching holes are NO...
Definition qgis.h:2151
BufferSide
Side of line to buffer.
Definition qgis.h:2176
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3405
AngularDirection
Angular directions.
Definition qgis.h:3546
@ NoOrientation
Unknown orientation or sentinel value.
Definition qgis.h:3549
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:2121
@ AddPartSelectedGeometryNotFound
The selected geometry cannot be found.
Definition qgis.h:2131
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
Definition qgis.h:2125
@ Success
Operation succeeded.
Definition qgis.h:2122
@ SelectionIsEmpty
No features were selected.
Definition qgis.h:2126
@ GeometryTypeHasChanged
Operation has changed geometry type.
Definition qgis.h:2140
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
Definition qgis.h:2137
@ AddRingCrossesExistingRings
The input ring crosses existing rings (it is not disjoint).
Definition qgis.h:2136
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition qgis.h:2132
@ AddRingNotClosed
The input ring is not closed.
Definition qgis.h:2134
@ SelectionIsGreaterThanOne
More than one features were selected.
Definition qgis.h:2127
@ SplitCannotSplitPoint
Cannot split points.
Definition qgis.h:2139
@ GeometryEngineError
Geometry engine misses a method implemented or an error occurred in the geometry engine.
Definition qgis.h:2128
@ NothingHappened
Nothing happened, without any error.
Definition qgis.h:2123
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition qgis.h:2124
@ LayerNotEditable
Cannot edit layer.
Definition qgis.h:2129
@ AddRingNotValid
The input ring is not valid.
Definition qgis.h:2135
QFlags< GeometryValidityFlag > GeometryValidityFlags
Geometry validity flags.
Definition qgis.h:2155
@ Segment
The actual start or end point of a segment.
Definition qgis.h:3180
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:2164
@ QgisInternal
Use internal QgsGeometryValidator method.
Definition qgis.h:2165
@ Geos
Use GEOS validation methods.
Definition qgis.h:2166
QFlags< GeosCreationFlag > GeosCreationFlags
Geos geometry creation behavior flags.
Definition qgis.h:2238
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:2201
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2188
CoverageValidityResult
Coverage validity results.
Definition qgis.h:2247
@ Error
An exception occurred while determining validity.
Definition qgis.h:2250
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3390
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2260
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:2764
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
bool contains(const QgsPoint &point, double epsilon=1E-8) const
Returns true if the circle contains the point.
QgsCircularString * toCircularString(bool oriented=false) const
Returns a circular string from the circle.
static QgsCircle minimalCircleFrom3Points(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon=1E-8)
Constructs the smallest circle from 3 points.
Circular string geometry type.
static QgsCircularString fromTwoPointsAndCenter(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center, bool useShortestArc=true)
Creates a circular string with a single arc representing the curve from p1 to p2 with the specified c...
Compound curve geometry type.
bool toggleCircularAtVertex(QgsVertexId position)
Converts the vertex at the given position from/to circular.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
A const WKB pointer.
Definition qgswkbptr.h: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.
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.
QgsGeometry clipped(const QgsRectangle &rectangle)
Clips the geometry using the specified rectangle.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
int makeDifferenceInPlace(const QgsGeometry &other)
Changes this geometry such that it does not intersect the other geometry.
void adjacentVertices(int atVertex, int &beforeVertex, int &afterVertex) const
Returns the indexes of the vertices before and after the given vertex index.
QgsMultiPolygonXY asMultiPolygon() const
Returns the contents of the geometry as a multi-polygon.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
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...
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.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform)
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
QgsGeometry minimumWidth() const
Returns a linestring geometry which represents the minimum diameter of the geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
Qgis::CoverageValidityResult validateCoverage(double gapWidth, QgsGeometry *invalidEdges=nullptr) const
Analyze a coverage (represented as a collection of polygonal geometry with exactly matching edge geom...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
QgsGeometry makeDifference(const QgsGeometry &other) const
Returns the geometry formed by modifying this geometry such that it does not intersect the other geom...
QgsGeometry simplifyCoverageVW(double tolerance, bool preserveBoundary) const
Operates on a coverage (represented as a list of polygonal geometry with exactly matching edge geomet...
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry 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.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
double frechetDistance(const QgsGeometry &geom) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry convertToType(Qgis::GeometryType destType, bool destMultipart=false) const
Try to convert the geometry to the requested type.
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
bool isAxisParallelRectangle(double maximumDeviation, bool simpleRectanglesOnly=false) const
Returns true if the geometry is a polygon that is almost an axis-parallel rectangle.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
void normalize()
Reorganizes the geometry into a normalized form (or "canonical" form).
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Returns the length of the QByteArray returned by asWkb().
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static Q_DECL_DEPRECATED QgsPolylineXY createPolylineFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolylineXY from a QPolygonF.
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
QgsBox3D boundingBox3D() const
Returns the 3D bounding box of the geometry.
friend class QgsInternalGeometryEngine
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsGeometry subdivide(int maxNodes=256, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Subdivides the geometry.
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...
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
bool insertVertex(double x, double y, int beforeVertex)
Insert a new vertex before the given vertex index, ring and item (first number is index 0) If the req...
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
static Q_DECL_DEPRECATED QgsPolygonXY createPolygonFromQPolygonF(const QPolygonF &polygon)
Creates a QgsPolygonXYfrom a QPolygonF.
bool convertToSingleType()
Converts multi type geometry into single type geometry e.g.
Qgis::GeometryOperationResult addRing(const QVector< QgsPointXY > &ring)
Adds a new ring to this geometry.
Qgis::GeometryType type
bool requiresConversionToStraightSegments() const
Returns true if the geometry is a curved geometry type which requires conversion to display as straig...
bool isSimple() const
Determines whether the geometry is simple (according to OGC definition), i.e.
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new LineString geometry from a list of QgsPoint points.
void validateGeometry(QVector< QgsGeometry::Error > &errors, Qgis::GeometryValidationEngine method=Qgis::GeometryValidationEngine::QgisInternal, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Validates geometry and produces a list of geometry errors.
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
Qgis::GeometryOperationResult avoidIntersectionsV2(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QPointF asQPointF() const
Returns contents of the geometry as a QPointF if wkbType is WKBPoint, otherwise returns a null QPoint...
void convertToStraightSegment(double tolerance=M_PI/180., QgsAbstractGeometry::SegmentationToleranceType toleranceType=QgsAbstractGeometry::MaximumAngle)
Converts the geometry to straight line segments, if it is a curved geometry type.
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
QgsGeometry & operator=(QgsGeometry const &rhs)
Creates a shallow copy of the geometry.
QgsGeometry orthogonalize(double tolerance=1.0E-8, int maxIterations=1000, double angleThreshold=15.0) const
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries angl...
Qgis::AngularDirection polygonOrientation() const
Returns the orientation of the polygon.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry largestEmptyCircle(double tolerance, const QgsGeometry &boundary=QgsGeometry()) const
Constructs the Largest Empty Circle for a set of obstacle geometries, up to a specified tolerance.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult addPart(const QVector< QgsPointXY > &points, Qgis::GeometryType geomType=Qgis::GeometryType::Unknown)
Adds a new part to a the geometry.
QgsGeometryPartIterator parts()
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry convertToCurves(double distanceTolerance=1e-8, double angleTolerance=1e-8) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry voronoiDiagram(const QgsGeometry &extent=QgsGeometry(), double tolerance=0.0, bool edgesOnly=false) const
Creates a Voronoi diagram for the nodes contained within the geometry.
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry minimumClearanceLine() const
Returns a LineString whose endpoints define the minimum clearance of a geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
virtual ~QgsGeometry()
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygonXY.
double sqrDistToVertexAt(QgsPointXY &point, int atVertex) const
Returns the squared Cartesian distance between the given point to the given vertex index (vertex at t...
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
QVector< QgsGeometry > coerceToType(Qgis::WkbType type, double defaultZ=0, double defaultM=0, bool avoidDuplicates=true) const
Attempts to coerce this geometry into the specified destination type.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsGeometry node() const
Returns a (Multi)LineString representing the fully noded version of a collection of linestrings.
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
int vertexNrFromVertexId(QgsVertexId id) const
Returns the vertex number corresponding to a vertex id.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false)
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
bool convertGeometryCollectionToSubclass(Qgis::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
QgsAbstractGeometry::part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QString asJson(int precision=17) const
Exports the geometry to a GeoJSON string.
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
double frechetDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Fréchet distance between this geometry and geom, restricted to discrete points for both g...
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
QgsGeometry unionCoverage() const
Optimized union algorithm for polygonal inputs that are correctly noded and do not overlap.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
bool convertToCurvedMultiType()
Converts a geometry into a multitype geometry of curve kind (when there is a corresponding curve type...
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
QgsGeometry orientedMinimumBoundingBox() const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometryConstPartIterator constParts() const
Returns Java-style iterator for traversal of parts of the geometry.
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult addPartV2(const QVector< QgsPointXY > &points, Qgis::WkbType wkbType=Qgis::WkbType::Unknown)
Adds a new part to a the geometry.
double minimumClearance() const
Computes the minimum clearance of a geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
Qgis::GeometryOperationResult reshapeGeometry(const QgsLineString &reshapeLineString)
Replaces a part of this geometry with another line.
double closestVertexWithContext(const QgsPointXY &point, int &atVertex) const
Searches for the closest vertex in this geometry to the given point.
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false) const
Returns the Delaunay triangulation for the vertices of the geometry.
void draw(QPainter &p) const
Draws the geometry onto a QPainter.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
bool toggleCircularAtVertex(int atVertex)
Converts the vertex at the given position from/to circular.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
bool moveVertex(double x, double y, int atVertex)
Moves the vertex at the given position number and item (first number is index 0) to the given coordin...
QgsGeometry constrainedDelaunayTriangulation() const
Returns a constrained Delaunay triangulation for the vertices of the geometry.
bool isGeosEqual(const QgsGeometry &) const
Compares the geometry with another geometry using GEOS.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
static QgsGeometry fromBox3D(const QgsBox3D &box)
Creates a new geometry from a QgsBox3D object Returns a 2D polygon geometry if the box is purely 2d,...
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
static QgsGeometry createWedgeBufferFromAngles(const QgsPoint &center, double startAngle, double endAngle, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
bool deletePart(int partNum)
Deletes part identified by the part number.
QgsGeometry removeInteriorRings(double minimumAllowedArea=-1) const
Removes the interior rings from a (multi)polygon geometry.
static QgsGeometry fromPoint(const QgsPoint &point)
Creates a new geometry from a QgsPoint object.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
Q_DECL_DEPRECATED int avoidIntersections(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:139
double frechetDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
Definition qgsgeos.cpp:827
double hausdorffDistanceDensify(const QgsAbstractGeometry *geometry, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:781
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition qgsgeos.cpp:2079
double hausdorffDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and another geometry.
Definition qgsgeos.cpp:758
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition qgsgeos.cpp:577
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry * > &geometries, QString *errorMsg=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
Definition qgsgeos.cpp:3239
double frechetDistance(const QgsAbstractGeometry *geometry, QString *errorMsg=nullptr) const
Returns the Fréchet distance between this geometry and another geometry, restricted to discrete point...
Definition qgsgeos.cpp:804
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.
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:76
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:7504
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7157
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
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