QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
81}
82
84{
85 if ( &p != this )
86 {
87 clearCache();
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 QString childWkt = curve->asWkt( precision );
309 if ( qgsgeometry_cast<const QgsLineString *>( curve ) )
310 {
311 // Type names of linear geometries are omitted
312 childWkt = childWkt.mid( childWkt.indexOf( '(' ) );
313 }
314 wkt += childWkt + ',';
315 }
316 if ( wkt.endsWith( ',' ) )
317 {
318 wkt.chop( 1 ); // Remove last ','
319 }
320 wkt += ')';
321 }
322 return wkt;
323}
324
325QDomElement QgsCurvePolygon::asGml2( QDomDocument &doc, int precision, const QString &ns, const AxisOrder axisOrder ) const
326{
327 // GML2 does not support curves
328 QDomElement elemPolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
329
330 if ( isEmpty() )
331 return elemPolygon;
332
333 QDomElement elemOuterBoundaryIs = doc.createElementNS( ns, QStringLiteral( "outerBoundaryIs" ) );
334 std::unique_ptr< QgsLineString > exteriorLineString( exteriorRing()->curveToLine() );
335 QDomElement outerRing = exteriorLineString->asGml2( doc, precision, ns, axisOrder );
336 outerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
337 elemOuterBoundaryIs.appendChild( outerRing );
338 elemPolygon.appendChild( elemOuterBoundaryIs );
339 std::unique_ptr< QgsLineString > interiorLineString;
340 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
341 {
342 QDomElement elemInnerBoundaryIs = doc.createElementNS( ns, QStringLiteral( "innerBoundaryIs" ) );
343 interiorLineString.reset( interiorRing( i )->curveToLine() );
344 QDomElement innerRing = interiorLineString->asGml2( doc, precision, ns, axisOrder );
345 innerRing.toElement().setTagName( QStringLiteral( "LinearRing" ) );
346 elemInnerBoundaryIs.appendChild( innerRing );
347 elemPolygon.appendChild( elemInnerBoundaryIs );
348 }
349 return elemPolygon;
350}
351
352QDomElement QgsCurvePolygon::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
353{
354 QDomElement elemCurvePolygon = doc.createElementNS( ns, QStringLiteral( "Polygon" ) );
355
356 if ( isEmpty() )
357 return elemCurvePolygon;
358
359 QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
360 QDomElement curveElem = exteriorRing()->asGml3( doc, precision, ns, axisOrder );
361 if ( curveElem.tagName() == QLatin1String( "LineString" ) )
362 {
363 curveElem.setTagName( QStringLiteral( "LinearRing" ) );
364 }
365 elemExterior.appendChild( curveElem );
366 elemCurvePolygon.appendChild( elemExterior );
367
368 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
369 {
370 QDomElement elemInterior = doc.createElementNS( ns, QStringLiteral( "interior" ) );
371 QDomElement innerRing = interiorRing( i )->asGml3( doc, precision, ns, axisOrder );
372 if ( innerRing.tagName() == QLatin1String( "LineString" ) )
373 {
374 innerRing.setTagName( QStringLiteral( "LinearRing" ) );
375 }
376 elemInterior.appendChild( innerRing );
377 elemCurvePolygon.appendChild( elemInterior );
378 }
379 return elemCurvePolygon;
380}
381
383{
384 json coordinates( json::array( ) );
385 if ( auto *lExteriorRing = exteriorRing() )
386 {
387 std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
388 QgsPointSequence exteriorPts;
389 exteriorLineString->points( exteriorPts );
390 coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
391
392 std::unique_ptr< QgsLineString > interiorLineString;
393 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
394 {
395 interiorLineString.reset( interiorRing( i )->curveToLine() );
396 QgsPointSequence interiorPts;
397 interiorLineString->points( interiorPts );
398 coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
399 }
400 }
401 return
402 {
403 { "type", "Polygon" },
404 { "coordinates", coordinates }
405 };
406}
407
409{
410 QString kml;
411 kml.append( QLatin1String( "<Polygon>" ) );
412 if ( mExteriorRing )
413 {
414 kml.append( QLatin1String( "<outerBoundaryIs>" ) );
415 kml.append( mExteriorRing->asKml( precision ) );
416 kml.append( QLatin1String( "</outerBoundaryIs>" ) );
417 }
418 const QVector<QgsCurve *> &interiorRings = mInteriorRings;
419 for ( const QgsCurve *ring : interiorRings )
420 {
421 kml.append( QLatin1String( "<innerBoundaryIs>" ) );
422 kml.append( ring->asKml( precision ) );
423 kml.append( QLatin1String( "</innerBoundaryIs>" ) );
424 }
425 kml.append( QLatin1String( "</Polygon>" ) );
426 return kml;
427}
428
430{
431 // normalize rings
432 if ( mExteriorRing )
433 mExteriorRing->normalize();
434
435 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
436 {
437 ring->normalize();
438 }
439
440 // sort rings
441 std::sort( mInteriorRings.begin(), mInteriorRings.end(), []( const QgsCurve * a, const QgsCurve * b )
442 {
443 return a->compareTo( b ) > 0;
444 } );
445
446 // normalize ring orientation
447 forceRHR();
448}
449
451{
452 if ( !mExteriorRing )
453 {
454 return 0.0;
455 }
456
457 double totalArea = 0.0;
458
459 if ( mExteriorRing->isRing() )
460 {
461 double area = 0.0;
462 mExteriorRing->sumUpArea( area );
463 totalArea += std::fabs( area );
464 }
465
466 for ( const QgsCurve *ring : mInteriorRings )
467 {
468 double area = 0.0;
469 if ( ring->isRing() )
470 {
471 ring->sumUpArea( area );
472 totalArea -= std::fabs( area );
473 }
474 }
475 return totalArea;
476}
477
479{
480 if ( !mExteriorRing )
481 return 0.0;
482
483 //sum perimeter of rings
484 double perimeter = mExteriorRing->length();
485 for ( const QgsCurve *ring : mInteriorRings )
486 {
487 perimeter += ring->length();
488 }
489 return perimeter;
490}
491
493{
494 const double p = perimeter();
495 if ( qgsDoubleNear( p, 0.0 ) )
496 return 0.0;
497
498 return 4.0 * M_PI * area() / pow( p, 2.0 );
499}
500
502{
503 std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
504 if ( !mExteriorRing )
505 return polygon.release();
506
507 polygon->setExteriorRing( exteriorRing()->curveToLine() );
508 QVector<QgsCurve *> interiors;
509 int n = numInteriorRings();
510 interiors.reserve( n );
511 for ( int i = 0; i < n; ++i )
512 {
513 interiors.append( interiorRing( i )->curveToLine() );
514 }
515 polygon->setInteriorRings( interiors );
516 return polygon.release();
517}
518
520{
521 if ( !mExteriorRing )
522 return nullptr;
523
524 if ( mInteriorRings.isEmpty() )
525 {
526 return mExteriorRing->clone();
527 }
528 else
529 {
530 QgsMultiCurve *multiCurve = new QgsMultiCurve();
531 int nInteriorRings = mInteriorRings.size();
532 multiCurve->reserve( nInteriorRings + 1 );
533 multiCurve->addGeometry( mExteriorRing->clone() );
534 for ( int i = 0; i < nInteriorRings; ++i )
535 {
536 multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
537 }
538 return multiCurve;
539 }
540}
541
542QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
543{
544 if ( !mExteriorRing )
545 return nullptr;
546
547
548 std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
549
550 // exterior ring
551 auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
552
553 if ( !exterior )
554 return nullptr;
555
556 polygon->mExteriorRing = std::move( exterior );
557
558 //interior rings
559 for ( auto interior : mInteriorRings )
560 {
561 if ( !interior )
562 continue;
563
564 QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
565
566 if ( !gridifiedInterior )
567 continue;
568
569 polygon->mInteriorRings.append( gridifiedInterior );
570 }
571
572 return polygon.release();
573
574}
575
576bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
577{
578 bool result = false;
579 auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
580 {
581 if ( ring->numPoints() <= 4 )
582 return false;
583
584 if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
585 {
586 QgsPoint startPoint;
587 Qgis::VertexType type;
588 ring->pointAt( 0, startPoint, type );
589 // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
590 ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
591 return true;
592 }
593
594 return false;
595 };
596 if ( mExteriorRing )
597 {
598 result = cleanRing( mExteriorRing.get() );
599 }
600 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
601 {
602 if ( cleanRing( ring ) ) result = true;
603 }
604 return result;
605}
606
608{
609 if ( !mExteriorRing && mInteriorRings.empty() )
610 return false;
611
612 // if we already have the bounding box calculated, then this check is trivial!
613 if ( !mBoundingBox.isNull() )
614 {
615 return mBoundingBox.intersects( box3d );
616 }
617
618 // loop through each ring and test the bounding box intersection.
619 // This gives us a chance to use optimisations which may be present on the individual
620 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
621 // of each individual ring geometry which we would have to do anyway... (and these
622 // bounding boxes are cached, so would be reused without additional expense)
623 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box3d ) )
624 return true;
625
626 for ( const QgsCurve *ring : mInteriorRings )
627 {
628 if ( ring->boundingBoxIntersects( box3d ) )
629 return true;
630 }
631
632 // even if we don't intersect the bounding box of any rings, we may still intersect the
633 // bounding box of the overall polygon (we are considering worst case scenario here and
634 // the polygon is invalid, with rings outside the exterior ring!)
635 // so here we fall back to the non-optimised base class check which has to first calculate
636 // the overall bounding box of the polygon..
637 return QgsSurface::boundingBoxIntersects( box3d );
638}
639
640QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
641{
642 std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
643 if ( !mExteriorRing )
644 {
645 return poly.release();
646 }
647
648 poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
649
650 QVector<QgsCurve *> rings;
651 rings.reserve( mInteriorRings.size() );
652 for ( const QgsCurve *ring : mInteriorRings )
653 {
654 rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
655 }
656 poly->setInteriorRings( rings );
657 return poly.release();
658}
659
661{
662 if ( !ring )
663 {
664 return;
665 }
666 mExteriorRing.reset( ring );
667
668 //set proper wkb type
670 {
672 }
674 {
676 }
677
678 //match dimensionality for rings
679 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
680 {
681 if ( is3D() )
682 ring->addZValue();
683 else
684 ring->dropZValue();
685
686 if ( isMeasure() )
687 ring->addMValue();
688 else
689 ring->dropMValue();
690 }
691 clearCache();
692}
693
694void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
695{
696 qDeleteAll( mInteriorRings );
697 mInteriorRings.clear();
698
699 //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
700 for ( QgsCurve *ring : rings )
701 {
702 addInteriorRing( ring );
703 }
704 clearCache();
705}
706
708{
709 if ( !ring )
710 return;
711
712 //ensure dimensionality of ring matches curve polygon
713 if ( !is3D() )
714 ring->dropZValue();
715 else if ( !ring->is3D() )
716 ring->addZValue();
717
718 if ( !isMeasure() )
719 ring->dropMValue();
720 else if ( !ring->isMeasure() )
721 ring->addMValue();
722
723 mInteriorRings.append( ring );
724 clearCache();
725}
726
728{
729 if ( nr < 0 || nr >= mInteriorRings.size() )
730 {
731 return false;
732 }
733 delete mInteriorRings.takeAt( nr );
734 clearCache();
735 return true;
736}
737
738void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
739{
740 for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
741 {
742 if ( minimumAllowedArea < 0 )
743 delete mInteriorRings.takeAt( ringIndex );
744 else
745 {
746 double area = 0.0;
747 mInteriorRings.at( ringIndex )->sumUpArea( area );
748 if ( std::fabs( area ) < minimumAllowedArea )
749 delete mInteriorRings.takeAt( ringIndex );
750 }
751 }
752
753 clearCache();
754}
755
757{
758 QVector<QgsCurve *> validRings;
759 validRings.reserve( mInteriorRings.size() );
760 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
761 {
762 if ( !curve->isRing() )
763 {
764 // remove invalid rings
765 delete curve;
766 }
767 else
768 {
769 validRings << curve;
770 }
771 }
772 mInteriorRings = validRings;
773}
774
776{
778}
779
781{
783 {
784 // flip exterior ring orientation
785 std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
786 mExteriorRing = std::move( flipped );
787 }
788
789 QVector<QgsCurve *> validRings;
790 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
791 {
792 if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
793 {
794 // flip interior ring orientation
795 QgsCurve *flipped = curve->reversed();
796 validRings << flipped;
797 delete curve;
798 }
799 else
800 {
801 validRings << curve;
802 }
803 }
804 mInteriorRings = validRings;
805}
806
808{
810 {
811 // flip exterior ring orientation
812 mExteriorRing.reset( mExteriorRing->reversed() );
813 }
814
815 QVector<QgsCurve *> validRings;
816 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
817 {
818 if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
819 {
820 // flip interior ring orientation
821 QgsCurve *flipped = curve->reversed();
822 validRings << flipped;
823 delete curve;
824 }
825 else
826 {
827 validRings << curve;
828 }
829 }
830 mInteriorRings = validRings;
831}
832
834{
835 QPainterPath p;
836 if ( mExteriorRing )
837 {
838 QPainterPath ring = mExteriorRing->asQPainterPath();
839 ring.closeSubpath();
840 p.addPath( ring );
841 }
842
843 for ( const QgsCurve *ring : mInteriorRings )
844 {
845 QPainterPath ringPath = ring->asQPainterPath();
846 ringPath.closeSubpath();
847 p.addPath( ringPath );
848 }
849
850 return p;
851}
852
853void QgsCurvePolygon::draw( QPainter &p ) const
854{
855 if ( !mExteriorRing )
856 return;
857
858 if ( mInteriorRings.empty() )
859 {
860 mExteriorRing->drawAsPolygon( p );
861 }
862 else
863 {
864 QPainterPath path;
865 mExteriorRing->addToPainterPath( path );
866
867 for ( const QgsCurve *ring : mInteriorRings )
868 {
869 ring->addToPainterPath( path );
870 }
871 p.drawPath( path );
872 }
873}
874
876{
877 if ( mExteriorRing )
878 {
879 mExteriorRing->transform( ct, d, transformZ );
880 }
881
882 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
883 {
884 curve->transform( ct, d, transformZ );
885 }
886 clearCache();
887}
888
889void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
890{
891 if ( mExteriorRing )
892 {
893 mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
894 }
895
896 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
897 {
898 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
899 }
900 clearCache();
901}
902
904{
905 QgsCoordinateSequence sequence;
906 sequence.append( QgsRingSequence() );
907
908 if ( mExteriorRing )
909 {
910 sequence.back().append( QgsPointSequence() );
911 mExteriorRing->points( sequence.back().back() );
912 }
913
914 for ( const QgsCurve *ring : mInteriorRings )
915 {
916 sequence.back().append( QgsPointSequence() );
917 ring->points( sequence.back().back() );
918 }
919
920 return sequence;
921}
922
924{
925 int count = 0;
926
927 if ( mExteriorRing )
928 {
929 count += mExteriorRing->nCoordinates();
930 }
931
932 for ( const QgsCurve *ring : mInteriorRings )
933 {
934 count += ring->nCoordinates();
935 }
936
937 return count;
938}
939
941{
942 if ( id.part != 0 )
943 return -1;
944
945 if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
946 return -1;
947
948 int number = 0;
949 if ( id.ring == 0 )
950 {
951 return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
952 }
953 else
954 {
955 number += mExteriorRing->numPoints();
956 }
957
958 for ( int i = 0; i < mInteriorRings.count(); ++i )
959 {
960 if ( id.ring == i + 1 )
961 {
962 int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
963 if ( partNumber == -1 )
964 return -1;
965 return number + partNumber;
966 }
967 else
968 {
969 number += mInteriorRings.at( i )->numPoints();
970 }
971 }
972 return -1; // should not happen
973}
974
976{
977 if ( !mExteriorRing )
978 return true;
979
980 return mExteriorRing->isEmpty();
981}
982
983double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
984{
985 if ( !mExteriorRing )
986 {
987 return -1;
988 }
989 QVector<QgsCurve *> segmentList;
990 segmentList.append( mExteriorRing.get() );
991 segmentList.append( mInteriorRings );
992 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
993}
994
996{
997 if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
998 {
999 return false;
1000 }
1001
1002 if ( vId.ring < 0 )
1003 {
1004 vId.ring = 0;
1005 vId.vertex = -1;
1006 if ( vId.part < 0 )
1007 {
1008 vId.part = 0;
1009 }
1010 return mExteriorRing->nextVertex( vId, vertex );
1011 }
1012 else
1013 {
1014 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1015
1016 if ( ring->nextVertex( vId, vertex ) )
1017 {
1018 return true;
1019 }
1020 ++vId.ring;
1021 vId.vertex = -1;
1022 if ( vId.ring >= 1 + mInteriorRings.size() )
1023 {
1024 return false;
1025 }
1026 ring = mInteriorRings[ vId.ring - 1 ];
1027 return ring->nextVertex( vId, vertex );
1028 }
1029}
1030
1031void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1032{
1033 int n = curve->numPoints();
1034 if ( vertex.vertex < 0 || vertex.vertex >= n )
1035 {
1036 previousVertex = QgsVertexId();
1037 nextVertex = QgsVertexId();
1038 return;
1039 }
1040
1041 if ( vertex.vertex == 0 && n < 3 )
1042 {
1043 previousVertex = QgsVertexId();
1044 }
1045 else if ( vertex.vertex == 0 )
1046 {
1047 previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1048 }
1049 else
1050 {
1051 previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1052 }
1053 if ( vertex.vertex == n - 1 && n < 3 )
1054 {
1055 nextVertex = QgsVertexId();
1056 }
1057 else if ( vertex.vertex == n - 1 )
1058 {
1059 nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1060 }
1061 else
1062 {
1063 nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1064 }
1065}
1066
1067void QgsCurvePolygon::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
1068{
1069 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1070 {
1071 previousVertex = QgsVertexId();
1073 return;
1074 }
1075
1076 if ( vertex.ring == 0 )
1077 {
1078 ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1079 }
1080 else
1081 {
1082 ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1083 }
1084}
1085
1087{
1088 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1089 {
1090 return false;
1091 }
1092
1093 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1094 int n = ring->numPoints();
1095 bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1096 if ( !success )
1097 {
1098 return false;
1099 }
1100
1101 // If first or last vertex is inserted, re-sync the last/first vertex
1102 if ( vId.vertex == 0 )
1103 ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1104 else if ( vId.vertex == n )
1105 ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1106
1107 clearCache();
1108
1109 return true;
1110}
1111
1113{
1114 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1115 {
1116 return false;
1117 }
1118
1119 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1120 int n = ring->numPoints();
1121 bool success = ring->moveVertex( vId, newPos );
1122 if ( success )
1123 {
1124 // If first or last vertex is moved, also move the last/first vertex
1125 if ( vId.vertex == 0 )
1126 ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1127 else if ( vId.vertex == n - 1 )
1128 ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1129 clearCache();
1130 }
1131 return success;
1132}
1133
1135{
1136 const int interiorRingId = vId.ring - 1;
1137 if ( !mExteriorRing || vId.ring < 0 || interiorRingId >= mInteriorRings.size() )
1138 {
1139 return false;
1140 }
1141
1142 // cppcheck-suppress containerOutOfBounds
1143 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1144 int n = ring->numPoints();
1145 if ( n <= 4 )
1146 {
1147 //no points will be left in ring, so remove whole ring
1148 if ( vId.ring == 0 )
1149 {
1150 mExteriorRing.reset();
1151 if ( !mInteriorRings.isEmpty() )
1152 {
1153 mExteriorRing.reset( mInteriorRings.takeFirst() );
1154 }
1155 }
1156 else
1157 {
1158 removeInteriorRing( vId.ring - 1 );
1159 }
1160 clearCache();
1161 return true;
1162 }
1163
1164 bool success = ring->deleteVertex( vId );
1165 if ( success )
1166 {
1167 // If first or last vertex is removed, re-sync the last/first vertex
1168 // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1169 // may have been deleted (e.g. with CircularString)
1170 if ( vId.vertex == 0 )
1171 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1172 else if ( vId.vertex == n - 1 )
1173 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1174 clearCache();
1175 }
1176 return success;
1177}
1178
1180{
1181 if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1182 {
1183 return true;
1184 }
1185
1186 for ( const QgsCurve *ring : mInteriorRings )
1187 {
1188 if ( ring->hasCurvedSegments() )
1189 {
1190 return true;
1191 }
1192 }
1193 return false;
1194}
1195
1197{
1198 return toPolygon( tolerance, toleranceType );
1199}
1200
1202{
1203 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1204 {
1205 //makes no sense - conversion of false to double!
1206 return false;
1207 }
1208
1209 QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1210 return ring->vertexAngle( vertex );
1211}
1212
1213int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1214{
1215 return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1216}
1217
1219{
1220 return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1221}
1222
1224{
1225 return ringCount() > 0 ? 1 : 0;
1226}
1227
1229{
1230 return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1231}
1232
1234{
1235 if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1236 {
1237 return 0.0;
1238 }
1239
1240 const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1241 return ring->segmentLength( startVertex );
1242}
1243
1244bool QgsCurvePolygon::addZValue( double zValue )
1245{
1246 if ( QgsWkbTypes::hasZ( mWkbType ) )
1247 return false;
1248
1250
1251 if ( mExteriorRing )
1252 mExteriorRing->addZValue( zValue );
1253 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1254 {
1255 curve->addZValue( zValue );
1256 }
1257 clearCache();
1258 return true;
1259}
1260
1261bool QgsCurvePolygon::addMValue( double mValue )
1262{
1263 if ( QgsWkbTypes::hasM( mWkbType ) )
1264 return false;
1265
1267
1268 if ( mExteriorRing )
1269 mExteriorRing->addMValue( mValue );
1270 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1271 {
1272 curve->addMValue( mValue );
1273 }
1274 clearCache();
1275 return true;
1276}
1277
1279{
1280 if ( !is3D() )
1281 return false;
1282
1284 if ( mExteriorRing )
1285 mExteriorRing->dropZValue();
1286 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1287 {
1288 curve->dropZValue();
1289 }
1290 clearCache();
1291 return true;
1292}
1293
1295{
1296 if ( !isMeasure() )
1297 return false;
1298
1300 if ( mExteriorRing )
1301 mExteriorRing->dropMValue();
1302 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1303 {
1304 curve->dropMValue();
1305 }
1306 clearCache();
1307 return true;
1308}
1309
1311{
1312 if ( mExteriorRing )
1313 mExteriorRing->swapXy();
1314 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1315 {
1316 curve->swapXy();
1317 }
1318 clearCache();
1319}
1320
1322{
1323 return clone();
1324}
1325
1327{
1328 if ( !transformer )
1329 return false;
1330
1331 bool res = true;
1332 if ( mExteriorRing )
1333 res = mExteriorRing->transform( transformer, feedback );
1334
1335 if ( !res || ( feedback && feedback->isCanceled() ) )
1336 {
1337 clearCache();
1338 return false;
1339 }
1340
1341 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1342 {
1343 res = curve->transform( transformer );
1344
1345 if ( feedback && feedback->isCanceled() )
1346 res = false;
1347
1348 if ( !res )
1349 break;
1350 }
1351 clearCache();
1352 return res;
1353}
1354
1355void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1356{
1357 if ( mExteriorRing )
1358 mExteriorRing->filterVertices( filter );
1359
1360 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1361 {
1362 curve->filterVertices( filter );
1363 }
1364 clearCache();
1365}
1366
1367void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1368{
1369 if ( mExteriorRing )
1370 mExteriorRing->transformVertices( transform );
1371
1372 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1373 {
1374 curve->transformVertices( transform );
1375 }
1376 clearCache();
1377}
1378
1380{
1381 return 1 + mInteriorRings.count();
1382}
1383
1385{
1386 if ( index == 0 )
1387 return mExteriorRing.get();
1388 else
1389 return mInteriorRings.at( index - 1 );
1390}
1391
1393{
1394 const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1395 if ( !otherPolygon )
1396 return -1;
1397
1398 if ( mExteriorRing && !otherPolygon->mExteriorRing )
1399 return 1;
1400 else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1401 return -1;
1402 else if ( mExteriorRing && otherPolygon->mExteriorRing )
1403 {
1404 int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1405 if ( shellComp != 0 )
1406 {
1407 return shellComp;
1408 }
1409 }
1410
1411 const int nHole1 = mInteriorRings.size();
1412 const int nHole2 = otherPolygon->mInteriorRings.size();
1413 if ( nHole1 < nHole2 )
1414 {
1415 return -1;
1416 }
1417 if ( nHole1 > nHole2 )
1418 {
1419 return 1;
1420 }
1421
1422 for ( int i = 0; i < nHole1; i++ )
1423 {
1424 const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1425 if ( holeComp != 0 )
1426 {
1427 return holeComp;
1428 }
1429 }
1430
1431 return 0;
1432}
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
VertexType
Types of vertex.
Definition: qgis.h:2477
@ Polygon
Polygons.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ Polygon
Polygon.
@ CircularString
CircularString.
@ CurvePolygon
CurvePolygon.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:2191
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 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.
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.
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:132
bool isNull() const
Test if the box is null (holding no spatial information).
Definition: qgsbox3d.cpp:289
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.
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.
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 ...
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.
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...
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)
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
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.
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition: qgscurve.cpp:198
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition: qgscurve.cpp:87
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h: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.
Definition: qgslinestring.h:45
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
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:439
Polygon geometry type.
Definition: qgspolygon.h:33
Surface geometry type.
Definition: qgssurface.h:34
QgsBox3D mBoundingBox
Definition: qgssurface.h:80
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgssurface.cpp:43
QString mValidityFailureReason
Definition: qgssurface.h:82
bool mHasCachedValidity
Definition: qgssurface.h:81
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.
Definition: qgswkbtypes.h:1144
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...
Definition: qgswkbtypes.h:862
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
Definition: qgswkbtypes.h:1127
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1092
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1068
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:628
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:222
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
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