QGIS API Documentation 4.3.0-Master (bf28115e945)
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#include <QString>
38
39using namespace Qt::StringLiterals;
40
45
50
52{
53 auto result = std::make_unique< QgsCurvePolygon >();
54 result->mWkbType = mWkbType;
55 return result.release();
56}
57
59{
60 return u"CurvePolygon"_s;
61}
62
64{
65 return 2;
66}
67
69 : QgsSurface( p )
70
71{
73 if ( p.mExteriorRing )
74 {
75 mExteriorRing.reset( p.mExteriorRing->clone() );
76 }
77
78 for ( const QgsCurve *ring : p.mInteriorRings )
79 {
80 mInteriorRings.push_back( ring->clone() );
81 }
82
86}
87
88// cppcheck-suppress operatorEqVarError
90{
91 if ( &p != this )
92 {
94 if ( p.mExteriorRing )
95 {
96 mExteriorRing.reset( p.mExteriorRing->clone() );
97 }
98
99 for ( const QgsCurve *ring : p.mInteriorRings )
100 {
101 mInteriorRings.push_back( ring->clone() );
102 }
103 }
104 return *this;
105}
106
108{
109 return new QgsCurvePolygon( *this );
110}
111
113{
115 mExteriorRing.reset();
116 qDeleteAll( mInteriorRings );
117 mInteriorRings.clear();
118 clearCache();
119}
120
121
123{
124 clear();
125 if ( !wkbPtr )
126 {
127 return false;
128 }
129
130 Qgis::WkbType type = wkbPtr.readHeader();
132 {
133 return false;
134 }
135 mWkbType = type;
136
137 int nRings;
138 wkbPtr >> nRings;
139 std::unique_ptr< QgsCurve > currentCurve;
140 for ( int i = 0; i < nRings; ++i )
141 {
142 Qgis::WkbType curveType = wkbPtr.readHeader();
143 wkbPtr -= 1 + sizeof( int );
144 Qgis::WkbType flatCurveType = QgsWkbTypes::flatType( curveType );
145 if ( flatCurveType == Qgis::WkbType::LineString )
146 {
147 currentCurve = std::make_unique<QgsLineString>();
148 }
149 else if ( flatCurveType == Qgis::WkbType::CircularString )
150 {
151 currentCurve = std::make_unique<QgsCircularString>();
152 }
153 else if ( flatCurveType == Qgis::WkbType::CompoundCurve )
154 {
155 currentCurve = std::make_unique<QgsCompoundCurve>();
156 }
157 else
158 {
159 return false;
160 }
161 currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
162 if ( i == 0 )
163 {
164 mExteriorRing = std::move( currentCurve );
165 }
166 else
167 {
168 mInteriorRings.append( currentCurve.release() );
169 }
170 }
171
172 return true;
173}
174
175bool QgsCurvePolygon::fromWkt( const QString &wkt )
176{
177 clear();
178
179 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
180
182 return false;
183
184 mWkbType = parts.first;
185
186 QString secondWithoutParentheses = parts.second;
187 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
188 if ( ( parts.second.compare( "EMPTY"_L1, Qt::CaseInsensitive ) == 0 ) || secondWithoutParentheses.isEmpty() )
189 return true;
190
191 QString defaultChildWkbType = u"LineString%1%2"_s.arg( is3D() ? u"Z"_s : QString(), isMeasure() ? u"M"_s : QString() );
192
193 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
194 for ( const QString &childWkt : blocks )
195 {
196 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
197
198 Qgis::WkbType flatCurveType = QgsWkbTypes::flatType( childParts.first );
199 if ( flatCurveType == Qgis::WkbType::LineString )
200 mInteriorRings.append( new QgsLineString() );
201 else if ( flatCurveType == Qgis::WkbType::CircularString )
202 mInteriorRings.append( new QgsCircularString() );
203 else if ( flatCurveType == Qgis::WkbType::CompoundCurve )
204 mInteriorRings.append( new QgsCompoundCurve() );
205 else
206 {
207 clear();
208 return false;
209 }
210 if ( !mInteriorRings.back()->fromWkt( childWkt ) )
211 {
212 clear();
213 return false;
214 }
215 }
216
217 if ( mInteriorRings.isEmpty() )
218 {
219 clear();
220 return false;
221 }
222
223 mExteriorRing.reset( mInteriorRings.takeFirst() );
224
225 //scan through rings and check if dimensionality of rings is different to CurvePolygon.
226 //if so, update the type dimensionality of the CurvePolygon to match
227 bool hasZ = false;
228 bool hasM = false;
229 if ( mExteriorRing )
230 {
231 hasZ = hasZ || mExteriorRing->is3D();
232 hasM = hasM || mExteriorRing->isMeasure();
233 }
234 for ( const QgsCurve *curve : std::as_const( mInteriorRings ) )
235 {
236 hasZ = hasZ || curve->is3D();
237 hasM = hasM || curve->isMeasure();
238 if ( hasZ && hasM )
239 break;
240 }
241 if ( hasZ )
242 addZValue( 0 );
243 if ( hasM )
244 addMValue( 0 );
245
246 return true;
247}
248
250{
251 if ( mExteriorRing )
252 {
253 return mExteriorRing->boundingBox3D();
254 }
255 return QgsBox3D();
256}
257
259{
260 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
261 if ( mExteriorRing )
262 {
263 binarySize += mExteriorRing->wkbSize( flags );
264 }
265 for ( const QgsCurve *curve : mInteriorRings )
266 {
267 binarySize += curve->wkbSize( flags );
268 }
269 return binarySize;
270}
271
272QByteArray QgsCurvePolygon::asWkb( WkbFlags flags ) const
273{
274 QByteArray wkbArray;
275 wkbArray.resize( QgsCurvePolygon::wkbSize( flags ) );
276 QgsWkbPtr wkbPtr( wkbArray );
277 wkbPtr << static_cast<char>( QgsApplication::endian() );
278 wkbPtr << static_cast<quint32>( wkbType() );
279 wkbPtr << static_cast<quint32>( ( mExteriorRing ? 1 : 0 ) + mInteriorRings.size() );
280 if ( mExteriorRing )
281 {
282 wkbPtr << mExteriorRing->asWkb( flags );
283 }
284 for ( const QgsCurve *curve : mInteriorRings )
285 {
286 wkbPtr << curve->asWkb( flags );
287 }
288 return wkbArray;
289}
290
291QString QgsCurvePolygon::asWkt( int precision ) const
292{
293 QString wkt = wktTypeStr();
294
295 if ( isEmpty() )
296 wkt += " EMPTY"_L1;
297 else
298 {
299 wkt += " ("_L1;
300 if ( mExteriorRing )
301 {
302 QString childWkt = mExteriorRing->asWkt( precision );
304 {
305 // Type names of linear geometries are omitted
306 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
307 }
308 wkt += childWkt + ',';
309 }
310 for ( const QgsCurve *curve : mInteriorRings )
311 {
312 if ( !curve->isEmpty() )
313 {
314 QString childWkt = curve->asWkt( precision );
316 {
317 // Type names of linear geometries are omitted
318 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
319 }
320 wkt += childWkt + ',';
321 }
322 }
323 if ( wkt.endsWith( ',' ) )
324 {
325 wkt.chop( 1 ); // Remove last ','
326 }
327 wkt += ')';
328 }
329 return wkt;
330}
331
332QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
333{
334 // GML2 does not support curves
335 QDomElement elemPolygon = doc.createElementNS( ns, u"Polygon"_s );
336
337 if ( isEmpty() )
338 return elemPolygon;
339
340 QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, u"outerBoundaryIs"_s );
341 std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
342 QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
343 outerRing.toElement().setTagName( u"LinearRing"_s );
344 elemOuterBoundaryIs.appendChild( outerRing );
345 elemPolygon.appendChild( elemOuterBoundaryIs );
346 std::unique_ptr< QgsLineString > interiorLineString;
347 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
348 {
349 QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, u"innerBoundaryIs"_s );
350 interiorLineString.reset( interiorRing( i )->curveToLine() );
351 QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
352 innerRing.toElement().setTagName( u"LinearRing"_s );
353 elemInnerBoundaryIs.appendChild( innerRing );
354 elemPolygon.appendChild( elemInnerBoundaryIs );
355 }
356 return elemPolygon;
357}
358
359QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
360{
361 QDomElement elemCurvePolygon = doc.createElementNS( ns, u"Polygon"_s );
362
363 if ( isEmpty() )
364 return elemCurvePolygon;
365
366 const auto exportRing = [&doc, precision, &ns, axisOrder]( const QgsCurve *ring ) {
367 QDomElement ringElem = ring->asGml3( doc, precision, ns, axisOrder );
368 if ( ringElem.tagName() == "LineString"_L1 )
369 {
370 ringElem.setTagName( u"LinearRing"_s );
371 }
372 else if ( ringElem.tagName() == "CompositeCurve"_L1 )
373 {
374 ringElem.setTagName( u"Ring"_s );
375 }
376 else if ( ringElem.tagName() == "Curve"_L1 )
377 {
378 QDomElement ringElemNew = doc.createElementNS( ns, u"Ring"_s );
379 QDomElement curveMemberElem = doc.createElementNS( ns, u"curveMember"_s );
380 ringElemNew.appendChild( curveMemberElem );
381 curveMemberElem.appendChild( ringElem );
382 ringElem = std::move( ringElemNew );
383 }
384 return ringElem;
385 };
386
387 QDomElement elemExterior = doc.createElementNS( ns, u"exterior"_s );
388 elemExterior.appendChild( exportRing( exteriorRing() ) );
389 elemCurvePolygon.appendChild( elemExterior );
390
391 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
392 {
393 QDomElement elemInterior = doc.createElementNS( ns, u"interior"_s );
394 elemInterior.appendChild( exportRing( interiorRing( i ) ) );
395 elemCurvePolygon.appendChild( elemInterior );
396 }
397 return elemCurvePolygon;
398}
399
400json QgsCurvePolygon::asJsonObject( int precision, Qgis::GeoJsonProfile profile ) const
401{
402 switch ( profile )
403 {
406 {
407 json coordinates( json::array() );
408 if ( const QgsCurve *lExteriorRing = exteriorRing() )
409 {
410 std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
411 QgsPointSequence exteriorPts;
412 exteriorLineString->points( exteriorPts );
413 coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision, profile ) );
414
415 std::unique_ptr< QgsLineString > interiorLineString;
416 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
417 {
418 interiorLineString.reset( interiorRing( i )->curveToLine() );
419 QgsPointSequence interiorPts;
420 interiorLineString->points( interiorPts );
421 coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision, profile ) );
422 }
423 }
424 return { { "type", "Polygon" }, { "coordinates", coordinates } };
425 }
428 {
429 json geometries = json::array();
430 if ( const QgsCurve *lExteriorRing = exteriorRing() )
431 {
432 geometries.push_back( lExteriorRing->asJsonObject( precision, profile ) );
433 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
434 {
435 geometries.push_back( interiorRing( i )->asJsonObject( precision, profile ) );
436 }
437 }
438 return { { "type", "CurvePolygon" }, { "geometries", geometries } };
439 }
440 }
442}
443
444QString QgsCurvePolygon::asKml( int precision ) const
445{
446 QString kml;
447 kml.append( "<Polygon>"_L1 );
448 if ( mExteriorRing )
449 {
450 kml.append( "<outerBoundaryIs>"_L1 );
451 kml.append( mExteriorRing->asKml( precision ) );
452 kml.append( "</outerBoundaryIs>"_L1 );
453 }
454 const QVector<QgsCurve *> &interiorRings = mInteriorRings;
455 for ( const QgsCurve *ring : interiorRings )
456 {
457 kml.append( "<innerBoundaryIs>"_L1 );
458 kml.append( ring->asKml( precision ) );
459 kml.append( "</innerBoundaryIs>"_L1 );
460 }
461 kml.append( "</Polygon>"_L1 );
462 return kml;
463}
464
466{
467 // normalize rings
468 if ( mExteriorRing )
469 mExteriorRing->normalize();
470
471 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
472 {
473 ring->normalize();
474 }
475
476 // sort rings
477 std::sort( mInteriorRings.begin(), mInteriorRings.end(), []( const QgsCurve *a, const QgsCurve *b ) { return a->compareTo( b ) > 0; } );
478
479 // normalize ring orientation
480 forceRHR();
481}
482
484{
485 if ( !mExteriorRing )
486 {
487 return 0.0;
488 }
489
490 double totalArea = 0.0;
491
492 if ( mExteriorRing->isRing() )
493 {
494 double area = 0.0;
495 mExteriorRing->sumUpArea( area );
496 totalArea += std::fabs( area );
497 }
498
499 for ( const QgsCurve *ring : mInteriorRings )
500 {
501 double area = 0.0;
502 if ( ring->isRing() )
503 {
504 ring->sumUpArea( area );
505 totalArea -= std::fabs( area );
506 }
507 }
508 return totalArea;
509}
510
512{
513 if ( !mExteriorRing )
514 {
515 return 0.0;
516 }
517
518 double totalArea3D = 0.0;
519
520 if ( mExteriorRing->isRing() )
521 {
522 double area3D = 0.0;
523 mExteriorRing->sumUpArea3D( area3D );
524 totalArea3D += std::abs( area3D );
525 }
526
527 for ( const QgsCurve *ring : mInteriorRings )
528 {
529 double area3D = 0.0;
530 if ( ring->isRing() )
531 {
532 ring->sumUpArea3D( area3D );
533 totalArea3D -= std::abs( area3D );
534 }
535 }
536
537 return totalArea3D;
538}
539
541{
542 if ( !mExteriorRing )
543 return 0.0;
544
545 //sum perimeter of rings
546 double perimeter = mExteriorRing->length();
547 for ( const QgsCurve *ring : mInteriorRings )
548 {
549 perimeter += ring->length();
550 }
551 return perimeter;
552}
553
555{
556 const double p = perimeter();
557 if ( qgsDoubleNear( p, 0.0 ) )
558 return 0.0;
559
560 return 4.0 * M_PI * area() / pow( p, 2.0 );
561}
562
564{
565 auto polygon = std::make_unique<QgsPolygon>();
566 if ( !mExteriorRing )
567 return polygon.release();
568
569 polygon->setExteriorRing( exteriorRing()->curveToLine() );
570 QVector<QgsCurve *> interiors;
571 int n = numInteriorRings();
572 interiors.reserve( n );
573 for ( int i = 0; i < n; ++i )
574 {
575 interiors.append( interiorRing( i )->curveToLine() );
576 }
577 polygon->setInteriorRings( interiors );
578 return polygon.release();
579}
580
582{
583 if ( !mExteriorRing )
584 return nullptr;
585
586 if ( mInteriorRings.isEmpty() )
587 {
588 return mExteriorRing->clone();
589 }
590 else
591 {
592 QgsMultiCurve *multiCurve = new QgsMultiCurve();
593 int nInteriorRings = mInteriorRings.size();
594 multiCurve->reserve( nInteriorRings + 1 );
595 multiCurve->addGeometry( mExteriorRing->clone() );
596 for ( int i = 0; i < nInteriorRings; ++i )
597 {
598 multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
599 }
600 return multiCurve;
601 }
602}
603
604QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
605{
606 if ( !mExteriorRing )
607 return nullptr;
608
609
610 std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
611
612 // exterior ring
613 auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) };
614
615 if ( !exterior )
616 return nullptr;
617
618 polygon->mExteriorRing = std::move( exterior );
619
620 //interior rings
621 for ( auto interior : mInteriorRings )
622 {
623 if ( !interior )
624 continue;
625
626 QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) );
627
628 if ( !gridifiedInterior )
629 continue;
630
631 polygon->mInteriorRings.append( gridifiedInterior );
632 }
633
634 return polygon.release();
635}
636
638{
639 if ( !mExteriorRing )
640 return nullptr;
641
642 // exterior ring
643 std::unique_ptr< QgsAbstractGeometry > exterior( mExteriorRing->simplifyByDistance( tolerance ) );
644 if ( !qgsgeometry_cast< QgsLineString * >( exterior.get() ) )
645 return nullptr;
646
647 auto polygon = std::make_unique< QgsPolygon >( qgis::down_cast< QgsLineString * >( exterior.release() ) );
648
649 //interior rings
650 for ( const QgsCurve *interior : mInteriorRings )
651 {
652 if ( !interior )
653 continue;
654
655 std::unique_ptr< QgsAbstractGeometry > simplifiedRing( interior->simplifyByDistance( tolerance ) );
656 if ( !simplifiedRing )
657 return nullptr;
658
659 if ( !qgsgeometry_cast< QgsLineString * >( simplifiedRing.get() ) )
660 return nullptr;
661
662 polygon->mInteriorRings.append( qgis::down_cast< QgsLineString * >( simplifiedRing.release() ) );
663 }
664
665 return polygon.release();
666}
667
668bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
669{
670 bool result = false;
671 auto cleanRing = [epsilon, useZValues]( QgsCurve *ring ) -> bool {
672 if ( ring->numPoints() <= 4 )
673 return false;
674
675 if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
676 {
677 QgsPoint startPoint;
678 Qgis::VertexType type;
679 ring->pointAt( 0, startPoint, type );
680 // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
681 ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
682 return true;
683 }
684
685 return false;
686 };
687 if ( mExteriorRing )
688 {
689 result = cleanRing( mExteriorRing.get() );
690 }
691 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
692 {
693 if ( cleanRing( ring ) )
694 result = true;
695 }
696 return result;
697}
698
700{
701 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
702 if ( !mExteriorRing && mInteriorRings.empty() )
703 return false;
704
705 // if we already have the bounding box calculated, then this check is trivial!
706 if ( !mBoundingBox.isNull() )
707 {
708 return mBoundingBox.intersects( box3d );
709 }
710
711 // loop through each ring and test the bounding box intersection.
712 // This gives us a chance to use optimisations which may be present on the individual
713 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
714 // of each individual ring geometry which we would have to do anyway... (and these
715 // bounding boxes are cached, so would be reused without additional expense)
716 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box3d ) )
717 return true;
718
719 for ( const QgsCurve *ring : mInteriorRings )
720 {
721 if ( ring->boundingBoxIntersects( box3d ) )
722 return true;
723 }
724
725 // even if we don't intersect the bounding box of any rings, we may still intersect the
726 // bounding box of the overall polygon (we are considering worst case scenario here and
727 // the polygon is invalid, with rings outside the exterior ring!)
728 // so here we fall back to the non-optimised base class check which has to first calculate
729 // the overall bounding box of the polygon..
730 return QgsSurface::boundingBoxIntersects( box3d );
731
732 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
733}
734
736{
737 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
738
739 if ( !mExteriorRing && mInteriorRings.empty() )
740 return false;
741
742 // if we already have the bounding box calculated, then this check is trivial!
743 if ( !mBoundingBox.isNull() )
744 {
745 return mBoundingBox.toRectangle().intersects( box );
746 }
747
748 // loop through each ring and test the bounding box intersection.
749 // This gives us a chance to use optimisations which may be present on the individual
750 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
751 // of each individual ring geometry which we would have to do anyway... (and these
752 // bounding boxes are cached, so would be reused without additional expense)
753 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box ) )
754 return true;
755
756 for ( const QgsCurve *ring : mInteriorRings )
757 {
758 if ( ring->boundingBoxIntersects( box ) )
759 return true;
760 }
761
762 // even if we don't intersect the bounding box of any rings, we may still intersect the
763 // bounding box of the overall polygon (we are considering worst case scenario here and
764 // the polygon is invalid, with rings outside the exterior ring!)
765 // so here we fall back to the non-optimised base class check which has to first calculate
766 // the overall bounding box of the polygon..
768
769 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
770}
771
772QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
773{
774 auto poly = std::make_unique<QgsPolygon>();
775 if ( !mExteriorRing )
776 {
777 return poly.release();
778 }
779
780 poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
781
782 QVector<QgsCurve *> rings;
783 rings.reserve( mInteriorRings.size() );
784 for ( const QgsCurve *ring : mInteriorRings )
785 {
786 rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
787 }
788 poly->setInteriorRings( rings );
789 return poly.release();
790}
791
793{
794 if ( !ring )
795 {
796 return;
797 }
798 mExteriorRing.reset( ring );
799
800 //set proper wkb type
802 {
804 }
806 {
808 }
809
810 //match dimensionality for rings
811 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
812 {
813 if ( is3D() )
814 ring->addZValue();
815 else
816 ring->dropZValue();
817
818 if ( isMeasure() )
819 ring->addMValue();
820 else
821 ring->dropMValue();
822 }
823 clearCache();
824}
825
826void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
827{
828 qDeleteAll( mInteriorRings );
829 mInteriorRings.clear();
830
831 //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
832 for ( QgsCurve *ring : rings )
833 {
834 addInteriorRing( ring );
835 }
836 clearCache();
837}
838
840{
841 if ( !ring )
842 return;
843
844 //ensure dimensionality of ring matches curve polygon
845 if ( !is3D() )
846 ring->dropZValue();
847 else if ( !ring->is3D() )
848 ring->addZValue();
849
850 if ( !isMeasure() )
851 ring->dropMValue();
852 else if ( !ring->isMeasure() )
853 ring->addMValue();
854
855 mInteriorRings.append( ring );
856 clearCache();
857}
858
860{
861 if ( nr < 0 || nr >= mInteriorRings.size() )
862 {
863 return false;
864 }
865 delete mInteriorRings.takeAt( nr );
866 clearCache();
867 return true;
868}
869
870void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
871{
872 for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
873 {
874 if ( minimumAllowedArea < 0 )
875 delete mInteriorRings.takeAt( ringIndex );
876 else
877 {
878 double area = 0.0;
879 mInteriorRings.at( ringIndex )->sumUpArea( area );
880 if ( std::fabs( area ) < minimumAllowedArea )
881 delete mInteriorRings.takeAt( ringIndex );
882 }
883 }
884
885 clearCache();
886}
887
889{
890 QVector<QgsCurve *> validRings;
891 validRings.reserve( mInteriorRings.size() );
892 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
893 {
894 if ( !curve->isRing() )
895 {
896 // remove invalid rings
897 delete curve;
898 }
899 else
900 {
901 validRings << curve;
902 }
903 }
904 mInteriorRings = validRings;
905}
906
911
913{
915 {
916 // flip exterior ring orientation
917 std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
918 mExteriorRing = std::move( flipped );
919 }
920
921 QVector<QgsCurve *> validRings;
922 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
923 {
924 if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
925 {
926 // flip interior ring orientation
927 QgsCurve *flipped = curve->reversed();
928 validRings << flipped;
929 delete curve;
930 }
931 else
932 {
933 validRings << curve;
934 }
935 }
936 mInteriorRings = validRings;
937}
938
940{
942 {
943 // flip exterior ring orientation
944 mExteriorRing.reset( mExteriorRing->reversed() );
945 }
946
947 QVector<QgsCurve *> validRings;
948 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
949 {
950 if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
951 {
952 // flip interior ring orientation
953 QgsCurve *flipped = curve->reversed();
954 validRings << flipped;
955 delete curve;
956 }
957 else
958 {
959 validRings << curve;
960 }
961 }
962 mInteriorRings = validRings;
963}
964
966{
967 QPainterPath p;
968 if ( mExteriorRing )
969 {
970 QPainterPath ring = mExteriorRing->asQPainterPath();
971 ring.closeSubpath();
972 p.addPath( ring );
973 }
974
975 for ( const QgsCurve *ring : mInteriorRings )
976 {
977 QPainterPath ringPath = ring->asQPainterPath();
978 ringPath.closeSubpath();
979 p.addPath( ringPath );
980 }
981
982 return p;
983}
984
985void QgsCurvePolygon::draw( QPainter &p ) const
986{
987 if ( !mExteriorRing )
988 return;
989
990 if ( mInteriorRings.empty() )
991 {
992 mExteriorRing->drawAsPolygon( p );
993 }
994 else
995 {
996 QPainterPath path;
997 mExteriorRing->addToPainterPath( path );
998
999 for ( const QgsCurve *ring : mInteriorRings )
1000 {
1001 ring->addToPainterPath( path );
1002 }
1003 p.drawPath( path );
1004 }
1005}
1006
1008{
1009 if ( mExteriorRing )
1010 {
1011 mExteriorRing->transform( ct, d, transformZ );
1012 }
1013
1014 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1015 {
1016 curve->transform( ct, d, transformZ );
1017 }
1018 clearCache();
1019}
1020
1021void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1022{
1023 if ( mExteriorRing )
1024 {
1025 mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
1026 }
1027
1028 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1029 {
1030 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
1031 }
1032 clearCache();
1033}
1034
1036{
1037 QgsCoordinateSequence sequence;
1038 sequence.append( QgsRingSequence() );
1039
1040 if ( mExteriorRing )
1041 {
1042 sequence.back().append( QgsPointSequence() );
1043 mExteriorRing->points( sequence.back().back() );
1044 }
1045
1046 for ( const QgsCurve *ring : mInteriorRings )
1047 {
1048 sequence.back().append( QgsPointSequence() );
1049 ring->points( sequence.back().back() );
1050 }
1051
1052 return sequence;
1053}
1054
1056{
1057 int count = 0;
1058
1059 if ( mExteriorRing )
1060 {
1061 count += mExteriorRing->nCoordinates();
1062 }
1063
1064 for ( const QgsCurve *ring : mInteriorRings )
1065 {
1066 count += ring->nCoordinates();
1067 }
1068
1069 return count;
1070}
1071
1073{
1074 if ( id.part != 0 )
1075 return -1;
1076
1077 if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
1078 return -1;
1079
1080 int number = 0;
1081 if ( id.ring == 0 )
1082 {
1083 return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1084 }
1085 else
1086 {
1087 number += mExteriorRing->numPoints();
1088 }
1089
1090 for ( int i = 0; i < mInteriorRings.count(); ++i )
1091 {
1092 if ( id.ring == i + 1 )
1093 {
1094 int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1095 if ( partNumber == -1 )
1096 return -1;
1097 return number + partNumber;
1098 }
1099 else
1100 {
1101 number += mInteriorRings.at( i )->numPoints();
1102 }
1103 }
1104 return -1; // should not happen
1105}
1106
1108{
1109 if ( !mExteriorRing )
1110 return true;
1111
1112 return mExteriorRing->isEmpty();
1113}
1114
1115double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1116{
1117 if ( !mExteriorRing )
1118 {
1119 return -1;
1120 }
1121 QVector<QgsCurve *> segmentList;
1122 segmentList.append( mExteriorRing.get() );
1123 segmentList.append( mInteriorRings );
1124 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
1125}
1126
1128{
1129 if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
1130 {
1131 return false;
1132 }
1133
1134 if ( vId.ring < 0 )
1135 {
1136 vId.ring = 0;
1137 vId.vertex = -1;
1138 if ( vId.part < 0 )
1139 {
1140 vId.part = 0;
1141 }
1142 return mExteriorRing->nextVertex( vId, vertex );
1143 }
1144 else
1145 {
1146 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1147
1148 if ( ring->nextVertex( vId, vertex ) )
1149 {
1150 return true;
1151 }
1152 ++vId.ring;
1153 vId.vertex = -1;
1154 if ( vId.ring >= 1 + mInteriorRings.size() )
1155 {
1156 return false;
1157 }
1158 ring = mInteriorRings[vId.ring - 1];
1159 return ring->nextVertex( vId, vertex );
1160 }
1161}
1162
1163void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1164{
1165 int n = curve->numPoints();
1166 if ( vertex.vertex < 0 || vertex.vertex >= n )
1167 {
1168 previousVertex = QgsVertexId();
1169 nextVertex = QgsVertexId();
1170 return;
1171 }
1172
1173 if ( vertex.vertex == 0 && n < 3 )
1174 {
1175 previousVertex = QgsVertexId();
1176 }
1177 else if ( vertex.vertex == 0 )
1178 {
1179 previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1180 }
1181 else
1182 {
1183 previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1184 }
1185 if ( vertex.vertex == n - 1 && n < 3 )
1186 {
1187 nextVertex = QgsVertexId();
1188 }
1189 else if ( vertex.vertex == n - 1 )
1190 {
1191 nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1192 }
1193 else
1194 {
1195 nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1196 }
1197}
1198
1200{
1201 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1202 {
1203 previousVertex = QgsVertexId();
1205 return;
1206 }
1207
1208 if ( vertex.ring == 0 )
1209 {
1210 ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1211 }
1212 else
1213 {
1214 ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1215 }
1216}
1217
1219{
1220 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1221 {
1222 return false;
1223 }
1224
1225 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1226 int n = ring->numPoints();
1227 bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1228 if ( !success )
1229 {
1230 return false;
1231 }
1232
1233 // If first or last vertex is inserted, re-sync the last/first vertex
1234 if ( vId.vertex == 0 )
1235 ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1236 else if ( vId.vertex == n )
1237 ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1238
1239 clearCache();
1240
1241 return true;
1242}
1243
1245{
1246 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1247 {
1248 return false;
1249 }
1250
1251 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1252 int n = ring->numPoints();
1253 bool success = ring->moveVertex( vId, newPos );
1254 if ( success )
1255 {
1256 // If first or last vertex is moved, also move the last/first vertex
1257 if ( vId.vertex == 0 )
1258 ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1259 else if ( vId.vertex == n - 1 )
1260 ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1261 clearCache();
1262 }
1263 return success;
1264}
1265
1267{
1268 const int interiorRingId = vId.ring - 1;
1269 if ( !mExteriorRing || vId.ring < 0 || interiorRingId >= mInteriorRings.size() )
1270 {
1271 return false;
1272 }
1273
1274 // cppcheck-suppress containerOutOfBounds
1275 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1276 int n = ring->numPoints();
1277 if ( n <= 4 )
1278 {
1279 //no points will be left in ring, so remove whole ring
1280 if ( vId.ring == 0 )
1281 {
1282 mExteriorRing.reset();
1283 if ( !mInteriorRings.isEmpty() )
1284 {
1285 mExteriorRing.reset( mInteriorRings.takeFirst() );
1286 }
1287 }
1288 else
1289 {
1290 removeInteriorRing( vId.ring - 1 );
1291 }
1292 clearCache();
1293 return true;
1294 }
1295
1296 bool success = ring->deleteVertex( vId );
1297 if ( success )
1298 {
1299 // If first or last vertex is removed, re-sync the last/first vertex
1300 // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1301 // may have been deleted (e.g. with CircularString)
1302 if ( vId.vertex == 0 )
1303 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1304 else if ( vId.vertex == n - 1 )
1305 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1306 clearCache();
1307 }
1308 return success;
1309}
1310
1311bool QgsCurvePolygon::deleteVertices( const QSet<QgsVertexId> &positions )
1312{
1313 if ( positions.empty() )
1314 {
1315 return false;
1316 }
1317
1318 QMap<int, QList<QgsVertexId >> ringVertices;
1319 for ( QgsVertexId pos : positions )
1320 {
1321 if ( !hasVertex( pos ) )
1322 {
1323 return false;
1324 }
1325
1326 ringVertices[pos.ring].append( QgsVertexId( 0, 0, pos.vertex ) );
1327 }
1328
1329 QMapIterator<int, QList<QgsVertexId >> ringVerticesIt( ringVertices );
1330
1331 ringVerticesIt.toBack();
1332 while ( ringVerticesIt.hasPrevious() )
1333 {
1334 ringVerticesIt.previous();
1335 QList<QgsVertexId> vertices = ringVerticesIt.value();
1336 int ringId = ringVerticesIt.key();
1337
1338 const int interiorRingId = ringId - 1;
1339
1340 // cppcheck-suppress containerOutOfBounds
1341 QgsCurve *ring = ringId == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1342
1343 int n = ring->numPoints();
1344
1345 // sort so we can check for first/last vertex deletion
1346 std::sort( vertices.begin(), vertices.end(), []( const QgsVertexId &a, const QgsVertexId &b ) { return a.vertex < b.vertex; } );
1347
1348 QgsVertexId firstVertexId = vertices.first();
1349 QgsVertexId lastVertexId = vertices.last();
1350
1351 // check if we are deleting the same point twice and remove the first, but not in a compound curve
1352 if ( ( firstVertexId.vertex == 0 ) && ( lastVertexId.vertex == n - 1 ) && !( QgsWkbTypes::flatType( ring->wkbType() ) == Qgis::WkbType::CompoundCurve ) )
1353 {
1354 vertices.removeFirst();
1355 }
1356
1357 if ( vertices.size() > n - 4 )
1358 {
1359 // no points will be left in ring, so remove whole ring
1360 if ( ringId == 0 )
1361 {
1362 mExteriorRing.reset();
1363 if ( !mInteriorRings.isEmpty() )
1364 {
1365 mExteriorRing.reset( mInteriorRings.takeFirst() );
1366 }
1367 }
1368 else
1369 {
1370 removeInteriorRing( ringId - 1 );
1371 }
1372 continue;
1373 }
1374
1375 if ( !ring->deleteVertices( QSet<QgsVertexId>( vertices.begin(), vertices.end() ) ) )
1376 {
1377 Q_ASSERT( false );
1378 return false;
1379 }
1380
1381 // in case of a compound curve, first/last vertex may have been deleted even if not specified
1382 // in such case, we copy the first vertex and add it at the end
1384 {
1385 // add start point at the end if not the same
1386 if ( !( ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) == ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) ) )
1387 {
1389 compoundRing->addVertex( ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1390 }
1391 continue;
1392 }
1393
1394 // If first or last vertex is removed, re-sync the last/first vertex
1395 if ( vertices.last().vertex == n - 1 )
1396 {
1397 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1398 }
1399 else if ( vertices.first().vertex == 0 )
1400 {
1401 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1402 }
1403 }
1404
1405 clearCache();
1406 return true;
1407}
1408
1410{
1411 if ( !mExteriorRing )
1412 return false;
1413
1414 if ( id.part == 0 && id.ring >= 0 && id.ring < mInteriorRings.size() + 1 )
1415 {
1416 // cppcheck-suppress containerOutOfBounds
1417 QgsCurve *ring = id.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( id.ring - 1 );
1418 return ring->hasVertex( QgsVertexId( 0, 0, id.vertex ) );
1419 }
1420
1421 return false;
1422}
1423
1425{
1426 if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1427 {
1428 return true;
1429 }
1430
1431 for ( const QgsCurve *ring : mInteriorRings )
1432 {
1433 if ( ring->hasCurvedSegments() )
1434 {
1435 return true;
1436 }
1437 }
1438 return false;
1439}
1440
1442{
1443 return toPolygon( tolerance, toleranceType );
1444}
1445
1447{
1448 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1449 {
1450 //makes no sense - conversion of false to double!
1451 return false;
1452 }
1453
1454 QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1455 return ring->vertexAngle( vertex );
1456}
1457
1458int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1459{
1460 return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1461}
1462
1464{
1465 return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1466}
1467
1469{
1470 return ringCount() > 0 ? 1 : 0;
1471}
1472
1474{
1475 return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1476}
1477
1479{
1480 if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1481 {
1482 return 0.0;
1483 }
1484
1485 const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1486 return ring->segmentLength( startVertex );
1487}
1488
1489bool QgsCurvePolygon::addZValue( double zValue )
1490{
1491 if ( QgsWkbTypes::hasZ( mWkbType ) )
1492 return false;
1493
1495
1496 if ( mExteriorRing )
1497 mExteriorRing->addZValue( zValue );
1498 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1499 {
1500 curve->addZValue( zValue );
1501 }
1502 clearCache();
1503 return true;
1504}
1505
1506bool QgsCurvePolygon::addMValue( double mValue )
1507{
1508 if ( QgsWkbTypes::hasM( mWkbType ) )
1509 return false;
1510
1512
1513 if ( mExteriorRing )
1514 mExteriorRing->addMValue( mValue );
1515 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1516 {
1517 curve->addMValue( mValue );
1518 }
1519 clearCache();
1520 return true;
1521}
1522
1524{
1525 if ( !is3D() )
1526 return false;
1527
1529 if ( mExteriorRing )
1530 mExteriorRing->dropZValue();
1531 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1532 {
1533 curve->dropZValue();
1534 }
1535 clearCache();
1536 return true;
1537}
1538
1540{
1541 if ( !isMeasure() )
1542 return false;
1543
1545 if ( mExteriorRing )
1546 mExteriorRing->dropMValue();
1547 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1548 {
1549 curve->dropMValue();
1550 }
1551 clearCache();
1552 return true;
1553}
1554
1556{
1557 if ( mExteriorRing )
1558 mExteriorRing->swapXy();
1559 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1560 {
1561 curve->swapXy();
1562 }
1563 clearCache();
1564}
1565
1567{
1568 return clone();
1569}
1570
1572{
1573 if ( !transformer )
1574 return false;
1575
1576 bool res = true;
1577 if ( mExteriorRing )
1578 res = mExteriorRing->transform( transformer, feedback );
1579
1580 if ( !res || ( feedback && feedback->isCanceled() ) )
1581 {
1582 clearCache();
1583 return false;
1584 }
1585
1586 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1587 {
1588 res = curve->transform( transformer );
1589
1590 if ( feedback && feedback->isCanceled() )
1591 res = false;
1592
1593 if ( !res )
1594 break;
1595 }
1596 clearCache();
1597 return res;
1598}
1599
1600void QgsCurvePolygon::filterVertices( const std::function<bool( const QgsPoint & )> &filter )
1601{
1602 if ( mExteriorRing )
1603 mExteriorRing->filterVertices( filter );
1604
1605 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1606 {
1607 curve->filterVertices( filter );
1608 }
1609 clearCache();
1610}
1611
1612void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1613{
1614 if ( mExteriorRing )
1615 mExteriorRing->transformVertices( transform );
1616
1617 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1618 {
1619 curve->transformVertices( transform );
1620 }
1621 clearCache();
1622}
1623
1625{
1626 return 1 + mInteriorRings.count();
1627}
1628
1630{
1631 if ( index == 0 )
1632 return mExteriorRing.get();
1633 else
1634 return mInteriorRings.at( index - 1 );
1635}
1636
1638{
1639 const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1640 if ( !otherPolygon )
1641 return -1;
1642
1643 if ( mExteriorRing && !otherPolygon->mExteriorRing )
1644 return 1;
1645 else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1646 return -1;
1647 else if ( mExteriorRing && otherPolygon->mExteriorRing )
1648 {
1649 int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1650 if ( shellComp != 0 )
1651 {
1652 return shellComp;
1653 }
1654 }
1655
1656 const int nHole1 = mInteriorRings.size();
1657 const int nHole2 = otherPolygon->mInteriorRings.size();
1658 if ( nHole1 < nHole2 )
1659 {
1660 return -1;
1661 }
1662 if ( nHole1 > nHole2 )
1663 {
1664 return 1;
1665 }
1666
1667 for ( int i = 0; i < nHole1; i++ )
1668 {
1669 const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1670 if ( holeComp != 0 )
1671 {
1672 return holeComp;
1673 }
1674 }
1675
1676 return 0;
1677}
@ CounterClockwise
Counter-clockwise direction.
Definition qgis.h:3629
@ Clockwise
Clockwise direction.
Definition qgis.h:3628
VertexType
Types of vertex.
Definition qgis.h:3260
GeoJsonProfile
GeoJson export Profile according to OGC Features and Geometries JSON - Part 1: Core https://docs....
Definition qgis.h:5045
@ Legacy
Legacy GeoJson profile used in QGIS prior to 4.2, which included some non-standard extensions and dev...
Definition qgis.h:5046
@ Rfc7946
GeoJson profile compliant with RFC7946 standard "http://www.opengis.net/def/profile/OGC/0/rfc7946".
Definition qgis.h:5047
@ JsonFg
GeoJson profile from OGC Features and Geometries JSON Part 1: core "http://www.opengis....
Definition qgis.h:5048
@ JsonFgPlus
GeoJson profile from OGC Features and Geometries JSON Part 1: core "http://www.opengis....
Definition qgis.h:5049
@ Polygon
Polygons.
Definition qgis.h:382
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ CompoundCurve
CompoundCurve.
Definition qgis.h:305
@ LineString
LineString.
Definition qgis.h:297
@ Polygon
Polygon.
Definition qgis.h:298
@ CircularString
CircularString.
Definition qgis.h:304
@ CurvePolygon
CurvePolygon.
Definition qgis.h:306
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2845
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.
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all 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.
virtual bool deleteVertices(const QSet< QgsVertexId > &positions)=0
Deletes vertices within 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:45
Circular string geometry type.
Compound curve geometry type.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
A const WKB pointer.
Definition qgswkbptr.h:211
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:60
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.
bool hasVertex(QgsVertexId position) const override
Returns true if the geometry contains a vertex matching the given position.
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.
bool deleteVertices(const QSet< QgsVertexId > &positions) override
Deletes vertices within the geometry.
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
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
json asJsonObject(int precision=17, Qgis::GeoJsonProfile profile=Qgis::GeoJsonProfile::Legacy) const override
Returns a json object representation of the geometry with the given precision and profile.
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:198
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
bool hasVertex(QgsVertexId position) const override
Returns true if the geometry contains a vertex matching the given position.
Definition qgscurve.cpp:266
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition qgscurve.cpp:87
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:56
void reserve(int size)
Attempts to allocate memory for at least size geometries.
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)
static json pointsToJson(const QgsPointSequence &points, int precision, Qgis::GeoJsonProfile profile)
Returns coordinates as json object.
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:53
Polygon geometry type.
Definition qgspolygon.h:37
A rectangle specified with double values.
Surface geometry type.
Definition qgssurface.h:34
QgsBox3D mBoundingBox
Definition qgssurface.h:99
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QString mValidityFailureReason
Definition qgssurface.h:101
bool mHasCachedValidity
Definition qgssurface.h:100
WKB pointer handler.
Definition qgswkbptr.h:47
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.
#define BUILTIN_UNREACHABLE
Definition qgis.h:8015
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7381
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:35
int vertex
Vertex number.
int part
Part number.
Definition qgsvertexid.h:94
int ring
Ring number.
Definition qgsvertexid.h:97