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