QGIS API Documentation 3.99.0-Master (a8882ad4560)
Loading...
Searching...
No Matches
qgscurvepolygon.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscurvepolygon.cpp
3 ---------------------
4 begin : September 2014
5 copyright : (C) 2014 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgscurvepolygon.h"
19
20#include <memory>
21#include <nlohmann/json.hpp>
22
23#include "qgsapplication.h"
24#include "qgscircularstring.h"
25#include "qgscompoundcurve.h"
26#include "qgsfeedback.h"
27#include "qgsgeometryutils.h"
28#include "qgslinestring.h"
29#include "qgsmulticurve.h"
30#include "qgspolygon.h"
31#include "qgswkbptr.h"
32
33#include <QJsonArray>
34#include <QJsonObject>
35#include <QPainter>
36#include <QPainterPath>
37
42
47
49{
50 auto result = std::make_unique< QgsCurvePolygon >();
51 result->mWkbType = mWkbType;
52 return result.release();
53}
54
56{
57 return u"CurvePolygon"_s;
58}
59
61{
62 return 2;
63}
64
66 : QgsSurface( p )
67
68{
70 if ( p.mExteriorRing )
71 {
72 mExteriorRing.reset( p.mExteriorRing->clone() );
73 }
74
75 for ( const QgsCurve *ring : p.mInteriorRings )
76 {
77 mInteriorRings.push_back( ring->clone() );
78 }
79
83}
84
85// cppcheck-suppress operatorEqVarError
87{
88 if ( &p != this )
89 {
91 if ( p.mExteriorRing )
92 {
93 mExteriorRing.reset( p.mExteriorRing->clone() );
94 }
95
96 for ( const QgsCurve *ring : p.mInteriorRings )
97 {
98 mInteriorRings.push_back( ring->clone() );
99 }
100 }
101 return *this;
102}
103
105{
106 return new QgsCurvePolygon( *this );
107}
108
110{
112 mExteriorRing.reset();
113 qDeleteAll( mInteriorRings );
114 mInteriorRings.clear();
115 clearCache();
116}
117
118
120{
121 clear();
122 if ( !wkbPtr )
123 {
124 return false;
125 }
126
127 Qgis::WkbType type = wkbPtr.readHeader();
129 {
130 return false;
131 }
132 mWkbType = type;
133
134 int nRings;
135 wkbPtr >> nRings;
136 std::unique_ptr< QgsCurve > currentCurve;
137 for ( int i = 0; i < nRings; ++i )
138 {
139 Qgis::WkbType curveType = wkbPtr.readHeader();
140 wkbPtr -= 1 + sizeof( int );
141 Qgis::WkbType flatCurveType = QgsWkbTypes::flatType( curveType );
142 if ( flatCurveType == Qgis::WkbType::LineString )
143 {
144 currentCurve = std::make_unique<QgsLineString>( );
145 }
146 else if ( flatCurveType == Qgis::WkbType::CircularString )
147 {
148 currentCurve = std::make_unique<QgsCircularString>( );
149 }
150 else if ( flatCurveType == Qgis::WkbType::CompoundCurve )
151 {
152 currentCurve = std::make_unique<QgsCompoundCurve>( );
153 }
154 else
155 {
156 return false;
157 }
158 currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
159 if ( i == 0 )
160 {
161 mExteriorRing = std::move( currentCurve );
162 }
163 else
164 {
165 mInteriorRings.append( currentCurve.release() );
166 }
167 }
168
169 return true;
170}
171
172bool QgsCurvePolygon::fromWkt( const QString &wkt )
173{
174 clear();
175
176 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
177
179 return false;
180
181 mWkbType = parts.first;
182
183 QString secondWithoutParentheses = parts.second;
184 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
185 if ( ( parts.second.compare( "EMPTY"_L1, Qt::CaseInsensitive ) == 0 ) ||
186 secondWithoutParentheses.isEmpty() )
187 return true;
188
189 QString defaultChildWkbType = u"LineString%1%2"_s.arg( is3D() ? u"Z"_s : QString(), isMeasure() ? u"M"_s : QString() );
190
191 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
192 for ( const QString &childWkt : blocks )
193 {
194 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
195
196 Qgis::WkbType flatCurveType = QgsWkbTypes::flatType( childParts.first );
197 if ( flatCurveType == Qgis::WkbType::LineString )
198 mInteriorRings.append( new QgsLineString() );
199 else if ( flatCurveType == Qgis::WkbType::CircularString )
200 mInteriorRings.append( new QgsCircularString() );
201 else if ( flatCurveType == Qgis::WkbType::CompoundCurve )
202 mInteriorRings.append( new QgsCompoundCurve() );
203 else
204 {
205 clear();
206 return false;
207 }
208 if ( !mInteriorRings.back()->fromWkt( childWkt ) )
209 {
210 clear();
211 return false;
212 }
213 }
214
215 if ( mInteriorRings.isEmpty() )
216 {
217 clear();
218 return false;
219 }
220
221 mExteriorRing.reset( mInteriorRings.takeFirst() );
222
223 //scan through rings and check if dimensionality of rings is different to CurvePolygon.
224 //if so, update the type dimensionality of the CurvePolygon to match
225 bool hasZ = false;
226 bool hasM = false;
227 if ( mExteriorRing )
228 {
229 hasZ = hasZ || mExteriorRing->is3D();
230 hasM = hasM || mExteriorRing->isMeasure();
231 }
232 for ( const QgsCurve *curve : std::as_const( mInteriorRings ) )
233 {
234 hasZ = hasZ || curve->is3D();
235 hasM = hasM || curve->isMeasure();
236 if ( hasZ && hasM )
237 break;
238 }
239 if ( hasZ )
240 addZValue( 0 );
241 if ( hasM )
242 addMValue( 0 );
243
244 return true;
245}
246
248{
249 if ( mExteriorRing )
250 {
251 return mExteriorRing->boundingBox3D();
252 }
253 return QgsBox3D();
254}
255
257{
258 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
259 if ( mExteriorRing )
260 {
261 binarySize += mExteriorRing->wkbSize( flags );
262 }
263 for ( const QgsCurve *curve : mInteriorRings )
264 {
265 binarySize += curve->wkbSize( flags );
266 }
267 return binarySize;
268}
269
270QByteArray QgsCurvePolygon::asWkb( WkbFlags flags ) const
271{
272 QByteArray wkbArray;
273 wkbArray.resize( QgsCurvePolygon::wkbSize( flags ) );
274 QgsWkbPtr wkbPtr( wkbArray );
275 wkbPtr << static_cast<char>( QgsApplication::endian() );
276 wkbPtr << static_cast<quint32>( wkbType() );
277 wkbPtr << static_cast<quint32>( ( mExteriorRing ? 1 : 0 ) + mInteriorRings.size() );
278 if ( mExteriorRing )
279 {
280 wkbPtr << mExteriorRing->asWkb( flags );
281 }
282 for ( const QgsCurve *curve : mInteriorRings )
283 {
284 wkbPtr << curve->asWkb( flags );
285 }
286 return wkbArray;
287}
288
289QString QgsCurvePolygon::asWkt( int precision ) const
290{
291 QString wkt = wktTypeStr();
292
293 if ( isEmpty() )
294 wkt += " EMPTY"_L1;
295 else
296 {
297 wkt += " ("_L1;
298 if ( mExteriorRing )
299 {
300 QString childWkt = mExteriorRing->asWkt( precision );
302 {
303 // Type names of linear geometries are omitted
304 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
305 }
306 wkt += childWkt + ',';
307 }
308 for ( const QgsCurve *curve : mInteriorRings )
309 {
310 if ( !curve->isEmpty() )
311 {
312 QString childWkt = curve->asWkt( precision );
314 {
315 // Type names of linear geometries are omitted
316 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
317 }
318 wkt += childWkt + ',';
319 }
320 }
321 if ( wkt.endsWith( ',' ) )
322 {
323 wkt.chop( 1 ); // Remove last ','
324 }
325 wkt += ')';
326 }
327 return wkt;
328}
329
330QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
331{
332 // GML2 does not support curves
333 QDomElement elemPolygon = doc.createElementNS( ns, u"Polygon"_s );
334
335 if ( isEmpty() )
336 return elemPolygon;
337
338 QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, u"outerBoundaryIs"_s );
339 std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
340 QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
341 outerRing.toElement().setTagName( u"LinearRing"_s );
342 elemOuterBoundaryIs.appendChild( outerRing );
343 elemPolygon.appendChild( elemOuterBoundaryIs );
344 std::unique_ptr< QgsLineString > interiorLineString;
345 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
346 {
347 QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, u"innerBoundaryIs"_s );
348 interiorLineString.reset( interiorRing( i )->curveToLine() );
349 QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
350 innerRing.toElement().setTagName( u"LinearRing"_s );
351 elemInnerBoundaryIs.appendChild( innerRing );
352 elemPolygon.appendChild( elemInnerBoundaryIs );
353 }
354 return elemPolygon;
355}
356
357QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
358{
359 QDomElement elemCurvePolygon = doc.createElementNS( ns, u"Polygon"_s );
360
361 if ( isEmpty() )
362 return elemCurvePolygon;
363
364 const auto exportRing = [&doc, precision, &ns, axisOrder]( const QgsCurve * ring )
365 {
366 QDomElement ringElem = ring->asGml3( doc, precision, ns, axisOrder );
367 if ( ringElem.tagName() == "LineString"_L1 )
368 {
369 ringElem.setTagName( u"LinearRing"_s );
370 }
371 else if ( ringElem.tagName() == "CompositeCurve"_L1 )
372 {
373 ringElem.setTagName( u"Ring"_s );
374 }
375 else if ( ringElem.tagName() == "Curve"_L1 )
376 {
377 QDomElement ringElemNew = doc.createElementNS( ns, u"Ring"_s );
378 QDomElement curveMemberElem = doc.createElementNS( ns, u"curveMember"_s );
379 ringElemNew.appendChild( curveMemberElem );
380 curveMemberElem.appendChild( ringElem );
381 ringElem = std::move( ringElemNew );
382 }
383 return ringElem;
384 };
385
386 QDomElement elemExterior = doc.createElementNS( ns, u"exterior"_s );
387 elemExterior.appendChild( exportRing( exteriorRing() ) );
388 elemCurvePolygon.appendChild( elemExterior );
389
390 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
391 {
392 QDomElement elemInterior = doc.createElementNS( ns, u"interior"_s );
393 elemInterior.appendChild( exportRing( interiorRing( i ) ) );
394 elemCurvePolygon.appendChild( elemInterior );
395 }
396 return elemCurvePolygon;
397}
398
399json QgsCurvePolygon::asJsonObject( int precision ) const
400{
401 json coordinates( json::array( ) );
402 if ( auto *lExteriorRing = exteriorRing() )
403 {
404 std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
405 QgsPointSequence exteriorPts;
406 exteriorLineString->points( exteriorPts );
407 coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
408
409 std::unique_ptr< QgsLineString > interiorLineString;
410 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
411 {
412 interiorLineString.reset( interiorRing( i )->curveToLine() );
413 QgsPointSequence interiorPts;
414 interiorLineString->points( interiorPts );
415 coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
416 }
417 }
418 return
419 {
420 { "type", "Polygon" },
421 { "coordinates", coordinates }
422 };
423}
424
425QString QgsCurvePolygon::asKml( int precision ) const
426{
427 QString kml;
428 kml.append( "<Polygon>"_L1 );
429 if ( mExteriorRing )
430 {
431 kml.append( "<outerBoundaryIs>"_L1 );
432 kml.append( mExteriorRing->asKml( precision ) );
433 kml.append( "</outerBoundaryIs>"_L1 );
434 }
435 const QVector<QgsCurve *> &interiorRings = mInteriorRings;
436 for ( const QgsCurve *ring : interiorRings )
437 {
438 kml.append( "<innerBoundaryIs>"_L1 );
439 kml.append( ring->asKml( precision ) );
440 kml.append( "</innerBoundaryIs>"_L1 );
441 }
442 kml.append( "</Polygon>"_L1 );
443 return kml;
444}
445
447{
448 // normalize rings
449 if ( mExteriorRing )
450 mExteriorRing->normalize();
451
452 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
453 {
454 ring->normalize();
455 }
456
457 // sort rings
458 std::sort( mInteriorRings.begin(), mInteriorRings.end(), []( const QgsCurve * a, const QgsCurve * b )
459 {
460 return a->compareTo( b ) > 0;
461 } );
462
463 // normalize ring orientation
464 forceRHR();
465}
466
468{
469 if ( !mExteriorRing )
470 {
471 return 0.0;
472 }
473
474 double totalArea = 0.0;
475
476 if ( mExteriorRing->isRing() )
477 {
478 double area = 0.0;
479 mExteriorRing->sumUpArea( area );
480 totalArea += std::fabs( area );
481 }
482
483 for ( const QgsCurve *ring : mInteriorRings )
484 {
485 double area = 0.0;
486 if ( ring->isRing() )
487 {
488 ring->sumUpArea( area );
489 totalArea -= std::fabs( area );
490 }
491 }
492 return totalArea;
493}
494
496{
497 if ( !mExteriorRing )
498 {
499 return 0.0;
500 }
501
502 double totalArea3D = 0.0;
503
504 if ( mExteriorRing->isRing() )
505 {
506 double area3D = 0.0;
507 mExteriorRing->sumUpArea3D( area3D );
508 totalArea3D += std::abs( area3D );
509 }
510
511 for ( const QgsCurve *ring : mInteriorRings )
512 {
513 double area3D = 0.0;
514 if ( ring->isRing() )
515 {
516 ring->sumUpArea3D( area3D );
517 totalArea3D -= std::abs( area3D );
518 }
519 }
520
521 return totalArea3D;
522}
523
525{
526 if ( !mExteriorRing )
527 return 0.0;
528
529 //sum perimeter of rings
530 double perimeter = mExteriorRing->length();
531 for ( const QgsCurve *ring : mInteriorRings )
532 {
533 perimeter += ring->length();
534 }
535 return perimeter;
536}
537
539{
540 const double p = perimeter();
541 if ( qgsDoubleNear( p, 0.0 ) )
542 return 0.0;
543
544 return 4.0 * M_PI * area() / pow( p, 2.0 );
545}
546
548{
549 auto polygon = std::make_unique<QgsPolygon>();
550 if ( !mExteriorRing )
551 return polygon.release();
552
553 polygon->setExteriorRing( exteriorRing()->curveToLine() );
554 QVector<QgsCurve *> interiors;
555 int n = numInteriorRings();
556 interiors.reserve( n );
557 for ( int i = 0; i < n; ++i )
558 {
559 interiors.append( interiorRing( i )->curveToLine() );
560 }
561 polygon->setInteriorRings( interiors );
562 return polygon.release();
563}
564
566{
567 if ( !mExteriorRing )
568 return nullptr;
569
570 if ( mInteriorRings.isEmpty() )
571 {
572 return mExteriorRing->clone();
573 }
574 else
575 {
576 QgsMultiCurve *multiCurve = new QgsMultiCurve();
577 int nInteriorRings = mInteriorRings.size();
578 multiCurve->reserve( nInteriorRings + 1 );
579 multiCurve->addGeometry( mExteriorRing->clone() );
580 for ( int i = 0; i < nInteriorRings; ++i )
581 {
582 multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
583 }
584 return multiCurve;
585 }
586}
587
588QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
589{
590 if ( !mExteriorRing )
591 return nullptr;
592
593
594 std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
595
596 // exterior ring
597 auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) };
598
599 if ( !exterior )
600 return nullptr;
601
602 polygon->mExteriorRing = std::move( exterior );
603
604 //interior rings
605 for ( auto interior : mInteriorRings )
606 {
607 if ( !interior )
608 continue;
609
610 QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) );
611
612 if ( !gridifiedInterior )
613 continue;
614
615 polygon->mInteriorRings.append( gridifiedInterior );
616 }
617
618 return polygon.release();
619
620}
621
623{
624 if ( !mExteriorRing )
625 return nullptr;
626
627 // exterior ring
628 std::unique_ptr< QgsAbstractGeometry > exterior( mExteriorRing->simplifyByDistance( tolerance ) );
629 if ( !qgsgeometry_cast< QgsLineString * >( exterior.get() ) )
630 return nullptr;
631
632 auto polygon = std::make_unique< QgsPolygon >( qgis::down_cast< QgsLineString * >( exterior.release() ) );
633
634 //interior rings
635 for ( const QgsCurve *interior : mInteriorRings )
636 {
637 if ( !interior )
638 continue;
639
640 std::unique_ptr< QgsAbstractGeometry > simplifiedRing( interior->simplifyByDistance( tolerance ) );
641 if ( !simplifiedRing )
642 return nullptr;
643
644 if ( !qgsgeometry_cast< QgsLineString * >( simplifiedRing.get() ) )
645 return nullptr;
646
647 polygon->mInteriorRings.append( qgis::down_cast< QgsLineString * >( simplifiedRing.release() ) );
648 }
649
650 return polygon.release();
651}
652
653bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
654{
655 bool result = false;
656 auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
657 {
658 if ( ring->numPoints() <= 4 )
659 return false;
660
661 if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
662 {
663 QgsPoint startPoint;
664 Qgis::VertexType type;
665 ring->pointAt( 0, startPoint, type );
666 // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
667 ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
668 return true;
669 }
670
671 return false;
672 };
673 if ( mExteriorRing )
674 {
675 result = cleanRing( mExteriorRing.get() );
676 }
677 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
678 {
679 if ( cleanRing( ring ) ) result = true;
680 }
681 return result;
682}
683
685{
686 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
687 if ( !mExteriorRing && mInteriorRings.empty() )
688 return false;
689
690 // if we already have the bounding box calculated, then this check is trivial!
691 if ( !mBoundingBox.isNull() )
692 {
693 return mBoundingBox.intersects( box3d );
694 }
695
696 // loop through each ring and test the bounding box intersection.
697 // This gives us a chance to use optimisations which may be present on the individual
698 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
699 // of each individual ring geometry which we would have to do anyway... (and these
700 // bounding boxes are cached, so would be reused without additional expense)
701 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box3d ) )
702 return true;
703
704 for ( const QgsCurve *ring : mInteriorRings )
705 {
706 if ( ring->boundingBoxIntersects( box3d ) )
707 return true;
708 }
709
710 // even if we don't intersect the bounding box of any rings, we may still intersect the
711 // bounding box of the overall polygon (we are considering worst case scenario here and
712 // the polygon is invalid, with rings outside the exterior ring!)
713 // so here we fall back to the non-optimised base class check which has to first calculate
714 // the overall bounding box of the polygon..
715 return QgsSurface::boundingBoxIntersects( box3d );
716
717 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
718}
719
721{
722 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
723
724 if ( !mExteriorRing && mInteriorRings.empty() )
725 return false;
726
727 // if we already have the bounding box calculated, then this check is trivial!
728 if ( !mBoundingBox.isNull() )
729 {
730 return mBoundingBox.toRectangle().intersects( box );
731 }
732
733 // loop through each ring and test the bounding box intersection.
734 // This gives us a chance to use optimisations which may be present on the individual
735 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
736 // of each individual ring geometry which we would have to do anyway... (and these
737 // bounding boxes are cached, so would be reused without additional expense)
738 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box ) )
739 return true;
740
741 for ( const QgsCurve *ring : mInteriorRings )
742 {
743 if ( ring->boundingBoxIntersects( box ) )
744 return true;
745 }
746
747 // even if we don't intersect the bounding box of any rings, we may still intersect the
748 // bounding box of the overall polygon (we are considering worst case scenario here and
749 // the polygon is invalid, with rings outside the exterior ring!)
750 // so here we fall back to the non-optimised base class check which has to first calculate
751 // the overall bounding box of the polygon..
753
754 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
755}
756
757QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
758{
759 auto poly = std::make_unique<QgsPolygon>();
760 if ( !mExteriorRing )
761 {
762 return poly.release();
763 }
764
765 poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
766
767 QVector<QgsCurve *> rings;
768 rings.reserve( mInteriorRings.size() );
769 for ( const QgsCurve *ring : mInteriorRings )
770 {
771 rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
772 }
773 poly->setInteriorRings( rings );
774 return poly.release();
775}
776
778{
779 if ( !ring )
780 {
781 return;
782 }
783 mExteriorRing.reset( ring );
784
785 //set proper wkb type
787 {
789 }
791 {
793 }
794
795 //match dimensionality for rings
796 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
797 {
798 if ( is3D() )
799 ring->addZValue();
800 else
801 ring->dropZValue();
802
803 if ( isMeasure() )
804 ring->addMValue();
805 else
806 ring->dropMValue();
807 }
808 clearCache();
809}
810
811void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
812{
813 qDeleteAll( mInteriorRings );
814 mInteriorRings.clear();
815
816 //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
817 for ( QgsCurve *ring : rings )
818 {
819 addInteriorRing( ring );
820 }
821 clearCache();
822}
823
825{
826 if ( !ring )
827 return;
828
829 //ensure dimensionality of ring matches curve polygon
830 if ( !is3D() )
831 ring->dropZValue();
832 else if ( !ring->is3D() )
833 ring->addZValue();
834
835 if ( !isMeasure() )
836 ring->dropMValue();
837 else if ( !ring->isMeasure() )
838 ring->addMValue();
839
840 mInteriorRings.append( ring );
841 clearCache();
842}
843
845{
846 if ( nr < 0 || nr >= mInteriorRings.size() )
847 {
848 return false;
849 }
850 delete mInteriorRings.takeAt( nr );
851 clearCache();
852 return true;
853}
854
855void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
856{
857 for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
858 {
859 if ( minimumAllowedArea < 0 )
860 delete mInteriorRings.takeAt( ringIndex );
861 else
862 {
863 double area = 0.0;
864 mInteriorRings.at( ringIndex )->sumUpArea( area );
865 if ( std::fabs( area ) < minimumAllowedArea )
866 delete mInteriorRings.takeAt( ringIndex );
867 }
868 }
869
870 clearCache();
871}
872
874{
875 QVector<QgsCurve *> validRings;
876 validRings.reserve( mInteriorRings.size() );
877 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
878 {
879 if ( !curve->isRing() )
880 {
881 // remove invalid rings
882 delete curve;
883 }
884 else
885 {
886 validRings << curve;
887 }
888 }
889 mInteriorRings = validRings;
890}
891
896
898{
900 {
901 // flip exterior ring orientation
902 std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
903 mExteriorRing = std::move( flipped );
904 }
905
906 QVector<QgsCurve *> validRings;
907 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
908 {
909 if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
910 {
911 // flip interior ring orientation
912 QgsCurve *flipped = curve->reversed();
913 validRings << flipped;
914 delete curve;
915 }
916 else
917 {
918 validRings << curve;
919 }
920 }
921 mInteriorRings = validRings;
922}
923
925{
927 {
928 // flip exterior ring orientation
929 mExteriorRing.reset( mExteriorRing->reversed() );
930 }
931
932 QVector<QgsCurve *> validRings;
933 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
934 {
935 if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
936 {
937 // flip interior ring orientation
938 QgsCurve *flipped = curve->reversed();
939 validRings << flipped;
940 delete curve;
941 }
942 else
943 {
944 validRings << curve;
945 }
946 }
947 mInteriorRings = validRings;
948}
949
951{
952 QPainterPath p;
953 if ( mExteriorRing )
954 {
955 QPainterPath ring = mExteriorRing->asQPainterPath();
956 ring.closeSubpath();
957 p.addPath( ring );
958 }
959
960 for ( const QgsCurve *ring : mInteriorRings )
961 {
962 QPainterPath ringPath = ring->asQPainterPath();
963 ringPath.closeSubpath();
964 p.addPath( ringPath );
965 }
966
967 return p;
968}
969
970void QgsCurvePolygon::draw( QPainter &p ) const
971{
972 if ( !mExteriorRing )
973 return;
974
975 if ( mInteriorRings.empty() )
976 {
977 mExteriorRing->drawAsPolygon( p );
978 }
979 else
980 {
981 QPainterPath path;
982 mExteriorRing->addToPainterPath( path );
983
984 for ( const QgsCurve *ring : mInteriorRings )
985 {
986 ring->addToPainterPath( path );
987 }
988 p.drawPath( path );
989 }
990}
991
993{
994 if ( mExteriorRing )
995 {
996 mExteriorRing->transform( ct, d, transformZ );
997 }
998
999 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1000 {
1001 curve->transform( ct, d, transformZ );
1002 }
1003 clearCache();
1004}
1005
1006void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1007{
1008 if ( mExteriorRing )
1009 {
1010 mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
1011 }
1012
1013 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1014 {
1015 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
1016 }
1017 clearCache();
1018}
1019
1021{
1022 QgsCoordinateSequence sequence;
1023 sequence.append( QgsRingSequence() );
1024
1025 if ( mExteriorRing )
1026 {
1027 sequence.back().append( QgsPointSequence() );
1028 mExteriorRing->points( sequence.back().back() );
1029 }
1030
1031 for ( const QgsCurve *ring : mInteriorRings )
1032 {
1033 sequence.back().append( QgsPointSequence() );
1034 ring->points( sequence.back().back() );
1035 }
1036
1037 return sequence;
1038}
1039
1041{
1042 int count = 0;
1043
1044 if ( mExteriorRing )
1045 {
1046 count += mExteriorRing->nCoordinates();
1047 }
1048
1049 for ( const QgsCurve *ring : mInteriorRings )
1050 {
1051 count += ring->nCoordinates();
1052 }
1053
1054 return count;
1055}
1056
1058{
1059 if ( id.part != 0 )
1060 return -1;
1061
1062 if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
1063 return -1;
1064
1065 int number = 0;
1066 if ( id.ring == 0 )
1067 {
1068 return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1069 }
1070 else
1071 {
1072 number += mExteriorRing->numPoints();
1073 }
1074
1075 for ( int i = 0; i < mInteriorRings.count(); ++i )
1076 {
1077 if ( id.ring == i + 1 )
1078 {
1079 int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1080 if ( partNumber == -1 )
1081 return -1;
1082 return number + partNumber;
1083 }
1084 else
1085 {
1086 number += mInteriorRings.at( i )->numPoints();
1087 }
1088 }
1089 return -1; // should not happen
1090}
1091
1093{
1094 if ( !mExteriorRing )
1095 return true;
1096
1097 return mExteriorRing->isEmpty();
1098}
1099
1100double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1101{
1102 if ( !mExteriorRing )
1103 {
1104 return -1;
1105 }
1106 QVector<QgsCurve *> segmentList;
1107 segmentList.append( mExteriorRing.get() );
1108 segmentList.append( mInteriorRings );
1109 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
1110}
1111
1113{
1114 if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
1115 {
1116 return false;
1117 }
1118
1119 if ( vId.ring < 0 )
1120 {
1121 vId.ring = 0;
1122 vId.vertex = -1;
1123 if ( vId.part < 0 )
1124 {
1125 vId.part = 0;
1126 }
1127 return mExteriorRing->nextVertex( vId, vertex );
1128 }
1129 else
1130 {
1131 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1132
1133 if ( ring->nextVertex( vId, vertex ) )
1134 {
1135 return true;
1136 }
1137 ++vId.ring;
1138 vId.vertex = -1;
1139 if ( vId.ring >= 1 + mInteriorRings.size() )
1140 {
1141 return false;
1142 }
1143 ring = mInteriorRings[ vId.ring - 1 ];
1144 return ring->nextVertex( vId, vertex );
1145 }
1146}
1147
1148void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1149{
1150 int n = curve->numPoints();
1151 if ( vertex.vertex < 0 || vertex.vertex >= n )
1152 {
1153 previousVertex = QgsVertexId();
1154 nextVertex = QgsVertexId();
1155 return;
1156 }
1157
1158 if ( vertex.vertex == 0 && n < 3 )
1159 {
1160 previousVertex = QgsVertexId();
1161 }
1162 else if ( vertex.vertex == 0 )
1163 {
1164 previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1165 }
1166 else
1167 {
1168 previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1169 }
1170 if ( vertex.vertex == n - 1 && n < 3 )
1171 {
1172 nextVertex = QgsVertexId();
1173 }
1174 else if ( vertex.vertex == n - 1 )
1175 {
1176 nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1177 }
1178 else
1179 {
1180 nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1181 }
1182}
1183
1185{
1186 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1187 {
1188 previousVertex = QgsVertexId();
1190 return;
1191 }
1192
1193 if ( vertex.ring == 0 )
1194 {
1195 ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1196 }
1197 else
1198 {
1199 ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1200 }
1201}
1202
1204{
1205 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1206 {
1207 return false;
1208 }
1209
1210 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1211 int n = ring->numPoints();
1212 bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1213 if ( !success )
1214 {
1215 return false;
1216 }
1217
1218 // If first or last vertex is inserted, re-sync the last/first vertex
1219 if ( vId.vertex == 0 )
1220 ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1221 else if ( vId.vertex == n )
1222 ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1223
1224 clearCache();
1225
1226 return true;
1227}
1228
1230{
1231 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1232 {
1233 return false;
1234 }
1235
1236 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1237 int n = ring->numPoints();
1238 bool success = ring->moveVertex( vId, newPos );
1239 if ( success )
1240 {
1241 // If first or last vertex is moved, also move the last/first vertex
1242 if ( vId.vertex == 0 )
1243 ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1244 else if ( vId.vertex == n - 1 )
1245 ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1246 clearCache();
1247 }
1248 return success;
1249}
1250
1252{
1253 const int interiorRingId = vId.ring - 1;
1254 if ( !mExteriorRing || vId.ring < 0 || interiorRingId >= mInteriorRings.size() )
1255 {
1256 return false;
1257 }
1258
1259 // cppcheck-suppress containerOutOfBounds
1260 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1261 int n = ring->numPoints();
1262 if ( n <= 4 )
1263 {
1264 //no points will be left in ring, so remove whole ring
1265 if ( vId.ring == 0 )
1266 {
1267 mExteriorRing.reset();
1268 if ( !mInteriorRings.isEmpty() )
1269 {
1270 mExteriorRing.reset( mInteriorRings.takeFirst() );
1271 }
1272 }
1273 else
1274 {
1275 removeInteriorRing( vId.ring - 1 );
1276 }
1277 clearCache();
1278 return true;
1279 }
1280
1281 bool success = ring->deleteVertex( vId );
1282 if ( success )
1283 {
1284 // If first or last vertex is removed, re-sync the last/first vertex
1285 // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1286 // may have been deleted (e.g. with CircularString)
1287 if ( vId.vertex == 0 )
1288 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1289 else if ( vId.vertex == n - 1 )
1290 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1291 clearCache();
1292 }
1293 return success;
1294}
1295
1297{
1298 if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1299 {
1300 return true;
1301 }
1302
1303 for ( const QgsCurve *ring : mInteriorRings )
1304 {
1305 if ( ring->hasCurvedSegments() )
1306 {
1307 return true;
1308 }
1309 }
1310 return false;
1311}
1312
1314{
1315 return toPolygon( tolerance, toleranceType );
1316}
1317
1319{
1320 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1321 {
1322 //makes no sense - conversion of false to double!
1323 return false;
1324 }
1325
1326 QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1327 return ring->vertexAngle( vertex );
1328}
1329
1330int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1331{
1332 return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1333}
1334
1336{
1337 return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1338}
1339
1341{
1342 return ringCount() > 0 ? 1 : 0;
1343}
1344
1346{
1347 return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1348}
1349
1351{
1352 if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1353 {
1354 return 0.0;
1355 }
1356
1357 const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1358 return ring->segmentLength( startVertex );
1359}
1360
1361bool QgsCurvePolygon::addZValue( double zValue )
1362{
1363 if ( QgsWkbTypes::hasZ( mWkbType ) )
1364 return false;
1365
1367
1368 if ( mExteriorRing )
1369 mExteriorRing->addZValue( zValue );
1370 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1371 {
1372 curve->addZValue( zValue );
1373 }
1374 clearCache();
1375 return true;
1376}
1377
1378bool QgsCurvePolygon::addMValue( double mValue )
1379{
1380 if ( QgsWkbTypes::hasM( mWkbType ) )
1381 return false;
1382
1384
1385 if ( mExteriorRing )
1386 mExteriorRing->addMValue( mValue );
1387 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1388 {
1389 curve->addMValue( mValue );
1390 }
1391 clearCache();
1392 return true;
1393}
1394
1396{
1397 if ( !is3D() )
1398 return false;
1399
1401 if ( mExteriorRing )
1402 mExteriorRing->dropZValue();
1403 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1404 {
1405 curve->dropZValue();
1406 }
1407 clearCache();
1408 return true;
1409}
1410
1412{
1413 if ( !isMeasure() )
1414 return false;
1415
1417 if ( mExteriorRing )
1418 mExteriorRing->dropMValue();
1419 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1420 {
1421 curve->dropMValue();
1422 }
1423 clearCache();
1424 return true;
1425}
1426
1428{
1429 if ( mExteriorRing )
1430 mExteriorRing->swapXy();
1431 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1432 {
1433 curve->swapXy();
1434 }
1435 clearCache();
1436}
1437
1439{
1440 return clone();
1441}
1442
1444{
1445 if ( !transformer )
1446 return false;
1447
1448 bool res = true;
1449 if ( mExteriorRing )
1450 res = mExteriorRing->transform( transformer, feedback );
1451
1452 if ( !res || ( feedback && feedback->isCanceled() ) )
1453 {
1454 clearCache();
1455 return false;
1456 }
1457
1458 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1459 {
1460 res = curve->transform( transformer );
1461
1462 if ( feedback && feedback->isCanceled() )
1463 res = false;
1464
1465 if ( !res )
1466 break;
1467 }
1468 clearCache();
1469 return res;
1470}
1471
1472void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1473{
1474 if ( mExteriorRing )
1475 mExteriorRing->filterVertices( filter );
1476
1477 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1478 {
1479 curve->filterVertices( filter );
1480 }
1481 clearCache();
1482}
1483
1484void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1485{
1486 if ( mExteriorRing )
1487 mExteriorRing->transformVertices( transform );
1488
1489 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1490 {
1491 curve->transformVertices( transform );
1492 }
1493 clearCache();
1494}
1495
1497{
1498 return 1 + mInteriorRings.count();
1499}
1500
1502{
1503 if ( index == 0 )
1504 return mExteriorRing.get();
1505 else
1506 return mInteriorRings.at( index - 1 );
1507}
1508
1510{
1511 const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1512 if ( !otherPolygon )
1513 return -1;
1514
1515 if ( mExteriorRing && !otherPolygon->mExteriorRing )
1516 return 1;
1517 else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1518 return -1;
1519 else if ( mExteriorRing && otherPolygon->mExteriorRing )
1520 {
1521 int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1522 if ( shellComp != 0 )
1523 {
1524 return shellComp;
1525 }
1526 }
1527
1528 const int nHole1 = mInteriorRings.size();
1529 const int nHole2 = otherPolygon->mInteriorRings.size();
1530 if ( nHole1 < nHole2 )
1531 {
1532 return -1;
1533 }
1534 if ( nHole1 > nHole2 )
1535 {
1536 return 1;
1537 }
1538
1539 for ( int i = 0; i < nHole1; i++ )
1540 {
1541 const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1542 if ( holeComp != 0 )
1543 {
1544 return holeComp;
1545 }
1546 }
1547
1548 return 0;
1549}
@ CounterClockwise
Counter-clockwise direction.
Definition qgis.h:3481
@ Clockwise
Clockwise direction.
Definition qgis.h:3480
VertexType
Types of vertex.
Definition qgis.h:3112
@ Polygon
Polygons.
Definition qgis.h:368
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ CompoundCurve
CompoundCurve.
Definition qgis.h:291
@ LineString
LineString.
Definition qgis.h:283
@ Polygon
Polygon.
Definition qgis.h:284
@ CircularString
CircularString.
Definition qgis.h:290
@ CurvePolygon
CurvePolygon.
Definition qgis.h:292
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2717
An abstract base class for classes which transform geometries by transforming input points to output ...
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool insertVertex(QgsVertexId position, const QgsPoint &vertex)=0
Inserts a vertex into the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
QgsAbstractGeometry()=default
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:42
Circular string geometry type.
Compound curve geometry type.
A const WKB pointer.
Definition qgswkbptr.h:139
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:56
Handles coordinate transforms between two coordinate systems.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns a WKB representation of the geometry.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
int wkbSize(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const override
Returns the length of the QByteArray returned by asWkb().
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML2 representation of the geometry.
double vertexAngle(QgsVertexId vertex) const override
Returns approximate rotation angle for a vertex.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
QgsCurvePolygon * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
void swapXy() override
Swaps the x and y coordinates from the geometry.
bool isEmpty() const override
Returns true if the geometry is empty.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
virtual QgsPolygon * toPolygon(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a new polygon geometry corresponding to a segmentized approximation of the curve.
QVector< QgsCurve * > mInteriorRings
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
void normalize() final
Reorganizes the geometry into a normalized form (or "canonical" form).
void removeInteriorRings(double minimumAllowedArea=-1)
Removes the interior rings from the polygon.
QgsCurvePolygon * clone() const override
Clones the geometry by performing a deep copy.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
QgsCurvePolygon * toCurveType() const override
Returns the geometry converted to the more generic curve type.
void forceRHR()
Forces the geometry to respect the Right-Hand-Rule, in which the area that is bounded by the polygon ...
QgsCurvePolygon * simplifyByDistance(double tolerance) const override
Simplifies the geometry by applying the Douglas Peucker simplification by distance algorithm.
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QgsBox3D calculateBoundingBox3D() const override
Calculates the minimal 3D bounding box for the geometry.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
double closestSegment(const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf=nullptr, double epsilon=4 *std::numeric_limits< double >::epsilon()) const override
Searches for the closest segment of the geometry to a given point.
int partCount() const override
Returns count of parts contained in the geometry.
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter) override
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
void draw(QPainter &p) const override
Draws the geometry using the specified QPainter.
double perimeter() const override
Returns the planar, 2-dimensional perimeter of the geometry.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
QgsCurvePolygon & operator=(const QgsCurvePolygon &p)
void forceCounterClockwise()
Forces the polygon to respect the exterior ring is counter-clockwise, interior rings are clockwise co...
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
double area3D() const override
Returns the 3-dimensional surface area of the geometry.
void forceClockwise()
Forces the polygon to respect the exterior ring is clockwise, interior rings are counter-clockwise co...
double roundness() const
Returns the roundness of the curve polygon.
virtual void addInteriorRing(QgsCurve *ring)
Adds an interior ring to the geometry (takes ownership).
bool boundingBoxIntersects(const QgsBox3D &box3d) const override
Returns true if the bounding box of this geometry intersects with a box3d.
~QgsCurvePolygon() override
int dimension() const override
Returns the inherent dimension of the geometry.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
virtual QgsPolygon * surfaceToPolygon() const
Gets a polygon representation of this surface.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
bool insertVertex(QgsVertexId position, const QgsPoint &vertex) override
Inserts a vertex into the geometry.
bool fromWkt(const QString &wkt) override
Sets the geometry from a WKT string.
void removeInvalidRings()
Removes any interior rings which are not valid from the polygon.
QgsAbstractGeometry * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
QString geometryType() const override
Returns a unique string representing the geometry type.
void transformVertices(const std::function< QgsPoint(const QgsPoint &) > &transform) override
Transforms the vertices from the geometry in place, applying the transform function to every vertex.
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", QgsAbstractGeometry::AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const override
Returns a GML3 representation of the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false) override
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
QgsCurvePolygon * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
bool dropMValue() override
Drops any measure values which exist in the geometry.
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void setInteriorRings(const QVector< QgsCurve * > &rings)
Sets all interior rings (takes ownership).
bool removeInteriorRing(int ringIndex)
Removes an interior ring from the polygon.
std::unique_ptr< QgsCurve > mExteriorRing
int compareToSameClass(const QgsAbstractGeometry *other) const final
Compares to an other geometry of the same class, and returns a integer for sorting of the two geometr...
double segmentLength(QgsVertexId startVertex) const override
Returns the length of the segment of the geometry which begins at startVertex.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual int numPoints() const =0
Returns the number of points in the curve.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition qgscurve.cpp:199
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition qgscurve.cpp:88
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void reserve(int size)
Attempts to allocate memory for at least size geometries.
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Line string geometry type, with support for z-dimension and m-values.
Multi curve geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Polygon geometry type.
Definition qgspolygon.h:33
A rectangle specified with double values.
Surface geometry type.
Definition qgssurface.h:34
QgsBox3D mBoundingBox
Definition qgssurface.h:101
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QString mValidityFailureReason
Definition qgssurface.h:103
bool mHasCachedValidity
Definition qgssurface.h:102
WKB pointer handler.
Definition qgswkbptr.h:45
static Qgis::WkbType dropM(Qgis::WkbType type)
Drops the m dimension (if present) for a WKB type and returns the new type.
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 Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6888
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
void ringAdjacentVertices(const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30
int vertex
Vertex number.
Definition qgsvertexid.h:94
int part
Part number.
Definition qgsvertexid.h:88
int ring
Ring number.
Definition qgsvertexid.h:91