QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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#include "qgsapplication.h"
20#include "qgscircularstring.h"
21#include "qgscompoundcurve.h"
22#include "qgsgeometryutils.h"
23#include "qgslinestring.h"
24#include "qgspolygon.h"
25#include "qgswkbptr.h"
26#include "qgsmulticurve.h"
27#include "qgsfeedback.h"
28
29#include <QJsonArray>
30#include <QJsonObject>
31#include <QPainter>
32#include <QPainterPath>
33#include <memory>
34#include <nlohmann/json.hpp>
35
40
45
47{
48 auto result = std::make_unique< QgsCurvePolygon >();
49 result->mWkbType = mWkbType;
50 return result.release();
51}
52
54{
55 return QStringLiteral( "CurvePolygon" );
56}
57
59{
60 return 2;
61}
62
64 : QgsSurface( p )
65
66{
68 if ( p.mExteriorRing )
69 {
70 mExteriorRing.reset( p.mExteriorRing->clone() );
71 }
72
73 for ( const QgsCurve *ring : p.mInteriorRings )
74 {
75 mInteriorRings.push_back( ring->clone() );
76 }
77
81}
82
84{
85 if ( &p != this )
86 {
87 clearCache();
89 if ( p.mExteriorRing )
90 {
91 mExteriorRing.reset( p.mExteriorRing->clone() );
92 }
93
94 for ( const QgsCurve *ring : p.mInteriorRings )
95 {
96 mInteriorRings.push_back( ring->clone() );
97 }
98 }
99 return *this;
100}
101
103{
104 return new QgsCurvePolygon( *this );
105}
106
108{
110 mExteriorRing.reset();
111 qDeleteAll( mInteriorRings );
112 mInteriorRings.clear();
113 clearCache();
114}
115
116
118{
119 clear();
120 if ( !wkbPtr )
121 {
122 return false;
123 }
124
125 Qgis::WkbType type = wkbPtr.readHeader();
127 {
128 return false;
129 }
130 mWkbType = type;
131
132 int nRings;
133 wkbPtr >> nRings;
134 std::unique_ptr< QgsCurve > currentCurve;
135 for ( int i = 0; i < nRings; ++i )
136 {
137 Qgis::WkbType curveType = wkbPtr.readHeader();
138 wkbPtr -= 1 + sizeof( int );
139 Qgis::WkbType flatCurveType = QgsWkbTypes::flatType( curveType );
140 if ( flatCurveType == Qgis::WkbType::LineString )
141 {
142 currentCurve.reset( new QgsLineString() );
143 }
144 else if ( flatCurveType == Qgis::WkbType::CircularString )
145 {
146 currentCurve.reset( new QgsCircularString() );
147 }
148 else if ( flatCurveType == Qgis::WkbType::CompoundCurve )
149 {
150 currentCurve.reset( new QgsCompoundCurve() );
151 }
152 else
153 {
154 return false;
155 }
156 currentCurve->fromWkb( wkbPtr ); // also updates wkbPtr
157 if ( i == 0 )
158 {
159 mExteriorRing = std::move( currentCurve );
160 }
161 else
162 {
163 mInteriorRings.append( currentCurve.release() );
164 }
165 }
166
167 return true;
168}
169
170bool QgsCurvePolygon::fromWkt( const QString &wkt )
171{
172 clear();
173
174 QPair<Qgis::WkbType, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
175
177 return false;
178
179 mWkbType = parts.first;
180
181 QString secondWithoutParentheses = parts.second;
182 secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
183 if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
184 secondWithoutParentheses.isEmpty() )
185 return true;
186
187 QString defaultChildWkbType = QStringLiteral( "LineString%1%2" ).arg( is3D() ? QStringLiteral( "Z" ) : QString(), isMeasure() ? QStringLiteral( "M" ) : QString() );
188
189 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
190 for ( const QString &childWkt : blocks )
191 {
192 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
193
194 Qgis::WkbType flatCurveType = QgsWkbTypes::flatType( childParts.first );
195 if ( flatCurveType == Qgis::WkbType::LineString )
196 mInteriorRings.append( new QgsLineString() );
197 else if ( flatCurveType == Qgis::WkbType::CircularString )
198 mInteriorRings.append( new QgsCircularString() );
199 else if ( flatCurveType == Qgis::WkbType::CompoundCurve )
200 mInteriorRings.append( new QgsCompoundCurve() );
201 else
202 {
203 clear();
204 return false;
205 }
206 if ( !mInteriorRings.back()->fromWkt( childWkt ) )
207 {
208 clear();
209 return false;
210 }
211 }
212
213 if ( mInteriorRings.isEmpty() )
214 {
215 clear();
216 return false;
217 }
218
219 mExteriorRing.reset( mInteriorRings.takeFirst() );
220
221 //scan through rings and check if dimensionality of rings is different to CurvePolygon.
222 //if so, update the type dimensionality of the CurvePolygon to match
223 bool hasZ = false;
224 bool hasM = false;
225 if ( mExteriorRing )
226 {
227 hasZ = hasZ || mExteriorRing->is3D();
228 hasM = hasM || mExteriorRing->isMeasure();
229 }
230 for ( const QgsCurve *curve : std::as_const( mInteriorRings ) )
231 {
232 hasZ = hasZ || curve->is3D();
233 hasM = hasM || curve->isMeasure();
234 if ( hasZ && hasM )
235 break;
236 }
237 if ( hasZ )
238 addZValue( 0 );
239 if ( hasM )
240 addMValue( 0 );
241
242 return true;
243}
244
246{
247 if ( mExteriorRing )
248 {
249 return mExteriorRing->boundingBox3D();
250 }
251 return QgsBox3D();
252}
253
255{
256 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
257 if ( mExteriorRing )
258 {
259 binarySize += mExteriorRing->wkbSize( flags );
260 }
261 for ( const QgsCurve *curve : mInteriorRings )
262 {
263 binarySize += curve->wkbSize( flags );
264 }
265 return binarySize;
266}
267
268QByteArray QgsCurvePolygon::asWkb( WkbFlags flags ) const
269{
270 QByteArray wkbArray;
271 wkbArray.resize( QgsCurvePolygon::wkbSize( flags ) );
272 QgsWkbPtr wkbPtr( wkbArray );
273 wkbPtr << static_cast<char>( QgsApplication::endian() );
274 wkbPtr << static_cast<quint32>( wkbType() );
275 wkbPtr << static_cast<quint32>( ( mExteriorRing ? 1 : 0 ) + mInteriorRings.size() );
276 if ( mExteriorRing )
277 {
278 wkbPtr << mExteriorRing->asWkb( flags );
279 }
280 for ( const QgsCurve *curve : mInteriorRings )
281 {
282 wkbPtr << curve->asWkb( flags );
283 }
284 return wkbArray;
285}
286
288{
289 QString wkt = wktTypeStr();
290
291 if ( isEmpty() )
292 wkt += QLatin1String( " EMPTY" );
293 else
294 {
295 wkt += QLatin1String( " (" );
296 if ( mExteriorRing )
297 {
298 QString childWkt = mExteriorRing->asWkt( precision );
299 if ( qgsgeometry_cast<QgsLineString *>( mExteriorRing.get() ) )
300 {
301 // Type names of linear geometries are omitted
302 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
303 }
304 wkt += childWkt + ',';
305 }
306 for ( const QgsCurve *curve : mInteriorRings )
307 {
308 QString childWkt = curve->asWkt( precision );
309 if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
310 {
311 // Type names of linear geometries are omitted
312 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
313 }
314 wkt += childWkt + ',';
315 }
316 if ( wkt.endsWith( ',' ) )
317 {
318 wkt.chop( 1 ); // Remove last ','
319 }
320 wkt += ')';
321 }
322 return wkt;
323}
324
325QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
326{
327 // GML2 does not support curves
328 QDomElement elemPolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
329
330 if ( isEmpty() )
331 return elemPolygon;
332
333 QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, QStringLiteral( "outerBoundaryIs" ) );
334 std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
335 QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
336 outerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
337 elemOuterBoundaryIs.appendChild( outerRing );
338 elemPolygon.appendChild( elemOuterBoundaryIs );
339 std::unique_ptr< QgsLineString > interiorLineString;
340 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
341 {
342 QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, QStringLiteral( "innerBoundaryIs" ) );
343 interiorLineString.reset( interiorRing( i )->curveToLine() );
344 QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
345 innerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
346 elemInnerBoundaryIs.appendChild( innerRing );
347 elemPolygon.appendChild( elemInnerBoundaryIs );
348 }
349 return elemPolygon;
350}
351
352QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
353{
354 QDomElement elemCurvePolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
355
356 if ( isEmpty() )
357 return elemCurvePolygon;
358
359 const auto exportRing = [&doc, precision, &ns, axisOrder]( const QgsCurve * ring )
360 {
361 QDomElement ringElem = ring->asGml3( doc, precision, ns, axisOrder );
362 if ( ringElem.tagName() == QLatin1String( "LineString" ) )
363 {
364 ringElem.setTagName( QStringLiteral( "LinearRing" ) );
365 }
366 else if ( ringElem.tagName() == QLatin1String( "CompositeCurve" ) )
367 {
368 ringElem.setTagName( QStringLiteral( "Ring" ) );
369 }
370 else if ( ringElem.tagName() == QLatin1String( "Curve" ) )
371 {
372 QDomElement ringElemNew = doc.createElementNS( ns, QStringLiteral( "Ring" ) );
373 QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
374 ringElemNew.appendChild( curveMemberElem );
375 curveMemberElem.appendChild( ringElem );
376 ringElem = std::move( ringElemNew );
377 }
378 return ringElem;
379 };
380
381 QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
382 elemExterior.appendChild( exportRing( exteriorRing() ) );
383 elemCurvePolygon.appendChild( elemExterior );
384
385 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
386 {
387 QDomElement elemInterior = doc.createElementNS( ns, QStringLiteral( "interior" ) );
388 elemInterior.appendChild( exportRing( interiorRing( i ) ) );
389 elemCurvePolygon.appendChild( elemInterior );
390 }
391 return elemCurvePolygon;
392}
393
395{
396 json coordinates( json::array( ) );
397 if ( auto *lExteriorRing = exteriorRing() )
398 {
399 std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
400 QgsPointSequence exteriorPts;
401 exteriorLineString->points( exteriorPts );
402 coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
403
404 std::unique_ptr< QgsLineString > interiorLineString;
405 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
406 {
407 interiorLineString.reset( interiorRing( i )->curveToLine() );
408 QgsPointSequence interiorPts;
409 interiorLineString->points( interiorPts );
410 coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
411 }
412 }
413 return
414 {
415 { "type", "Polygon" },
416 { "coordinates", coordinates }
417 };
418}
419
421{
422 QString kml;
423 kml.append( QLatin1String( "<Polygon>" ) );
424 if ( mExteriorRing )
425 {
426 kml.append( QLatin1String( "<outerBoundaryIs>" ) );
427 kml.append( mExteriorRing->asKml( precision ) );
428 kml.append( QLatin1String( "</outerBoundaryIs>" ) );
429 }
430 const QVector<QgsCurve *> &interiorRings = mInteriorRings;
431 for ( const QgsCurve *ring : interiorRings )
432 {
433 kml.append( QLatin1String( "<innerBoundaryIs>" ) );
434 kml.append( ring->asKml( precision ) );
435 kml.append( QLatin1String( "</innerBoundaryIs>" ) );
436 }
437 kml.append( QLatin1String( "</Polygon>" ) );
438 return kml;
439}
440
442{
443 // normalize rings
444 if ( mExteriorRing )
445 mExteriorRing->normalize();
446
447 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
448 {
449 ring->normalize();
450 }
451
452 // sort rings
453 std::sort( mInteriorRings.begin(), mInteriorRings.end(), []( const QgsCurve * a, const QgsCurve * b )
454 {
455 return a->compareTo( b ) > 0;
456 } );
457
458 // normalize ring orientation
459 forceRHR();
460}
461
463{
464 if ( !mExteriorRing )
465 {
466 return 0.0;
467 }
468
469 double totalArea = 0.0;
470
471 if ( mExteriorRing->isRing() )
472 {
473 double area = 0.0;
474 mExteriorRing->sumUpArea( area );
475 totalArea += std::fabs( area );
476 }
477
478 for ( const QgsCurve *ring : mInteriorRings )
479 {
480 double area = 0.0;
481 if ( ring->isRing() )
482 {
483 ring->sumUpArea( area );
484 totalArea -= std::fabs( area );
485 }
486 }
487 return totalArea;
488}
489
491{
492 if ( !mExteriorRing )
493 return 0.0;
494
495 //sum perimeter of rings
496 double perimeter = mExteriorRing->length();
497 for ( const QgsCurve *ring : mInteriorRings )
498 {
499 perimeter += ring->length();
500 }
501 return perimeter;
502}
503
505{
506 const double p = perimeter();
507 if ( qgsDoubleNear( p, 0.0 ) )
508 return 0.0;
509
510 return 4.0 * M_PI * area() / pow( p, 2.0 );
511}
512
514{
515 std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
516 if ( !mExteriorRing )
517 return polygon.release();
518
519 polygon->setExteriorRing( exteriorRing()->curveToLine() );
520 QVector<QgsCurve *> interiors;
521 int n = numInteriorRings();
522 interiors.reserve( n );
523 for ( int i = 0; i < n; ++i )
524 {
525 interiors.append( interiorRing( i )->curveToLine() );
526 }
527 polygon->setInteriorRings( interiors );
528 return polygon.release();
529}
530
532{
533 if ( !mExteriorRing )
534 return nullptr;
535
536 if ( mInteriorRings.isEmpty() )
537 {
538 return mExteriorRing->clone();
539 }
540 else
541 {
542 QgsMultiCurve *multiCurve = new QgsMultiCurve();
543 int nInteriorRings = mInteriorRings.size();
544 multiCurve->reserve( nInteriorRings + 1 );
545 multiCurve->addGeometry( mExteriorRing->clone() );
546 for ( int i = 0; i < nInteriorRings; ++i )
547 {
548 multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
549 }
550 return multiCurve;
551 }
552}
553
554QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
555{
556 if ( !mExteriorRing )
557 return nullptr;
558
559
560 std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
561
562 // exterior ring
563 auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) };
564
565 if ( !exterior )
566 return nullptr;
567
568 polygon->mExteriorRing = std::move( exterior );
569
570 //interior rings
571 for ( auto interior : mInteriorRings )
572 {
573 if ( !interior )
574 continue;
575
576 QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) );
577
578 if ( !gridifiedInterior )
579 continue;
580
581 polygon->mInteriorRings.append( gridifiedInterior );
582 }
583
584 return polygon.release();
585
586}
587
589{
590 if ( !mExteriorRing )
591 return nullptr;
592
593 // exterior ring
594 std::unique_ptr< QgsAbstractGeometry > exterior( mExteriorRing->simplifyByDistance( tolerance ) );
595 if ( !qgsgeometry_cast< QgsLineString * >( exterior.get() ) )
596 return nullptr;
597
598 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >( qgis::down_cast< QgsLineString * >( exterior.release() ) );
599
600 //interior rings
601 for ( const QgsCurve *interior : mInteriorRings )
602 {
603 if ( !interior )
604 continue;
605
606 std::unique_ptr< QgsAbstractGeometry > simplifiedRing( interior->simplifyByDistance( tolerance ) );
607 if ( !simplifiedRing )
608 return nullptr;
609
610 if ( !qgsgeometry_cast< QgsLineString * >( simplifiedRing.get() ) )
611 return nullptr;
612
613 polygon->mInteriorRings.append( qgis::down_cast< QgsLineString * >( simplifiedRing.release() ) );
614 }
615
616 return polygon.release();
617}
618
619bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
620{
621 bool result = false;
622 auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
623 {
624 if ( ring->numPoints() <= 4 )
625 return false;
626
627 if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
628 {
629 QgsPoint startPoint;
630 Qgis::VertexType type;
631 ring->pointAt( 0, startPoint, type );
632 // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
633 ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
634 return true;
635 }
636
637 return false;
638 };
639 if ( mExteriorRing )
640 {
641 result = cleanRing( mExteriorRing.get() );
642 }
643 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
644 {
645 if ( cleanRing( ring ) ) result = true;
646 }
647 return result;
648}
649
651{
652 if ( !mExteriorRing && mInteriorRings.empty() )
653 return false;
654
655 // if we already have the bounding box calculated, then this check is trivial!
656 if ( !mBoundingBox.isNull() )
657 {
658 return mBoundingBox.intersects( box3d );
659 }
660
661 // loop through each ring and test the bounding box intersection.
662 // This gives us a chance to use optimisations which may be present on the individual
663 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
664 // of each individual ring geometry which we would have to do anyway... (and these
665 // bounding boxes are cached, so would be reused without additional expense)
666 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box3d ) )
667 return true;
668
669 for ( const QgsCurve *ring : mInteriorRings )
670 {
671 if ( ring->boundingBoxIntersects( box3d ) )
672 return true;
673 }
674
675 // even if we don't intersect the bounding box of any rings, we may still intersect the
676 // bounding box of the overall polygon (we are considering worst case scenario here and
677 // the polygon is invalid, with rings outside the exterior ring!)
678 // so here we fall back to the non-optimised base class check which has to first calculate
679 // the overall bounding box of the polygon..
680 return QgsSurface::boundingBoxIntersects( box3d );
681}
682
683QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
684{
685 std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
686 if ( !mExteriorRing )
687 {
688 return poly.release();
689 }
690
691 poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
692
693 QVector<QgsCurve *> rings;
694 rings.reserve( mInteriorRings.size() );
695 for ( const QgsCurve *ring : mInteriorRings )
696 {
697 rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
698 }
699 poly->setInteriorRings( rings );
700 return poly.release();
701}
702
704{
705 if ( !ring )
706 {
707 return;
708 }
709 mExteriorRing.reset( ring );
710
711 //set proper wkb type
713 {
715 }
717 {
719 }
720
721 //match dimensionality for rings
722 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
723 {
724 if ( is3D() )
725 ring->addZValue();
726 else
727 ring->dropZValue();
728
729 if ( isMeasure() )
730 ring->addMValue();
731 else
732 ring->dropMValue();
733 }
734 clearCache();
735}
736
737void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
738{
739 qDeleteAll( mInteriorRings );
740 mInteriorRings.clear();
741
742 //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
743 for ( QgsCurve *ring : rings )
744 {
745 addInteriorRing( ring );
746 }
747 clearCache();
748}
749
751{
752 if ( !ring )
753 return;
754
755 //ensure dimensionality of ring matches curve polygon
756 if ( !is3D() )
757 ring->dropZValue();
758 else if ( !ring->is3D() )
759 ring->addZValue();
760
761 if ( !isMeasure() )
762 ring->dropMValue();
763 else if ( !ring->isMeasure() )
764 ring->addMValue();
765
766 mInteriorRings.append( ring );
767 clearCache();
768}
769
771{
772 if ( nr < 0 || nr >= mInteriorRings.size() )
773 {
774 return false;
775 }
776 delete mInteriorRings.takeAt( nr );
777 clearCache();
778 return true;
779}
780
781void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
782{
783 for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
784 {
785 if ( minimumAllowedArea < 0 )
786 delete mInteriorRings.takeAt( ringIndex );
787 else
788 {
789 double area = 0.0;
790 mInteriorRings.at( ringIndex )->sumUpArea( area );
791 if ( std::fabs( area ) < minimumAllowedArea )
792 delete mInteriorRings.takeAt( ringIndex );
793 }
794 }
795
796 clearCache();
797}
798
800{
801 QVector<QgsCurve *> validRings;
802 validRings.reserve( mInteriorRings.size() );
803 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
804 {
805 if ( !curve->isRing() )
806 {
807 // remove invalid rings
808 delete curve;
809 }
810 else
811 {
812 validRings << curve;
813 }
814 }
815 mInteriorRings = validRings;
816}
817
822
824{
826 {
827 // flip exterior ring orientation
828 std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
829 mExteriorRing = std::move( flipped );
830 }
831
832 QVector<QgsCurve *> validRings;
833 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
834 {
835 if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
836 {
837 // flip interior ring orientation
838 QgsCurve *flipped = curve->reversed();
839 validRings << flipped;
840 delete curve;
841 }
842 else
843 {
844 validRings << curve;
845 }
846 }
847 mInteriorRings = validRings;
848}
849
851{
853 {
854 // flip exterior ring orientation
855 mExteriorRing.reset( mExteriorRing->reversed() );
856 }
857
858 QVector<QgsCurve *> validRings;
859 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
860 {
861 if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
862 {
863 // flip interior ring orientation
864 QgsCurve *flipped = curve->reversed();
865 validRings << flipped;
866 delete curve;
867 }
868 else
869 {
870 validRings << curve;
871 }
872 }
873 mInteriorRings = validRings;
874}
875
877{
878 QPainterPath p;
879 if ( mExteriorRing )
880 {
881 QPainterPath ring = mExteriorRing->asQPainterPath();
882 ring.closeSubpath();
883 p.addPath( ring );
884 }
885
886 for ( const QgsCurve *ring : mInteriorRings )
887 {
888 QPainterPath ringPath = ring->asQPainterPath();
889 ringPath.closeSubpath();
890 p.addPath( ringPath );
891 }
892
893 return p;
894}
895
896void QgsCurvePolygon::draw( QPainter &p ) const
897{
898 if ( !mExteriorRing )
899 return;
900
901 if ( mInteriorRings.empty() )
902 {
903 mExteriorRing->drawAsPolygon( p );
904 }
905 else
906 {
907 QPainterPath path;
908 mExteriorRing->addToPainterPath( path );
909
910 for ( const QgsCurve *ring : mInteriorRings )
911 {
912 ring->addToPainterPath( path );
913 }
914 p.drawPath( path );
915 }
916}
917
919{
920 if ( mExteriorRing )
921 {
922 mExteriorRing->transform( ct, d, transformZ );
923 }
924
925 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
926 {
927 curve->transform( ct, d, transformZ );
928 }
929 clearCache();
930}
931
932void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
933{
934 if ( mExteriorRing )
935 {
936 mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
937 }
938
939 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
940 {
941 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
942 }
943 clearCache();
944}
945
947{
948 QgsCoordinateSequence sequence;
949 sequence.append( QgsRingSequence() );
950
951 if ( mExteriorRing )
952 {
953 sequence.back().append( QgsPointSequence() );
954 mExteriorRing->points( sequence.back().back() );
955 }
956
957 for ( const QgsCurve *ring : mInteriorRings )
958 {
959 sequence.back().append( QgsPointSequence() );
960 ring->points( sequence.back().back() );
961 }
962
963 return sequence;
964}
965
967{
968 int count = 0;
969
970 if ( mExteriorRing )
971 {
972 count += mExteriorRing->nCoordinates();
973 }
974
975 for ( const QgsCurve *ring : mInteriorRings )
976 {
977 count += ring->nCoordinates();
978 }
979
980 return count;
981}
982
984{
985 if ( id.part != 0 )
986 return -1;
987
988 if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
989 return -1;
990
991 int number = 0;
992 if ( id.ring == 0 )
993 {
994 return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
995 }
996 else
997 {
998 number += mExteriorRing->numPoints();
999 }
1000
1001 for ( int i = 0; i < mInteriorRings.count(); ++i )
1002 {
1003 if ( id.ring == i + 1 )
1004 {
1005 int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1006 if ( partNumber == -1 )
1007 return -1;
1008 return number + partNumber;
1009 }
1010 else
1011 {
1012 number += mInteriorRings.at( i )->numPoints();
1013 }
1014 }
1015 return -1; // should not happen
1016}
1017
1019{
1020 if ( !mExteriorRing )
1021 return true;
1022
1023 return mExteriorRing->isEmpty();
1024}
1025
1026double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1027{
1028 if ( !mExteriorRing )
1029 {
1030 return -1;
1031 }
1032 QVector<QgsCurve *> segmentList;
1033 segmentList.append( mExteriorRing.get() );
1034 segmentList.append( mInteriorRings );
1035 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
1036}
1037
1039{
1040 if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
1041 {
1042 return false;
1043 }
1044
1045 if ( vId.ring < 0 )
1046 {
1047 vId.ring = 0;
1048 vId.vertex = -1;
1049 if ( vId.part < 0 )
1050 {
1051 vId.part = 0;
1052 }
1053 return mExteriorRing->nextVertex( vId, vertex );
1054 }
1055 else
1056 {
1057 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1058
1059 if ( ring->nextVertex( vId, vertex ) )
1060 {
1061 return true;
1062 }
1063 ++vId.ring;
1064 vId.vertex = -1;
1065 if ( vId.ring >= 1 + mInteriorRings.size() )
1066 {
1067 return false;
1068 }
1069 ring = mInteriorRings[ vId.ring - 1 ];
1070 return ring->nextVertex( vId, vertex );
1071 }
1072}
1073
1074void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1075{
1076 int n = curve->numPoints();
1077 if ( vertex.vertex < 0 || vertex.vertex >= n )
1078 {
1079 previousVertex = QgsVertexId();
1080 nextVertex = QgsVertexId();
1081 return;
1082 }
1083
1084 if ( vertex.vertex == 0 && n < 3 )
1085 {
1086 previousVertex = QgsVertexId();
1087 }
1088 else if ( vertex.vertex == 0 )
1089 {
1090 previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1091 }
1092 else
1093 {
1094 previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1095 }
1096 if ( vertex.vertex == n - 1 && n < 3 )
1097 {
1098 nextVertex = QgsVertexId();
1099 }
1100 else if ( vertex.vertex == n - 1 )
1101 {
1102 nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1103 }
1104 else
1105 {
1106 nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1107 }
1108}
1109
1110void QgsCurvePolygon::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
1111{
1112 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1113 {
1114 previousVertex = QgsVertexId();
1116 return;
1117 }
1118
1119 if ( vertex.ring == 0 )
1120 {
1121 ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1122 }
1123 else
1124 {
1125 ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1126 }
1127}
1128
1130{
1131 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1132 {
1133 return false;
1134 }
1135
1136 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1137 int n = ring->numPoints();
1138 bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1139 if ( !success )
1140 {
1141 return false;
1142 }
1143
1144 // If first or last vertex is inserted, re-sync the last/first vertex
1145 if ( vId.vertex == 0 )
1146 ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1147 else if ( vId.vertex == n )
1148 ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1149
1150 clearCache();
1151
1152 return true;
1153}
1154
1156{
1157 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1158 {
1159 return false;
1160 }
1161
1162 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1163 int n = ring->numPoints();
1164 bool success = ring->moveVertex( vId, newPos );
1165 if ( success )
1166 {
1167 // If first or last vertex is moved, also move the last/first vertex
1168 if ( vId.vertex == 0 )
1169 ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1170 else if ( vId.vertex == n - 1 )
1171 ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1172 clearCache();
1173 }
1174 return success;
1175}
1176
1178{
1179 const int interiorRingId = vId.ring - 1;
1180 if ( !mExteriorRing || vId.ring < 0 || interiorRingId >= mInteriorRings.size() )
1181 {
1182 return false;
1183 }
1184
1185 // cppcheck-suppress containerOutOfBounds
1186 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1187 int n = ring->numPoints();
1188 if ( n <= 4 )
1189 {
1190 //no points will be left in ring, so remove whole ring
1191 if ( vId.ring == 0 )
1192 {
1193 mExteriorRing.reset();
1194 if ( !mInteriorRings.isEmpty() )
1195 {
1196 mExteriorRing.reset( mInteriorRings.takeFirst() );
1197 }
1198 }
1199 else
1200 {
1201 removeInteriorRing( vId.ring - 1 );
1202 }
1203 clearCache();
1204 return true;
1205 }
1206
1207 bool success = ring->deleteVertex( vId );
1208 if ( success )
1209 {
1210 // If first or last vertex is removed, re-sync the last/first vertex
1211 // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1212 // may have been deleted (e.g. with CircularString)
1213 if ( vId.vertex == 0 )
1214 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1215 else if ( vId.vertex == n - 1 )
1216 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1217 clearCache();
1218 }
1219 return success;
1220}
1221
1223{
1224 if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1225 {
1226 return true;
1227 }
1228
1229 for ( const QgsCurve *ring : mInteriorRings )
1230 {
1231 if ( ring->hasCurvedSegments() )
1232 {
1233 return true;
1234 }
1235 }
1236 return false;
1237}
1238
1240{
1241 return toPolygon( tolerance, toleranceType );
1242}
1243
1245{
1246 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1247 {
1248 //makes no sense - conversion of false to double!
1249 return false;
1250 }
1251
1252 QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1253 return ring->vertexAngle( vertex );
1254}
1255
1256int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1257{
1258 return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1259}
1260
1262{
1263 return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1264}
1265
1267{
1268 return ringCount() > 0 ? 1 : 0;
1269}
1270
1272{
1273 return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1274}
1275
1277{
1278 if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1279 {
1280 return 0.0;
1281 }
1282
1283 const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1284 return ring->segmentLength( startVertex );
1285}
1286
1287bool QgsCurvePolygon::addZValue( double zValue )
1288{
1289 if ( QgsWkbTypes::hasZ( mWkbType ) )
1290 return false;
1291
1293
1294 if ( mExteriorRing )
1295 mExteriorRing->addZValue( zValue );
1296 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1297 {
1298 curve->addZValue( zValue );
1299 }
1300 clearCache();
1301 return true;
1302}
1303
1304bool QgsCurvePolygon::addMValue( double mValue )
1305{
1306 if ( QgsWkbTypes::hasM( mWkbType ) )
1307 return false;
1308
1310
1311 if ( mExteriorRing )
1312 mExteriorRing->addMValue( mValue );
1313 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1314 {
1315 curve->addMValue( mValue );
1316 }
1317 clearCache();
1318 return true;
1319}
1320
1322{
1323 if ( !is3D() )
1324 return false;
1325
1327 if ( mExteriorRing )
1328 mExteriorRing->dropZValue();
1329 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1330 {
1331 curve->dropZValue();
1332 }
1333 clearCache();
1334 return true;
1335}
1336
1338{
1339 if ( !isMeasure() )
1340 return false;
1341
1343 if ( mExteriorRing )
1344 mExteriorRing->dropMValue();
1345 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1346 {
1347 curve->dropMValue();
1348 }
1349 clearCache();
1350 return true;
1351}
1352
1354{
1355 if ( mExteriorRing )
1356 mExteriorRing->swapXy();
1357 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1358 {
1359 curve->swapXy();
1360 }
1361 clearCache();
1362}
1363
1365{
1366 return clone();
1367}
1368
1370{
1371 if ( !transformer )
1372 return false;
1373
1374 bool res = true;
1375 if ( mExteriorRing )
1376 res = mExteriorRing->transform( transformer, feedback );
1377
1378 if ( !res || ( feedback && feedback->isCanceled() ) )
1379 {
1380 clearCache();
1381 return false;
1382 }
1383
1384 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1385 {
1386 res = curve->transform( transformer );
1387
1388 if ( feedback && feedback->isCanceled() )
1389 res = false;
1390
1391 if ( !res )
1392 break;
1393 }
1394 clearCache();
1395 return res;
1396}
1397
1398void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1399{
1400 if ( mExteriorRing )
1401 mExteriorRing->filterVertices( filter );
1402
1403 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1404 {
1405 curve->filterVertices( filter );
1406 }
1407 clearCache();
1408}
1409
1410void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1411{
1412 if ( mExteriorRing )
1413 mExteriorRing->transformVertices( transform );
1414
1415 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1416 {
1417 curve->transformVertices( transform );
1418 }
1419 clearCache();
1420}
1421
1423{
1424 return 1 + mInteriorRings.count();
1425}
1426
1428{
1429 if ( index == 0 )
1430 return mExteriorRing.get();
1431 else
1432 return mInteriorRings.at( index - 1 );
1433}
1434
1436{
1437 const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1438 if ( !otherPolygon )
1439 return -1;
1440
1441 if ( mExteriorRing && !otherPolygon->mExteriorRing )
1442 return 1;
1443 else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1444 return -1;
1445 else if ( mExteriorRing && otherPolygon->mExteriorRing )
1446 {
1447 int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1448 if ( shellComp != 0 )
1449 {
1450 return shellComp;
1451 }
1452 }
1453
1454 const int nHole1 = mInteriorRings.size();
1455 const int nHole2 = otherPolygon->mInteriorRings.size();
1456 if ( nHole1 < nHole2 )
1457 {
1458 return -1;
1459 }
1460 if ( nHole1 > nHole2 )
1461 {
1462 return 1;
1463 }
1464
1465 for ( int i = 0; i < nHole1; i++ )
1466 {
1467 const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1468 if ( holeComp != 0 )
1469 {
1470 return holeComp;
1471 }
1472 }
1473
1474 return 0;
1475}
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
VertexType
Types of vertex.
Definition qgis.h:2619
@ Polygon
Polygons.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:201
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ Polygon
Polygon.
@ CircularString
CircularString.
@ CurvePolygon
CurvePolygon.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2289
An abstract base class for classes which transform geometries by transforming input points to output ...
Abstract base class for all geometries.
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.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
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:43
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp:132
bool isNull() const
Test if the box is null (holding no spatial information).
Definition qgsbox3d.cpp:289
Circular string geometry type.
Compound curve geometry type.
A const WKB pointer.
Definition qgswkbptr.h:138
Qgis::WkbType readHeader() const
readHeader
Definition qgswkbptr.cpp:55
Class for doing transforms between two map coordinate systems.
Curve polygon geometry type.
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.
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.
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)
QgsPolygon * surfaceToPolygon() const override
Gets a polygon representation of this surface.
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:35
virtual int numPoints() const =0
Returns the number of points in the curve.
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
Definition qgscurve.cpp:70
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition qgscurve.cpp:198
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:53
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
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition qgspoint.cpp:526
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
Definition qgspoint.cpp:443
Polygon geometry type.
Definition qgspolygon.h:33
Surface geometry type.
Definition qgssurface.h:34
QgsBox3D mBoundingBox
Definition qgssurface.h:80
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QString mValidityFailureReason
Definition qgssurface.h:82
bool mHasCachedValidity
Definition qgssurface.h:81
WKB pointer handler.
Definition qgswkbptr.h:44
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 bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType 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:5465
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
void ringAdjacentVertices(const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex)
int precision
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