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