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