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