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