QGIS API Documentation 3.99.0-Master (752b475928d)
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 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
658 if ( !mExteriorRing && mInteriorRings.empty() )
659 return false;
660
661 // if we already have the bounding box calculated, then this check is trivial!
662 if ( !mBoundingBox.isNull() )
663 {
664 return mBoundingBox.intersects( box3d );
665 }
666
667 // loop through each ring and test the bounding box intersection.
668 // This gives us a chance to use optimisations which may be present on the individual
669 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
670 // of each individual ring geometry which we would have to do anyway... (and these
671 // bounding boxes are cached, so would be reused without additional expense)
672 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box3d ) )
673 return true;
674
675 for ( const QgsCurve *ring : mInteriorRings )
676 {
677 if ( ring->boundingBoxIntersects( box3d ) )
678 return true;
679 }
680
681 // even if we don't intersect the bounding box of any rings, we may still intersect the
682 // bounding box of the overall polygon (we are considering worst case scenario here and
683 // the polygon is invalid, with rings outside the exterior ring!)
684 // so here we fall back to the non-optimised base class check which has to first calculate
685 // the overall bounding box of the polygon..
686 return QgsSurface::boundingBoxIntersects( box3d );
687
688 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
689}
690
692{
693 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
694
695 if ( !mExteriorRing && mInteriorRings.empty() )
696 return false;
697
698 // if we already have the bounding box calculated, then this check is trivial!
699 if ( !mBoundingBox.isNull() )
700 {
701 return mBoundingBox.toRectangle().intersects( box );
702 }
703
704 // loop through each ring and test the bounding box intersection.
705 // This gives us a chance to use optimisations which may be present on the individual
706 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
707 // of each individual ring geometry which we would have to do anyway... (and these
708 // bounding boxes are cached, so would be reused without additional expense)
709 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box ) )
710 return true;
711
712 for ( const QgsCurve *ring : mInteriorRings )
713 {
714 if ( ring->boundingBoxIntersects( box ) )
715 return true;
716 }
717
718 // even if we don't intersect the bounding box of any rings, we may still intersect the
719 // bounding box of the overall polygon (we are considering worst case scenario here and
720 // the polygon is invalid, with rings outside the exterior ring!)
721 // so here we fall back to the non-optimised base class check which has to first calculate
722 // the overall bounding box of the polygon..
724
725 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
726}
727
728QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
729{
730 auto poly = std::make_unique<QgsPolygon>();
731 if ( !mExteriorRing )
732 {
733 return poly.release();
734 }
735
736 poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
737
738 QVector<QgsCurve *> rings;
739 rings.reserve( mInteriorRings.size() );
740 for ( const QgsCurve *ring : mInteriorRings )
741 {
742 rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
743 }
744 poly->setInteriorRings( rings );
745 return poly.release();
746}
747
749{
750 if ( !ring )
751 {
752 return;
753 }
754 mExteriorRing.reset( ring );
755
756 //set proper wkb type
758 {
760 }
762 {
764 }
765
766 //match dimensionality for rings
767 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
768 {
769 if ( is3D() )
770 ring->addZValue();
771 else
772 ring->dropZValue();
773
774 if ( isMeasure() )
775 ring->addMValue();
776 else
777 ring->dropMValue();
778 }
779 clearCache();
780}
781
782void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
783{
784 qDeleteAll( mInteriorRings );
785 mInteriorRings.clear();
786
787 //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
788 for ( QgsCurve *ring : rings )
789 {
790 addInteriorRing( ring );
791 }
792 clearCache();
793}
794
796{
797 if ( !ring )
798 return;
799
800 //ensure dimensionality of ring matches curve polygon
801 if ( !is3D() )
802 ring->dropZValue();
803 else if ( !ring->is3D() )
804 ring->addZValue();
805
806 if ( !isMeasure() )
807 ring->dropMValue();
808 else if ( !ring->isMeasure() )
809 ring->addMValue();
810
811 mInteriorRings.append( ring );
812 clearCache();
813}
814
816{
817 if ( nr < 0 || nr >= mInteriorRings.size() )
818 {
819 return false;
820 }
821 delete mInteriorRings.takeAt( nr );
822 clearCache();
823 return true;
824}
825
826void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
827{
828 for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
829 {
830 if ( minimumAllowedArea < 0 )
831 delete mInteriorRings.takeAt( ringIndex );
832 else
833 {
834 double area = 0.0;
835 mInteriorRings.at( ringIndex )->sumUpArea( area );
836 if ( std::fabs( area ) < minimumAllowedArea )
837 delete mInteriorRings.takeAt( ringIndex );
838 }
839 }
840
841 clearCache();
842}
843
845{
846 QVector<QgsCurve *> validRings;
847 validRings.reserve( mInteriorRings.size() );
848 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
849 {
850 if ( !curve->isRing() )
851 {
852 // remove invalid rings
853 delete curve;
854 }
855 else
856 {
857 validRings << curve;
858 }
859 }
860 mInteriorRings = validRings;
861}
862
867
869{
871 {
872 // flip exterior ring orientation
873 std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
874 mExteriorRing = std::move( flipped );
875 }
876
877 QVector<QgsCurve *> validRings;
878 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
879 {
880 if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
881 {
882 // flip interior ring orientation
883 QgsCurve *flipped = curve->reversed();
884 validRings << flipped;
885 delete curve;
886 }
887 else
888 {
889 validRings << curve;
890 }
891 }
892 mInteriorRings = validRings;
893}
894
896{
898 {
899 // flip exterior ring orientation
900 mExteriorRing.reset( mExteriorRing->reversed() );
901 }
902
903 QVector<QgsCurve *> validRings;
904 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
905 {
906 if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
907 {
908 // flip interior ring orientation
909 QgsCurve *flipped = curve->reversed();
910 validRings << flipped;
911 delete curve;
912 }
913 else
914 {
915 validRings << curve;
916 }
917 }
918 mInteriorRings = validRings;
919}
920
922{
923 QPainterPath p;
924 if ( mExteriorRing )
925 {
926 QPainterPath ring = mExteriorRing->asQPainterPath();
927 ring.closeSubpath();
928 p.addPath( ring );
929 }
930
931 for ( const QgsCurve *ring : mInteriorRings )
932 {
933 QPainterPath ringPath = ring->asQPainterPath();
934 ringPath.closeSubpath();
935 p.addPath( ringPath );
936 }
937
938 return p;
939}
940
941void QgsCurvePolygon::draw( QPainter &p ) const
942{
943 if ( !mExteriorRing )
944 return;
945
946 if ( mInteriorRings.empty() )
947 {
948 mExteriorRing->drawAsPolygon( p );
949 }
950 else
951 {
952 QPainterPath path;
953 mExteriorRing->addToPainterPath( path );
954
955 for ( const QgsCurve *ring : mInteriorRings )
956 {
957 ring->addToPainterPath( path );
958 }
959 p.drawPath( path );
960 }
961}
962
964{
965 if ( mExteriorRing )
966 {
967 mExteriorRing->transform( ct, d, transformZ );
968 }
969
970 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
971 {
972 curve->transform( ct, d, transformZ );
973 }
974 clearCache();
975}
976
977void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
978{
979 if ( mExteriorRing )
980 {
981 mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
982 }
983
984 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
985 {
986 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
987 }
988 clearCache();
989}
990
992{
993 QgsCoordinateSequence sequence;
994 sequence.append( QgsRingSequence() );
995
996 if ( mExteriorRing )
997 {
998 sequence.back().append( QgsPointSequence() );
999 mExteriorRing->points( sequence.back().back() );
1000 }
1001
1002 for ( const QgsCurve *ring : mInteriorRings )
1003 {
1004 sequence.back().append( QgsPointSequence() );
1005 ring->points( sequence.back().back() );
1006 }
1007
1008 return sequence;
1009}
1010
1012{
1013 int count = 0;
1014
1015 if ( mExteriorRing )
1016 {
1017 count += mExteriorRing->nCoordinates();
1018 }
1019
1020 for ( const QgsCurve *ring : mInteriorRings )
1021 {
1022 count += ring->nCoordinates();
1023 }
1024
1025 return count;
1026}
1027
1029{
1030 if ( id.part != 0 )
1031 return -1;
1032
1033 if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
1034 return -1;
1035
1036 int number = 0;
1037 if ( id.ring == 0 )
1038 {
1039 return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1040 }
1041 else
1042 {
1043 number += mExteriorRing->numPoints();
1044 }
1045
1046 for ( int i = 0; i < mInteriorRings.count(); ++i )
1047 {
1048 if ( id.ring == i + 1 )
1049 {
1050 int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1051 if ( partNumber == -1 )
1052 return -1;
1053 return number + partNumber;
1054 }
1055 else
1056 {
1057 number += mInteriorRings.at( i )->numPoints();
1058 }
1059 }
1060 return -1; // should not happen
1061}
1062
1064{
1065 if ( !mExteriorRing )
1066 return true;
1067
1068 return mExteriorRing->isEmpty();
1069}
1070
1071double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1072{
1073 if ( !mExteriorRing )
1074 {
1075 return -1;
1076 }
1077 QVector<QgsCurve *> segmentList;
1078 segmentList.append( mExteriorRing.get() );
1079 segmentList.append( mInteriorRings );
1080 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
1081}
1082
1084{
1085 if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
1086 {
1087 return false;
1088 }
1089
1090 if ( vId.ring < 0 )
1091 {
1092 vId.ring = 0;
1093 vId.vertex = -1;
1094 if ( vId.part < 0 )
1095 {
1096 vId.part = 0;
1097 }
1098 return mExteriorRing->nextVertex( vId, vertex );
1099 }
1100 else
1101 {
1102 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1103
1104 if ( ring->nextVertex( vId, vertex ) )
1105 {
1106 return true;
1107 }
1108 ++vId.ring;
1109 vId.vertex = -1;
1110 if ( vId.ring >= 1 + mInteriorRings.size() )
1111 {
1112 return false;
1113 }
1114 ring = mInteriorRings[ vId.ring - 1 ];
1115 return ring->nextVertex( vId, vertex );
1116 }
1117}
1118
1119void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1120{
1121 int n = curve->numPoints();
1122 if ( vertex.vertex < 0 || vertex.vertex >= n )
1123 {
1124 previousVertex = QgsVertexId();
1125 nextVertex = QgsVertexId();
1126 return;
1127 }
1128
1129 if ( vertex.vertex == 0 && n < 3 )
1130 {
1131 previousVertex = QgsVertexId();
1132 }
1133 else if ( vertex.vertex == 0 )
1134 {
1135 previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1136 }
1137 else
1138 {
1139 previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1140 }
1141 if ( vertex.vertex == n - 1 && n < 3 )
1142 {
1143 nextVertex = QgsVertexId();
1144 }
1145 else if ( vertex.vertex == n - 1 )
1146 {
1147 nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1148 }
1149 else
1150 {
1151 nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1152 }
1153}
1154
1156{
1157 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1158 {
1159 previousVertex = QgsVertexId();
1161 return;
1162 }
1163
1164 if ( vertex.ring == 0 )
1165 {
1166 ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1167 }
1168 else
1169 {
1170 ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1171 }
1172}
1173
1175{
1176 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1177 {
1178 return false;
1179 }
1180
1181 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1182 int n = ring->numPoints();
1183 bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1184 if ( !success )
1185 {
1186 return false;
1187 }
1188
1189 // If first or last vertex is inserted, re-sync the last/first vertex
1190 if ( vId.vertex == 0 )
1191 ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1192 else if ( vId.vertex == n )
1193 ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1194
1195 clearCache();
1196
1197 return true;
1198}
1199
1201{
1202 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1203 {
1204 return false;
1205 }
1206
1207 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1208 int n = ring->numPoints();
1209 bool success = ring->moveVertex( vId, newPos );
1210 if ( success )
1211 {
1212 // If first or last vertex is moved, also move the last/first vertex
1213 if ( vId.vertex == 0 )
1214 ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1215 else if ( vId.vertex == n - 1 )
1216 ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1217 clearCache();
1218 }
1219 return success;
1220}
1221
1223{
1224 const int interiorRingId = vId.ring - 1;
1225 if ( !mExteriorRing || vId.ring < 0 || interiorRingId >= mInteriorRings.size() )
1226 {
1227 return false;
1228 }
1229
1230 // cppcheck-suppress containerOutOfBounds
1231 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1232 int n = ring->numPoints();
1233 if ( n <= 4 )
1234 {
1235 //no points will be left in ring, so remove whole ring
1236 if ( vId.ring == 0 )
1237 {
1238 mExteriorRing.reset();
1239 if ( !mInteriorRings.isEmpty() )
1240 {
1241 mExteriorRing.reset( mInteriorRings.takeFirst() );
1242 }
1243 }
1244 else
1245 {
1246 removeInteriorRing( vId.ring - 1 );
1247 }
1248 clearCache();
1249 return true;
1250 }
1251
1252 bool success = ring->deleteVertex( vId );
1253 if ( success )
1254 {
1255 // If first or last vertex is removed, re-sync the last/first vertex
1256 // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1257 // may have been deleted (e.g. with CircularString)
1258 if ( vId.vertex == 0 )
1259 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1260 else if ( vId.vertex == n - 1 )
1261 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1262 clearCache();
1263 }
1264 return success;
1265}
1266
1268{
1269 if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1270 {
1271 return true;
1272 }
1273
1274 for ( const QgsCurve *ring : mInteriorRings )
1275 {
1276 if ( ring->hasCurvedSegments() )
1277 {
1278 return true;
1279 }
1280 }
1281 return false;
1282}
1283
1285{
1286 return toPolygon( tolerance, toleranceType );
1287}
1288
1290{
1291 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1292 {
1293 //makes no sense - conversion of false to double!
1294 return false;
1295 }
1296
1297 QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1298 return ring->vertexAngle( vertex );
1299}
1300
1301int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1302{
1303 return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1304}
1305
1307{
1308 return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1309}
1310
1312{
1313 return ringCount() > 0 ? 1 : 0;
1314}
1315
1317{
1318 return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1319}
1320
1322{
1323 if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1324 {
1325 return 0.0;
1326 }
1327
1328 const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1329 return ring->segmentLength( startVertex );
1330}
1331
1332bool QgsCurvePolygon::addZValue( double zValue )
1333{
1334 if ( QgsWkbTypes::hasZ( mWkbType ) )
1335 return false;
1336
1338
1339 if ( mExteriorRing )
1340 mExteriorRing->addZValue( zValue );
1341 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1342 {
1343 curve->addZValue( zValue );
1344 }
1345 clearCache();
1346 return true;
1347}
1348
1349bool QgsCurvePolygon::addMValue( double mValue )
1350{
1351 if ( QgsWkbTypes::hasM( mWkbType ) )
1352 return false;
1353
1355
1356 if ( mExteriorRing )
1357 mExteriorRing->addMValue( mValue );
1358 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1359 {
1360 curve->addMValue( mValue );
1361 }
1362 clearCache();
1363 return true;
1364}
1365
1367{
1368 if ( !is3D() )
1369 return false;
1370
1372 if ( mExteriorRing )
1373 mExteriorRing->dropZValue();
1374 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1375 {
1376 curve->dropZValue();
1377 }
1378 clearCache();
1379 return true;
1380}
1381
1383{
1384 if ( !isMeasure() )
1385 return false;
1386
1388 if ( mExteriorRing )
1389 mExteriorRing->dropMValue();
1390 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1391 {
1392 curve->dropMValue();
1393 }
1394 clearCache();
1395 return true;
1396}
1397
1399{
1400 if ( mExteriorRing )
1401 mExteriorRing->swapXy();
1402 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1403 {
1404 curve->swapXy();
1405 }
1406 clearCache();
1407}
1408
1410{
1411 return clone();
1412}
1413
1415{
1416 if ( !transformer )
1417 return false;
1418
1419 bool res = true;
1420 if ( mExteriorRing )
1421 res = mExteriorRing->transform( transformer, feedback );
1422
1423 if ( !res || ( feedback && feedback->isCanceled() ) )
1424 {
1425 clearCache();
1426 return false;
1427 }
1428
1429 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1430 {
1431 res = curve->transform( transformer );
1432
1433 if ( feedback && feedback->isCanceled() )
1434 res = false;
1435
1436 if ( !res )
1437 break;
1438 }
1439 clearCache();
1440 return res;
1441}
1442
1443void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1444{
1445 if ( mExteriorRing )
1446 mExteriorRing->filterVertices( filter );
1447
1448 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1449 {
1450 curve->filterVertices( filter );
1451 }
1452 clearCache();
1453}
1454
1455void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1456{
1457 if ( mExteriorRing )
1458 mExteriorRing->transformVertices( transform );
1459
1460 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1461 {
1462 curve->transformVertices( transform );
1463 }
1464 clearCache();
1465}
1466
1468{
1469 return 1 + mInteriorRings.count();
1470}
1471
1473{
1474 if ( index == 0 )
1475 return mExteriorRing.get();
1476 else
1477 return mInteriorRings.at( index - 1 );
1478}
1479
1481{
1482 const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1483 if ( !otherPolygon )
1484 return -1;
1485
1486 if ( mExteriorRing && !otherPolygon->mExteriorRing )
1487 return 1;
1488 else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1489 return -1;
1490 else if ( mExteriorRing && otherPolygon->mExteriorRing )
1491 {
1492 int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1493 if ( shellComp != 0 )
1494 {
1495 return shellComp;
1496 }
1497 }
1498
1499 const int nHole1 = mInteriorRings.size();
1500 const int nHole2 = otherPolygon->mInteriorRings.size();
1501 if ( nHole1 < nHole2 )
1502 {
1503 return -1;
1504 }
1505 if ( nHole1 > nHole2 )
1506 {
1507 return 1;
1508 }
1509
1510 for ( int i = 0; i < nHole1; i++ )
1511 {
1512 const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1513 if ( holeComp != 0 )
1514 {
1515 return holeComp;
1516 }
1517 }
1518
1519 return 0;
1520}
@ 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
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: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