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