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