QGIS API Documentation 4.1.0-Master (60fea48833c)
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 ) || secondWithoutParentheses.isEmpty() )
189 return true;
190
191 QString defaultChildWkbType = u"LineString%1%2"_s.arg( is3D() ? u"Z"_s : QString(), isMeasure() ? u"M"_s : QString() );
192
193 const QStringList blocks = QgsGeometryUtils::wktGetChildBlocks( parts.second, defaultChildWkbType );
194 for ( const QString &childWkt : blocks )
195 {
196 QPair<Qgis::WkbType, QString> childParts = QgsGeometryUtils::wktReadBlock( childWkt );
197
198 Qgis::WkbType flatCurveType = QgsWkbTypes::flatType( childParts.first );
199 if ( flatCurveType == Qgis::WkbType::LineString )
200 mInteriorRings.append( new QgsLineString() );
201 else if ( flatCurveType == Qgis::WkbType::CircularString )
202 mInteriorRings.append( new QgsCircularString() );
203 else if ( flatCurveType == Qgis::WkbType::CompoundCurve )
204 mInteriorRings.append( new QgsCompoundCurve() );
205 else
206 {
207 clear();
208 return false;
209 }
210 if ( !mInteriorRings.back()->fromWkt( childWkt ) )
211 {
212 clear();
213 return false;
214 }
215 }
216
217 if ( mInteriorRings.isEmpty() )
218 {
219 clear();
220 return false;
221 }
222
223 mExteriorRing.reset( mInteriorRings.takeFirst() );
224
225 //scan through rings and check if dimensionality of rings is different to CurvePolygon.
226 //if so, update the type dimensionality of the CurvePolygon to match
227 bool hasZ = false;
228 bool hasM = false;
229 if ( mExteriorRing )
230 {
231 hasZ = hasZ || mExteriorRing->is3D();
232 hasM = hasM || mExteriorRing->isMeasure();
233 }
234 for ( const QgsCurve *curve : std::as_const( mInteriorRings ) )
235 {
236 hasZ = hasZ || curve->is3D();
237 hasM = hasM || curve->isMeasure();
238 if ( hasZ && hasM )
239 break;
240 }
241 if ( hasZ )
242 addZValue( 0 );
243 if ( hasM )
244 addMValue( 0 );
245
246 return true;
247}
248
250{
251 if ( mExteriorRing )
252 {
253 return mExteriorRing->boundingBox3D();
254 }
255 return QgsBox3D();
256}
257
259{
260 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
261 if ( mExteriorRing )
262 {
263 binarySize += mExteriorRing->wkbSize( flags );
264 }
265 for ( const QgsCurve *curve : mInteriorRings )
266 {
267 binarySize += curve->wkbSize( flags );
268 }
269 return binarySize;
270}
271
272QByteArray QgsCurvePolygon::asWkb( WkbFlags flags ) const
273{
274 QByteArray wkbArray;
275 wkbArray.resize( QgsCurvePolygon::wkbSize( flags ) );
276 QgsWkbPtr wkbPtr( wkbArray );
277 wkbPtr << static_cast<char>( QgsApplication::endian() );
278 wkbPtr << static_cast<quint32>( wkbType() );
279 wkbPtr << static_cast<quint32>( ( mExteriorRing ? 1 : 0 ) + mInteriorRings.size() );
280 if ( mExteriorRing )
281 {
282 wkbPtr << mExteriorRing->asWkb( flags );
283 }
284 for ( const QgsCurve *curve : mInteriorRings )
285 {
286 wkbPtr << curve->asWkb( flags );
287 }
288 return wkbArray;
289}
290
291QString QgsCurvePolygon::asWkt( int precision ) const
292{
293 QString wkt = wktTypeStr();
294
295 if ( isEmpty() )
296 wkt += " EMPTY"_L1;
297 else
298 {
299 wkt += " ("_L1;
300 if ( mExteriorRing )
301 {
302 QString childWkt = mExteriorRing->asWkt( precision );
304 {
305 // Type names of linear geometries are omitted
306 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
307 }
308 wkt += childWkt + ',';
309 }
310 for ( const QgsCurve *curve : mInteriorRings )
311 {
312 if ( !curve->isEmpty() )
313 {
314 QString childWkt = curve->asWkt( precision );
316 {
317 // Type names of linear geometries are omitted
318 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
319 }
320 wkt += childWkt + ',';
321 }
322 }
323 if ( wkt.endsWith( ',' ) )
324 {
325 wkt.chop( 1 ); // Remove last ','
326 }
327 wkt += ')';
328 }
329 return wkt;
330}
331
332QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
333{
334 // GML2 does not support curves
335 QDomElement elemPolygon = doc.createElementNS( ns, u"Polygon"_s );
336
337 if ( isEmpty() )
338 return elemPolygon;
339
340 QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, u"outerBoundaryIs"_s );
341 std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
342 QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
343 outerRing.toElement().setTagName( u"LinearRing"_s );
344 elemOuterBoundaryIs.appendChild( outerRing );
345 elemPolygon.appendChild( elemOuterBoundaryIs );
346 std::unique_ptr< QgsLineString > interiorLineString;
347 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
348 {
349 QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, u"innerBoundaryIs"_s );
350 interiorLineString.reset( interiorRing( i )->curveToLine() );
351 QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
352 innerRing.toElement().setTagName( u"LinearRing"_s );
353 elemInnerBoundaryIs.appendChild( innerRing );
354 elemPolygon.appendChild( elemInnerBoundaryIs );
355 }
356 return elemPolygon;
357}
358
359QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
360{
361 QDomElement elemCurvePolygon = doc.createElementNS( ns, u"Polygon"_s );
362
363 if ( isEmpty() )
364 return elemCurvePolygon;
365
366 const auto exportRing = [&doc, precision, &ns, axisOrder]( const QgsCurve *ring ) {
367 QDomElement ringElem = ring->asGml3( doc, precision, ns, axisOrder );
368 if ( ringElem.tagName() == "LineString"_L1 )
369 {
370 ringElem.setTagName( u"LinearRing"_s );
371 }
372 else if ( ringElem.tagName() == "CompositeCurve"_L1 )
373 {
374 ringElem.setTagName( u"Ring"_s );
375 }
376 else if ( ringElem.tagName() == "Curve"_L1 )
377 {
378 QDomElement ringElemNew = doc.createElementNS( ns, u"Ring"_s );
379 QDomElement curveMemberElem = doc.createElementNS( ns, u"curveMember"_s );
380 ringElemNew.appendChild( curveMemberElem );
381 curveMemberElem.appendChild( ringElem );
382 ringElem = std::move( ringElemNew );
383 }
384 return ringElem;
385 };
386
387 QDomElement elemExterior = doc.createElementNS( ns, u"exterior"_s );
388 elemExterior.appendChild( exportRing( exteriorRing() ) );
389 elemCurvePolygon.appendChild( elemExterior );
390
391 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
392 {
393 QDomElement elemInterior = doc.createElementNS( ns, u"interior"_s );
394 elemInterior.appendChild( exportRing( interiorRing( i ) ) );
395 elemCurvePolygon.appendChild( elemInterior );
396 }
397 return elemCurvePolygon;
398}
399
400json QgsCurvePolygon::asJsonObject( int precision ) const
401{
402 json coordinates( json::array() );
403 if ( auto *lExteriorRing = exteriorRing() )
404 {
405 std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
406 QgsPointSequence exteriorPts;
407 exteriorLineString->points( exteriorPts );
408 coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
409
410 std::unique_ptr< QgsLineString > interiorLineString;
411 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
412 {
413 interiorLineString.reset( interiorRing( i )->curveToLine() );
414 QgsPointSequence interiorPts;
415 interiorLineString->points( interiorPts );
416 coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
417 }
418 }
419 return { { "type", "Polygon" }, { "coordinates", coordinates } };
420}
421
422QString QgsCurvePolygon::asKml( int precision ) const
423{
424 QString kml;
425 kml.append( "<Polygon>"_L1 );
426 if ( mExteriorRing )
427 {
428 kml.append( "<outerBoundaryIs>"_L1 );
429 kml.append( mExteriorRing->asKml( precision ) );
430 kml.append( "</outerBoundaryIs>"_L1 );
431 }
432 const QVector<QgsCurve *> &interiorRings = mInteriorRings;
433 for ( const QgsCurve *ring : interiorRings )
434 {
435 kml.append( "<innerBoundaryIs>"_L1 );
436 kml.append( ring->asKml( precision ) );
437 kml.append( "</innerBoundaryIs>"_L1 );
438 }
439 kml.append( "</Polygon>"_L1 );
440 return kml;
441}
442
444{
445 // normalize rings
446 if ( mExteriorRing )
447 mExteriorRing->normalize();
448
449 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
450 {
451 ring->normalize();
452 }
453
454 // sort rings
455 std::sort( mInteriorRings.begin(), mInteriorRings.end(), []( const QgsCurve *a, const QgsCurve *b ) { return a->compareTo( b ) > 0; } );
456
457 // normalize ring orientation
458 forceRHR();
459}
460
462{
463 if ( !mExteriorRing )
464 {
465 return 0.0;
466 }
467
468 double totalArea = 0.0;
469
470 if ( mExteriorRing->isRing() )
471 {
472 double area = 0.0;
473 mExteriorRing->sumUpArea( area );
474 totalArea += std::fabs( area );
475 }
476
477 for ( const QgsCurve *ring : mInteriorRings )
478 {
479 double area = 0.0;
480 if ( ring->isRing() )
481 {
482 ring->sumUpArea( area );
483 totalArea -= std::fabs( area );
484 }
485 }
486 return totalArea;
487}
488
490{
491 if ( !mExteriorRing )
492 {
493 return 0.0;
494 }
495
496 double totalArea3D = 0.0;
497
498 if ( mExteriorRing->isRing() )
499 {
500 double area3D = 0.0;
501 mExteriorRing->sumUpArea3D( area3D );
502 totalArea3D += std::abs( area3D );
503 }
504
505 for ( const QgsCurve *ring : mInteriorRings )
506 {
507 double area3D = 0.0;
508 if ( ring->isRing() )
509 {
510 ring->sumUpArea3D( area3D );
511 totalArea3D -= std::abs( area3D );
512 }
513 }
514
515 return totalArea3D;
516}
517
519{
520 if ( !mExteriorRing )
521 return 0.0;
522
523 //sum perimeter of rings
524 double perimeter = mExteriorRing->length();
525 for ( const QgsCurve *ring : mInteriorRings )
526 {
527 perimeter += ring->length();
528 }
529 return perimeter;
530}
531
533{
534 const double p = perimeter();
535 if ( qgsDoubleNear( p, 0.0 ) )
536 return 0.0;
537
538 return 4.0 * M_PI * area() / pow( p, 2.0 );
539}
540
542{
543 auto polygon = std::make_unique<QgsPolygon>();
544 if ( !mExteriorRing )
545 return polygon.release();
546
547 polygon->setExteriorRing( exteriorRing()->curveToLine() );
548 QVector<QgsCurve *> interiors;
549 int n = numInteriorRings();
550 interiors.reserve( n );
551 for ( int i = 0; i < n; ++i )
552 {
553 interiors.append( interiorRing( i )->curveToLine() );
554 }
555 polygon->setInteriorRings( interiors );
556 return polygon.release();
557}
558
560{
561 if ( !mExteriorRing )
562 return nullptr;
563
564 if ( mInteriorRings.isEmpty() )
565 {
566 return mExteriorRing->clone();
567 }
568 else
569 {
570 QgsMultiCurve *multiCurve = new QgsMultiCurve();
571 int nInteriorRings = mInteriorRings.size();
572 multiCurve->reserve( nInteriorRings + 1 );
573 multiCurve->addGeometry( mExteriorRing->clone() );
574 for ( int i = 0; i < nInteriorRings; ++i )
575 {
576 multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
577 }
578 return multiCurve;
579 }
580}
581
582QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
583{
584 if ( !mExteriorRing )
585 return nullptr;
586
587
588 std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
589
590 // exterior ring
591 auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) };
592
593 if ( !exterior )
594 return nullptr;
595
596 polygon->mExteriorRing = std::move( exterior );
597
598 //interior rings
599 for ( auto interior : mInteriorRings )
600 {
601 if ( !interior )
602 continue;
603
604 QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) );
605
606 if ( !gridifiedInterior )
607 continue;
608
609 polygon->mInteriorRings.append( gridifiedInterior );
610 }
611
612 return polygon.release();
613}
614
616{
617 if ( !mExteriorRing )
618 return nullptr;
619
620 // exterior ring
621 std::unique_ptr< QgsAbstractGeometry > exterior( mExteriorRing->simplifyByDistance( tolerance ) );
622 if ( !qgsgeometry_cast< QgsLineString * >( exterior.get() ) )
623 return nullptr;
624
625 auto polygon = std::make_unique< QgsPolygon >( qgis::down_cast< QgsLineString * >( exterior.release() ) );
626
627 //interior rings
628 for ( const QgsCurve *interior : mInteriorRings )
629 {
630 if ( !interior )
631 continue;
632
633 std::unique_ptr< QgsAbstractGeometry > simplifiedRing( interior->simplifyByDistance( tolerance ) );
634 if ( !simplifiedRing )
635 return nullptr;
636
637 if ( !qgsgeometry_cast< QgsLineString * >( simplifiedRing.get() ) )
638 return nullptr;
639
640 polygon->mInteriorRings.append( qgis::down_cast< QgsLineString * >( simplifiedRing.release() ) );
641 }
642
643 return polygon.release();
644}
645
646bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
647{
648 bool result = false;
649 auto cleanRing = [epsilon, useZValues]( QgsCurve *ring ) -> bool {
650 if ( ring->numPoints() <= 4 )
651 return false;
652
653 if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
654 {
655 QgsPoint startPoint;
656 Qgis::VertexType type;
657 ring->pointAt( 0, startPoint, type );
658 // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
659 ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
660 return true;
661 }
662
663 return false;
664 };
665 if ( mExteriorRing )
666 {
667 result = cleanRing( mExteriorRing.get() );
668 }
669 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
670 {
671 if ( cleanRing( ring ) )
672 result = true;
673 }
674 return result;
675}
676
678{
679 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
680 if ( !mExteriorRing && mInteriorRings.empty() )
681 return false;
682
683 // if we already have the bounding box calculated, then this check is trivial!
684 if ( !mBoundingBox.isNull() )
685 {
686 return mBoundingBox.intersects( box3d );
687 }
688
689 // loop through each ring and test the bounding box intersection.
690 // This gives us a chance to use optimisations which may be present on the individual
691 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
692 // of each individual ring geometry which we would have to do anyway... (and these
693 // bounding boxes are cached, so would be reused without additional expense)
694 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box3d ) )
695 return true;
696
697 for ( const QgsCurve *ring : mInteriorRings )
698 {
699 if ( ring->boundingBoxIntersects( box3d ) )
700 return true;
701 }
702
703 // even if we don't intersect the bounding box of any rings, we may still intersect the
704 // bounding box of the overall polygon (we are considering worst case scenario here and
705 // the polygon is invalid, with rings outside the exterior ring!)
706 // so here we fall back to the non-optimised base class check which has to first calculate
707 // the overall bounding box of the polygon..
708 return QgsSurface::boundingBoxIntersects( box3d );
709
710 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsRectangle& box), update that if you change this!
711}
712
714{
715 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
716
717 if ( !mExteriorRing && mInteriorRings.empty() )
718 return false;
719
720 // if we already have the bounding box calculated, then this check is trivial!
721 if ( !mBoundingBox.isNull() )
722 {
723 return mBoundingBox.toRectangle().intersects( box );
724 }
725
726 // loop through each ring and test the bounding box intersection.
727 // This gives us a chance to use optimisations which may be present on the individual
728 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
729 // of each individual ring geometry which we would have to do anyway... (and these
730 // bounding boxes are cached, so would be reused without additional expense)
731 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box ) )
732 return true;
733
734 for ( const QgsCurve *ring : mInteriorRings )
735 {
736 if ( ring->boundingBoxIntersects( box ) )
737 return true;
738 }
739
740 // even if we don't intersect the bounding box of any rings, we may still intersect the
741 // bounding box of the overall polygon (we are considering worst case scenario here and
742 // the polygon is invalid, with rings outside the exterior ring!)
743 // so here we fall back to the non-optimised base class check which has to first calculate
744 // the overall bounding box of the polygon..
746
747 // SIMILAR LOGIC IN boundingBoxIntersects(const QgsBox3D &box3d), update that if you change this!
748}
749
750QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
751{
752 auto poly = std::make_unique<QgsPolygon>();
753 if ( !mExteriorRing )
754 {
755 return poly.release();
756 }
757
758 poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
759
760 QVector<QgsCurve *> rings;
761 rings.reserve( mInteriorRings.size() );
762 for ( const QgsCurve *ring : mInteriorRings )
763 {
764 rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
765 }
766 poly->setInteriorRings( rings );
767 return poly.release();
768}
769
771{
772 if ( !ring )
773 {
774 return;
775 }
776 mExteriorRing.reset( ring );
777
778 //set proper wkb type
780 {
782 }
784 {
786 }
787
788 //match dimensionality for rings
789 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
790 {
791 if ( is3D() )
792 ring->addZValue();
793 else
794 ring->dropZValue();
795
796 if ( isMeasure() )
797 ring->addMValue();
798 else
799 ring->dropMValue();
800 }
801 clearCache();
802}
803
804void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
805{
806 qDeleteAll( mInteriorRings );
807 mInteriorRings.clear();
808
809 //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
810 for ( QgsCurve *ring : rings )
811 {
812 addInteriorRing( ring );
813 }
814 clearCache();
815}
816
818{
819 if ( !ring )
820 return;
821
822 //ensure dimensionality of ring matches curve polygon
823 if ( !is3D() )
824 ring->dropZValue();
825 else if ( !ring->is3D() )
826 ring->addZValue();
827
828 if ( !isMeasure() )
829 ring->dropMValue();
830 else if ( !ring->isMeasure() )
831 ring->addMValue();
832
833 mInteriorRings.append( ring );
834 clearCache();
835}
836
838{
839 if ( nr < 0 || nr >= mInteriorRings.size() )
840 {
841 return false;
842 }
843 delete mInteriorRings.takeAt( nr );
844 clearCache();
845 return true;
846}
847
848void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
849{
850 for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
851 {
852 if ( minimumAllowedArea < 0 )
853 delete mInteriorRings.takeAt( ringIndex );
854 else
855 {
856 double area = 0.0;
857 mInteriorRings.at( ringIndex )->sumUpArea( area );
858 if ( std::fabs( area ) < minimumAllowedArea )
859 delete mInteriorRings.takeAt( ringIndex );
860 }
861 }
862
863 clearCache();
864}
865
867{
868 QVector<QgsCurve *> validRings;
869 validRings.reserve( mInteriorRings.size() );
870 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
871 {
872 if ( !curve->isRing() )
873 {
874 // remove invalid rings
875 delete curve;
876 }
877 else
878 {
879 validRings << curve;
880 }
881 }
882 mInteriorRings = validRings;
883}
884
889
891{
893 {
894 // flip exterior ring orientation
895 std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
896 mExteriorRing = std::move( flipped );
897 }
898
899 QVector<QgsCurve *> validRings;
900 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
901 {
902 if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
903 {
904 // flip interior ring orientation
905 QgsCurve *flipped = curve->reversed();
906 validRings << flipped;
907 delete curve;
908 }
909 else
910 {
911 validRings << curve;
912 }
913 }
914 mInteriorRings = validRings;
915}
916
918{
920 {
921 // flip exterior ring orientation
922 mExteriorRing.reset( mExteriorRing->reversed() );
923 }
924
925 QVector<QgsCurve *> validRings;
926 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
927 {
928 if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
929 {
930 // flip interior ring orientation
931 QgsCurve *flipped = curve->reversed();
932 validRings << flipped;
933 delete curve;
934 }
935 else
936 {
937 validRings << curve;
938 }
939 }
940 mInteriorRings = validRings;
941}
942
944{
945 QPainterPath p;
946 if ( mExteriorRing )
947 {
948 QPainterPath ring = mExteriorRing->asQPainterPath();
949 ring.closeSubpath();
950 p.addPath( ring );
951 }
952
953 for ( const QgsCurve *ring : mInteriorRings )
954 {
955 QPainterPath ringPath = ring->asQPainterPath();
956 ringPath.closeSubpath();
957 p.addPath( ringPath );
958 }
959
960 return p;
961}
962
963void QgsCurvePolygon::draw( QPainter &p ) const
964{
965 if ( !mExteriorRing )
966 return;
967
968 if ( mInteriorRings.empty() )
969 {
970 mExteriorRing->drawAsPolygon( p );
971 }
972 else
973 {
974 QPainterPath path;
975 mExteriorRing->addToPainterPath( path );
976
977 for ( const QgsCurve *ring : mInteriorRings )
978 {
979 ring->addToPainterPath( path );
980 }
981 p.drawPath( path );
982 }
983}
984
986{
987 if ( mExteriorRing )
988 {
989 mExteriorRing->transform( ct, d, transformZ );
990 }
991
992 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
993 {
994 curve->transform( ct, d, transformZ );
995 }
996 clearCache();
997}
998
999void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
1000{
1001 if ( mExteriorRing )
1002 {
1003 mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
1004 }
1005
1006 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1007 {
1008 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
1009 }
1010 clearCache();
1011}
1012
1014{
1015 QgsCoordinateSequence sequence;
1016 sequence.append( QgsRingSequence() );
1017
1018 if ( mExteriorRing )
1019 {
1020 sequence.back().append( QgsPointSequence() );
1021 mExteriorRing->points( sequence.back().back() );
1022 }
1023
1024 for ( const QgsCurve *ring : mInteriorRings )
1025 {
1026 sequence.back().append( QgsPointSequence() );
1027 ring->points( sequence.back().back() );
1028 }
1029
1030 return sequence;
1031}
1032
1034{
1035 int count = 0;
1036
1037 if ( mExteriorRing )
1038 {
1039 count += mExteriorRing->nCoordinates();
1040 }
1041
1042 for ( const QgsCurve *ring : mInteriorRings )
1043 {
1044 count += ring->nCoordinates();
1045 }
1046
1047 return count;
1048}
1049
1051{
1052 if ( id.part != 0 )
1053 return -1;
1054
1055 if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
1056 return -1;
1057
1058 int number = 0;
1059 if ( id.ring == 0 )
1060 {
1061 return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1062 }
1063 else
1064 {
1065 number += mExteriorRing->numPoints();
1066 }
1067
1068 for ( int i = 0; i < mInteriorRings.count(); ++i )
1069 {
1070 if ( id.ring == i + 1 )
1071 {
1072 int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
1073 if ( partNumber == -1 )
1074 return -1;
1075 return number + partNumber;
1076 }
1077 else
1078 {
1079 number += mInteriorRings.at( i )->numPoints();
1080 }
1081 }
1082 return -1; // should not happen
1083}
1084
1086{
1087 if ( !mExteriorRing )
1088 return true;
1089
1090 return mExteriorRing->isEmpty();
1091}
1092
1093double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
1094{
1095 if ( !mExteriorRing )
1096 {
1097 return -1;
1098 }
1099 QVector<QgsCurve *> segmentList;
1100 segmentList.append( mExteriorRing.get() );
1101 segmentList.append( mInteriorRings );
1102 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
1103}
1104
1106{
1107 if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
1108 {
1109 return false;
1110 }
1111
1112 if ( vId.ring < 0 )
1113 {
1114 vId.ring = 0;
1115 vId.vertex = -1;
1116 if ( vId.part < 0 )
1117 {
1118 vId.part = 0;
1119 }
1120 return mExteriorRing->nextVertex( vId, vertex );
1121 }
1122 else
1123 {
1124 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1125
1126 if ( ring->nextVertex( vId, vertex ) )
1127 {
1128 return true;
1129 }
1130 ++vId.ring;
1131 vId.vertex = -1;
1132 if ( vId.ring >= 1 + mInteriorRings.size() )
1133 {
1134 return false;
1135 }
1136 ring = mInteriorRings[vId.ring - 1];
1137 return ring->nextVertex( vId, vertex );
1138 }
1139}
1140
1141void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1142{
1143 int n = curve->numPoints();
1144 if ( vertex.vertex < 0 || vertex.vertex >= n )
1145 {
1146 previousVertex = QgsVertexId();
1147 nextVertex = QgsVertexId();
1148 return;
1149 }
1150
1151 if ( vertex.vertex == 0 && n < 3 )
1152 {
1153 previousVertex = QgsVertexId();
1154 }
1155 else if ( vertex.vertex == 0 )
1156 {
1157 previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1158 }
1159 else
1160 {
1161 previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1162 }
1163 if ( vertex.vertex == n - 1 && n < 3 )
1164 {
1165 nextVertex = QgsVertexId();
1166 }
1167 else if ( vertex.vertex == n - 1 )
1168 {
1169 nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1170 }
1171 else
1172 {
1173 nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1174 }
1175}
1176
1178{
1179 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1180 {
1181 previousVertex = QgsVertexId();
1183 return;
1184 }
1185
1186 if ( vertex.ring == 0 )
1187 {
1188 ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1189 }
1190 else
1191 {
1192 ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1193 }
1194}
1195
1197{
1198 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1199 {
1200 return false;
1201 }
1202
1203 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1204 int n = ring->numPoints();
1205 bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1206 if ( !success )
1207 {
1208 return false;
1209 }
1210
1211 // If first or last vertex is inserted, re-sync the last/first vertex
1212 if ( vId.vertex == 0 )
1213 ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1214 else if ( vId.vertex == n )
1215 ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1216
1217 clearCache();
1218
1219 return true;
1220}
1221
1223{
1224 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1225 {
1226 return false;
1227 }
1228
1229 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1230 int n = ring->numPoints();
1231 bool success = ring->moveVertex( vId, newPos );
1232 if ( success )
1233 {
1234 // If first or last vertex is moved, also move the last/first vertex
1235 if ( vId.vertex == 0 )
1236 ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1237 else if ( vId.vertex == n - 1 )
1238 ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1239 clearCache();
1240 }
1241 return success;
1242}
1243
1245{
1246 const int interiorRingId = vId.ring - 1;
1247 if ( !mExteriorRing || vId.ring < 0 || interiorRingId >= mInteriorRings.size() )
1248 {
1249 return false;
1250 }
1251
1252 // cppcheck-suppress containerOutOfBounds
1253 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1254 int n = ring->numPoints();
1255 if ( n <= 4 )
1256 {
1257 //no points will be left in ring, so remove whole ring
1258 if ( vId.ring == 0 )
1259 {
1260 mExteriorRing.reset();
1261 if ( !mInteriorRings.isEmpty() )
1262 {
1263 mExteriorRing.reset( mInteriorRings.takeFirst() );
1264 }
1265 }
1266 else
1267 {
1268 removeInteriorRing( vId.ring - 1 );
1269 }
1270 clearCache();
1271 return true;
1272 }
1273
1274 bool success = ring->deleteVertex( vId );
1275 if ( success )
1276 {
1277 // If first or last vertex is removed, re-sync the last/first vertex
1278 // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1279 // may have been deleted (e.g. with CircularString)
1280 if ( vId.vertex == 0 )
1281 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1282 else if ( vId.vertex == n - 1 )
1283 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1284 clearCache();
1285 }
1286 return success;
1287}
1288
1290{
1291 if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1292 {
1293 return true;
1294 }
1295
1296 for ( const QgsCurve *ring : mInteriorRings )
1297 {
1298 if ( ring->hasCurvedSegments() )
1299 {
1300 return true;
1301 }
1302 }
1303 return false;
1304}
1305
1307{
1308 return toPolygon( tolerance, toleranceType );
1309}
1310
1312{
1313 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1314 {
1315 //makes no sense - conversion of false to double!
1316 return false;
1317 }
1318
1319 QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1320 return ring->vertexAngle( vertex );
1321}
1322
1323int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1324{
1325 return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1326}
1327
1329{
1330 return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1331}
1332
1334{
1335 return ringCount() > 0 ? 1 : 0;
1336}
1337
1339{
1340 return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1341}
1342
1344{
1345 if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1346 {
1347 return 0.0;
1348 }
1349
1350 const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1351 return ring->segmentLength( startVertex );
1352}
1353
1354bool QgsCurvePolygon::addZValue( double zValue )
1355{
1356 if ( QgsWkbTypes::hasZ( mWkbType ) )
1357 return false;
1358
1360
1361 if ( mExteriorRing )
1362 mExteriorRing->addZValue( zValue );
1363 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1364 {
1365 curve->addZValue( zValue );
1366 }
1367 clearCache();
1368 return true;
1369}
1370
1371bool QgsCurvePolygon::addMValue( double mValue )
1372{
1373 if ( QgsWkbTypes::hasM( mWkbType ) )
1374 return false;
1375
1377
1378 if ( mExteriorRing )
1379 mExteriorRing->addMValue( mValue );
1380 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1381 {
1382 curve->addMValue( mValue );
1383 }
1384 clearCache();
1385 return true;
1386}
1387
1389{
1390 if ( !is3D() )
1391 return false;
1392
1394 if ( mExteriorRing )
1395 mExteriorRing->dropZValue();
1396 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1397 {
1398 curve->dropZValue();
1399 }
1400 clearCache();
1401 return true;
1402}
1403
1405{
1406 if ( !isMeasure() )
1407 return false;
1408
1410 if ( mExteriorRing )
1411 mExteriorRing->dropMValue();
1412 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1413 {
1414 curve->dropMValue();
1415 }
1416 clearCache();
1417 return true;
1418}
1419
1421{
1422 if ( mExteriorRing )
1423 mExteriorRing->swapXy();
1424 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1425 {
1426 curve->swapXy();
1427 }
1428 clearCache();
1429}
1430
1432{
1433 return clone();
1434}
1435
1437{
1438 if ( !transformer )
1439 return false;
1440
1441 bool res = true;
1442 if ( mExteriorRing )
1443 res = mExteriorRing->transform( transformer, feedback );
1444
1445 if ( !res || ( feedback && feedback->isCanceled() ) )
1446 {
1447 clearCache();
1448 return false;
1449 }
1450
1451 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1452 {
1453 res = curve->transform( transformer );
1454
1455 if ( feedback && feedback->isCanceled() )
1456 res = false;
1457
1458 if ( !res )
1459 break;
1460 }
1461 clearCache();
1462 return res;
1463}
1464
1465void QgsCurvePolygon::filterVertices( const std::function<bool( const QgsPoint & )> &filter )
1466{
1467 if ( mExteriorRing )
1468 mExteriorRing->filterVertices( filter );
1469
1470 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1471 {
1472 curve->filterVertices( filter );
1473 }
1474 clearCache();
1475}
1476
1477void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1478{
1479 if ( mExteriorRing )
1480 mExteriorRing->transformVertices( transform );
1481
1482 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1483 {
1484 curve->transformVertices( transform );
1485 }
1486 clearCache();
1487}
1488
1490{
1491 return 1 + mInteriorRings.count();
1492}
1493
1495{
1496 if ( index == 0 )
1497 return mExteriorRing.get();
1498 else
1499 return mInteriorRings.at( index - 1 );
1500}
1501
1503{
1504 const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1505 if ( !otherPolygon )
1506 return -1;
1507
1508 if ( mExteriorRing && !otherPolygon->mExteriorRing )
1509 return 1;
1510 else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1511 return -1;
1512 else if ( mExteriorRing && otherPolygon->mExteriorRing )
1513 {
1514 int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1515 if ( shellComp != 0 )
1516 {
1517 return shellComp;
1518 }
1519 }
1520
1521 const int nHole1 = mInteriorRings.size();
1522 const int nHole2 = otherPolygon->mInteriorRings.size();
1523 if ( nHole1 < nHole2 )
1524 {
1525 return -1;
1526 }
1527 if ( nHole1 > nHole2 )
1528 {
1529 return 1;
1530 }
1531
1532 for ( int i = 0; i < nHole1; i++ )
1533 {
1534 const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1535 if ( holeComp != 0 )
1536 {
1537 return holeComp;
1538 }
1539 }
1540
1541 return 0;
1542}
@ CounterClockwise
Counter-clockwise direction.
Definition qgis.h:3548
@ Clockwise
Clockwise direction.
Definition qgis.h:3547
VertexType
Types of vertex.
Definition qgis.h:3179
@ Polygon
Polygons.
Definition qgis.h:382
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ CompoundCurve
CompoundCurve.
Definition qgis.h:305
@ LineString
LineString.
Definition qgis.h:297
@ Polygon
Polygon.
Definition qgis.h:298
@ CircularString
CircularString.
Definition qgis.h:304
@ CurvePolygon
CurvePolygon.
Definition qgis.h:306
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2764
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:211
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:198
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition qgscurve.cpp:87
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
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:99
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
QString mValidityFailureReason
Definition qgssurface.h:101
bool mHasCachedValidity
Definition qgssurface.h:100
WKB pointer handler.
Definition qgswkbptr.h:47
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:6975
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:99
int part
Part number.
Definition qgsvertexid.h:93
int ring
Ring number.
Definition qgsvertexid.h:96