QGIS API Documentation 3.37.0-Master (49cede7946b)
Loading...
Searching...
No Matches
qgscurvepolygon.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscurvepolygon.cpp
3 ---------------------
4 begin : September 2014
5 copyright : (C) 2014 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgscurvepolygon.h"
19#include "qgsapplication.h"
20#include "qgscircularstring.h"
21#include "qgscompoundcurve.h"
22#include "qgsgeometryutils.h"
23#include "qgslinestring.h"
24#include "qgspolygon.h"
25#include "qgswkbptr.h"
26#include "qgsmulticurve.h"
27#include "qgsfeedback.h"
28
29#include <QJsonArray>
30#include <QJsonObject>
31#include <QPainter>
32#include <QPainterPath>
33#include <memory>
34#include <nlohmann/json.hpp>
35
40
45
47{
48 auto result = std::make_unique< QgsCurvePolygon >();
49 result->mWkbType = mWkbType;
50 return result.release();
51}
52
54{
55 return QStringLiteral( "CurvePolygon" );
56}
57
59{
60 return 2;
61}
62
64 : QgsSurface( p )
65
66{
68 if ( p.mExteriorRing )
69 {
70 mExteriorRing.reset( p.mExteriorRing->clone() );
71 }
72
73 for ( const QgsCurve *ring : p.mInteriorRings )
74 {
75 mInteriorRings.push_back( ring->clone() );
76 }
77
81}
82
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 const auto exportRing = [&doc, precision, &ns, axisOrder]( const QgsCurve * ring )
360 {
361 QDomElement ringElem = ring->asGml3( doc, precision, ns, axisOrder );
362 if ( ringElem.tagName() == QLatin1String( "LineString" ) )
363 {
364 ringElem.setTagName( QStringLiteral( "LinearRing" ) );
365 }
366 else if ( ringElem.tagName() == QLatin1String( "CompositeCurve" ) )
367 {
368 ringElem.setTagName( QStringLiteral( "Ring" ) );
369 }
370 else if ( ringElem.tagName() == QLatin1String( "Curve" ) )
371 {
372 QDomElement ringElemNew = doc.createElementNS( ns, QStringLiteral( "Ring" ) );
373 QDomElement curveMemberElem = doc.createElementNS( ns, QStringLiteral( "curveMember" ) );
374 ringElemNew.appendChild( curveMemberElem );
375 curveMemberElem.appendChild( ringElem );
376 ringElem = std::move( ringElemNew );
377 }
378 return ringElem;
379 };
380
381 QDomElement elemExterior = doc.createElementNS( ns, QStringLiteral( "exterior" ) );
382 elemExterior.appendChild( exportRing( exteriorRing() ) );
383 elemCurvePolygon.appendChild( elemExterior );
384
385 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
386 {
387 QDomElement elemInterior = doc.createElementNS( ns, QStringLiteral( "interior" ) );
388 elemInterior.appendChild( exportRing( interiorRing( i ) ) );
389 elemCurvePolygon.appendChild( elemInterior );
390 }
391 return elemCurvePolygon;
392}
393
395{
396 json coordinates( json::array( ) );
397 if ( auto *lExteriorRing = exteriorRing() )
398 {
399 std::unique_ptr< QgsLineString > exteriorLineString( lExteriorRing->curveToLine() );
400 QgsPointSequence exteriorPts;
401 exteriorLineString->points( exteriorPts );
402 coordinates.push_back( QgsGeometryUtils::pointsToJson( exteriorPts, precision ) );
403
404 std::unique_ptr< QgsLineString > interiorLineString;
405 for ( int i = 0, n = numInteriorRings(); i < n; ++i )
406 {
407 interiorLineString.reset( interiorRing( i )->curveToLine() );
408 QgsPointSequence interiorPts;
409 interiorLineString->points( interiorPts );
410 coordinates.push_back( QgsGeometryUtils::pointsToJson( interiorPts, precision ) );
411 }
412 }
413 return
414 {
415 { "type", "Polygon" },
416 { "coordinates", coordinates }
417 };
418}
419
421{
422 QString kml;
423 kml.append( QLatin1String( "<Polygon>" ) );
424 if ( mExteriorRing )
425 {
426 kml.append( QLatin1String( "<outerBoundaryIs>" ) );
427 kml.append( mExteriorRing->asKml( precision ) );
428 kml.append( QLatin1String( "</outerBoundaryIs>" ) );
429 }
430 const QVector<QgsCurve *> &interiorRings = mInteriorRings;
431 for ( const QgsCurve *ring : interiorRings )
432 {
433 kml.append( QLatin1String( "<innerBoundaryIs>" ) );
434 kml.append( ring->asKml( precision ) );
435 kml.append( QLatin1String( "</innerBoundaryIs>" ) );
436 }
437 kml.append( QLatin1String( "</Polygon>" ) );
438 return kml;
439}
440
442{
443 // normalize rings
444 if ( mExteriorRing )
445 mExteriorRing->normalize();
446
447 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
448 {
449 ring->normalize();
450 }
451
452 // sort rings
453 std::sort( mInteriorRings.begin(), mInteriorRings.end(), []( const QgsCurve * a, const QgsCurve * b )
454 {
455 return a->compareTo( b ) > 0;
456 } );
457
458 // normalize ring orientation
459 forceRHR();
460}
461
463{
464 if ( !mExteriorRing )
465 {
466 return 0.0;
467 }
468
469 double totalArea = 0.0;
470
471 if ( mExteriorRing->isRing() )
472 {
473 double area = 0.0;
474 mExteriorRing->sumUpArea( area );
475 totalArea += std::fabs( area );
476 }
477
478 for ( const QgsCurve *ring : mInteriorRings )
479 {
480 double area = 0.0;
481 if ( ring->isRing() )
482 {
483 ring->sumUpArea( area );
484 totalArea -= std::fabs( area );
485 }
486 }
487 return totalArea;
488}
489
491{
492 if ( !mExteriorRing )
493 return 0.0;
494
495 //sum perimeter of rings
496 double perimeter = mExteriorRing->length();
497 for ( const QgsCurve *ring : mInteriorRings )
498 {
499 perimeter += ring->length();
500 }
501 return perimeter;
502}
503
505{
506 const double p = perimeter();
507 if ( qgsDoubleNear( p, 0.0 ) )
508 return 0.0;
509
510 return 4.0 * M_PI * area() / pow( p, 2.0 );
511}
512
514{
515 std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
516 if ( !mExteriorRing )
517 return polygon.release();
518
519 polygon->setExteriorRing( exteriorRing()->curveToLine() );
520 QVector<QgsCurve *> interiors;
521 int n = numInteriorRings();
522 interiors.reserve( n );
523 for ( int i = 0; i < n; ++i )
524 {
525 interiors.append( interiorRing( i )->curveToLine() );
526 }
527 polygon->setInteriorRings( interiors );
528 return polygon.release();
529}
530
532{
533 if ( !mExteriorRing )
534 return nullptr;
535
536 if ( mInteriorRings.isEmpty() )
537 {
538 return mExteriorRing->clone();
539 }
540 else
541 {
542 QgsMultiCurve *multiCurve = new QgsMultiCurve();
543 int nInteriorRings = mInteriorRings.size();
544 multiCurve->reserve( nInteriorRings + 1 );
545 multiCurve->addGeometry( mExteriorRing->clone() );
546 for ( int i = 0; i < nInteriorRings; ++i )
547 {
548 multiCurve->addGeometry( mInteriorRings.at( i )->clone() );
549 }
550 return multiCurve;
551 }
552}
553
554QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
555{
556 if ( !mExteriorRing )
557 return nullptr;
558
559
560 std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );
561
562 // exterior ring
563 auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) };
564
565 if ( !exterior )
566 return nullptr;
567
568 polygon->mExteriorRing = std::move( exterior );
569
570 //interior rings
571 for ( auto interior : mInteriorRings )
572 {
573 if ( !interior )
574 continue;
575
576 QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) );
577
578 if ( !gridifiedInterior )
579 continue;
580
581 polygon->mInteriorRings.append( gridifiedInterior );
582 }
583
584 return polygon.release();
585
586}
587
588bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
589{
590 bool result = false;
591 auto cleanRing = [epsilon, useZValues ]( QgsCurve * ring )->bool
592 {
593 if ( ring->numPoints() <= 4 )
594 return false;
595
596 if ( ring->removeDuplicateNodes( epsilon, useZValues ) )
597 {
598 QgsPoint startPoint;
599 Qgis::VertexType type;
600 ring->pointAt( 0, startPoint, type );
601 // ensure ring is properly closed - if we removed the final node, it may no longer be properly closed
602 ring->moveVertex( QgsVertexId( -1, -1, ring->numPoints() - 1 ), startPoint );
603 return true;
604 }
605
606 return false;
607 };
608 if ( mExteriorRing )
609 {
610 result = cleanRing( mExteriorRing.get() );
611 }
612 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
613 {
614 if ( cleanRing( ring ) ) result = true;
615 }
616 return result;
617}
618
620{
621 if ( !mExteriorRing && mInteriorRings.empty() )
622 return false;
623
624 // if we already have the bounding box calculated, then this check is trivial!
625 if ( !mBoundingBox.isNull() )
626 {
627 return mBoundingBox.intersects( box3d );
628 }
629
630 // loop through each ring and test the bounding box intersection.
631 // This gives us a chance to use optimisations which may be present on the individual
632 // ring geometry subclasses, and at worst it will cause a calculation of the bounding box
633 // of each individual ring geometry which we would have to do anyway... (and these
634 // bounding boxes are cached, so would be reused without additional expense)
635 if ( mExteriorRing && mExteriorRing->boundingBoxIntersects( box3d ) )
636 return true;
637
638 for ( const QgsCurve *ring : mInteriorRings )
639 {
640 if ( ring->boundingBoxIntersects( box3d ) )
641 return true;
642 }
643
644 // even if we don't intersect the bounding box of any rings, we may still intersect the
645 // bounding box of the overall polygon (we are considering worst case scenario here and
646 // the polygon is invalid, with rings outside the exterior ring!)
647 // so here we fall back to the non-optimised base class check which has to first calculate
648 // the overall bounding box of the polygon..
649 return QgsSurface::boundingBoxIntersects( box3d );
650}
651
652QgsPolygon *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
653{
654 std::unique_ptr< QgsPolygon > poly( new QgsPolygon() );
655 if ( !mExteriorRing )
656 {
657 return poly.release();
658 }
659
660 poly->setExteriorRing( mExteriorRing->curveToLine( tolerance, toleranceType ) );
661
662 QVector<QgsCurve *> rings;
663 rings.reserve( mInteriorRings.size() );
664 for ( const QgsCurve *ring : mInteriorRings )
665 {
666 rings.push_back( ring->curveToLine( tolerance, toleranceType ) );
667 }
668 poly->setInteriorRings( rings );
669 return poly.release();
670}
671
673{
674 if ( !ring )
675 {
676 return;
677 }
678 mExteriorRing.reset( ring );
679
680 //set proper wkb type
682 {
684 }
686 {
688 }
689
690 //match dimensionality for rings
691 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
692 {
693 if ( is3D() )
694 ring->addZValue();
695 else
696 ring->dropZValue();
697
698 if ( isMeasure() )
699 ring->addMValue();
700 else
701 ring->dropMValue();
702 }
703 clearCache();
704}
705
706void QgsCurvePolygon::setInteriorRings( const QVector<QgsCurve *> &rings )
707{
708 qDeleteAll( mInteriorRings );
709 mInteriorRings.clear();
710
711 //add rings one-by-one, so that they can each be converted to the correct type for the CurvePolygon
712 for ( QgsCurve *ring : rings )
713 {
714 addInteriorRing( ring );
715 }
716 clearCache();
717}
718
720{
721 if ( !ring )
722 return;
723
724 //ensure dimensionality of ring matches curve polygon
725 if ( !is3D() )
726 ring->dropZValue();
727 else if ( !ring->is3D() )
728 ring->addZValue();
729
730 if ( !isMeasure() )
731 ring->dropMValue();
732 else if ( !ring->isMeasure() )
733 ring->addMValue();
734
735 mInteriorRings.append( ring );
736 clearCache();
737}
738
740{
741 if ( nr < 0 || nr >= mInteriorRings.size() )
742 {
743 return false;
744 }
745 delete mInteriorRings.takeAt( nr );
746 clearCache();
747 return true;
748}
749
750void QgsCurvePolygon::removeInteriorRings( double minimumAllowedArea )
751{
752 for ( int ringIndex = mInteriorRings.size() - 1; ringIndex >= 0; --ringIndex )
753 {
754 if ( minimumAllowedArea < 0 )
755 delete mInteriorRings.takeAt( ringIndex );
756 else
757 {
758 double area = 0.0;
759 mInteriorRings.at( ringIndex )->sumUpArea( area );
760 if ( std::fabs( area ) < minimumAllowedArea )
761 delete mInteriorRings.takeAt( ringIndex );
762 }
763 }
764
765 clearCache();
766}
767
769{
770 QVector<QgsCurve *> validRings;
771 validRings.reserve( mInteriorRings.size() );
772 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
773 {
774 if ( !curve->isRing() )
775 {
776 // remove invalid rings
777 delete curve;
778 }
779 else
780 {
781 validRings << curve;
782 }
783 }
784 mInteriorRings = validRings;
785}
786
791
793{
795 {
796 // flip exterior ring orientation
797 std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
798 mExteriorRing = std::move( flipped );
799 }
800
801 QVector<QgsCurve *> validRings;
802 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
803 {
804 if ( curve && curve->orientation() != Qgis::AngularDirection::CounterClockwise )
805 {
806 // flip interior ring orientation
807 QgsCurve *flipped = curve->reversed();
808 validRings << flipped;
809 delete curve;
810 }
811 else
812 {
813 validRings << curve;
814 }
815 }
816 mInteriorRings = validRings;
817}
818
820{
822 {
823 // flip exterior ring orientation
824 mExteriorRing.reset( mExteriorRing->reversed() );
825 }
826
827 QVector<QgsCurve *> validRings;
828 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
829 {
830 if ( curve && curve->orientation() != Qgis::AngularDirection::Clockwise )
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{
847 QPainterPath p;
848 if ( mExteriorRing )
849 {
850 QPainterPath ring = mExteriorRing->asQPainterPath();
851 ring.closeSubpath();
852 p.addPath( ring );
853 }
854
855 for ( const QgsCurve *ring : mInteriorRings )
856 {
857 QPainterPath ringPath = ring->asQPainterPath();
858 ringPath.closeSubpath();
859 p.addPath( ringPath );
860 }
861
862 return p;
863}
864
865void QgsCurvePolygon::draw( QPainter &p ) const
866{
867 if ( !mExteriorRing )
868 return;
869
870 if ( mInteriorRings.empty() )
871 {
872 mExteriorRing->drawAsPolygon( p );
873 }
874 else
875 {
876 QPainterPath path;
877 mExteriorRing->addToPainterPath( path );
878
879 for ( const QgsCurve *ring : mInteriorRings )
880 {
881 ring->addToPainterPath( path );
882 }
883 p.drawPath( path );
884 }
885}
886
888{
889 if ( mExteriorRing )
890 {
891 mExteriorRing->transform( ct, d, transformZ );
892 }
893
894 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
895 {
896 curve->transform( ct, d, transformZ );
897 }
898 clearCache();
899}
900
901void QgsCurvePolygon::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
902{
903 if ( mExteriorRing )
904 {
905 mExteriorRing->transform( t, zTranslate, zScale, mTranslate, mScale );
906 }
907
908 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
909 {
910 curve->transform( t, zTranslate, zScale, mTranslate, mScale );
911 }
912 clearCache();
913}
914
916{
917 QgsCoordinateSequence sequence;
918 sequence.append( QgsRingSequence() );
919
920 if ( mExteriorRing )
921 {
922 sequence.back().append( QgsPointSequence() );
923 mExteriorRing->points( sequence.back().back() );
924 }
925
926 for ( const QgsCurve *ring : mInteriorRings )
927 {
928 sequence.back().append( QgsPointSequence() );
929 ring->points( sequence.back().back() );
930 }
931
932 return sequence;
933}
934
936{
937 int count = 0;
938
939 if ( mExteriorRing )
940 {
941 count += mExteriorRing->nCoordinates();
942 }
943
944 for ( const QgsCurve *ring : mInteriorRings )
945 {
946 count += ring->nCoordinates();
947 }
948
949 return count;
950}
951
953{
954 if ( id.part != 0 )
955 return -1;
956
957 if ( id.ring < 0 || id.ring >= ringCount() || !mExteriorRing )
958 return -1;
959
960 int number = 0;
961 if ( id.ring == 0 )
962 {
963 return mExteriorRing->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
964 }
965 else
966 {
967 number += mExteriorRing->numPoints();
968 }
969
970 for ( int i = 0; i < mInteriorRings.count(); ++i )
971 {
972 if ( id.ring == i + 1 )
973 {
974 int partNumber = mInteriorRings.at( i )->vertexNumberFromVertexId( QgsVertexId( 0, 0, id.vertex ) );
975 if ( partNumber == -1 )
976 return -1;
977 return number + partNumber;
978 }
979 else
980 {
981 number += mInteriorRings.at( i )->numPoints();
982 }
983 }
984 return -1; // should not happen
985}
986
988{
989 if ( !mExteriorRing )
990 return true;
991
992 return mExteriorRing->isEmpty();
993}
994
995double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
996{
997 if ( !mExteriorRing )
998 {
999 return -1;
1000 }
1001 QVector<QgsCurve *> segmentList;
1002 segmentList.append( mExteriorRing.get() );
1003 segmentList.append( mInteriorRings );
1004 return QgsGeometryUtils::closestSegmentFromComponents( segmentList, QgsGeometryUtils::Ring, pt, segmentPt, vertexAfter, leftOf, epsilon );
1005}
1006
1008{
1009 if ( !mExteriorRing || vId.ring >= 1 + mInteriorRings.size() )
1010 {
1011 return false;
1012 }
1013
1014 if ( vId.ring < 0 )
1015 {
1016 vId.ring = 0;
1017 vId.vertex = -1;
1018 if ( vId.part < 0 )
1019 {
1020 vId.part = 0;
1021 }
1022 return mExteriorRing->nextVertex( vId, vertex );
1023 }
1024 else
1025 {
1026 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings[vId.ring - 1];
1027
1028 if ( ring->nextVertex( vId, vertex ) )
1029 {
1030 return true;
1031 }
1032 ++vId.ring;
1033 vId.vertex = -1;
1034 if ( vId.ring >= 1 + mInteriorRings.size() )
1035 {
1036 return false;
1037 }
1038 ring = mInteriorRings[ vId.ring - 1 ];
1039 return ring->nextVertex( vId, vertex );
1040 }
1041}
1042
1043void ringAdjacentVertices( const QgsCurve *curve, QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex )
1044{
1045 int n = curve->numPoints();
1046 if ( vertex.vertex < 0 || vertex.vertex >= n )
1047 {
1048 previousVertex = QgsVertexId();
1049 nextVertex = QgsVertexId();
1050 return;
1051 }
1052
1053 if ( vertex.vertex == 0 && n < 3 )
1054 {
1055 previousVertex = QgsVertexId();
1056 }
1057 else if ( vertex.vertex == 0 )
1058 {
1059 previousVertex = QgsVertexId( vertex.part, vertex.ring, n - 2 );
1060 }
1061 else
1062 {
1063 previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
1064 }
1065 if ( vertex.vertex == n - 1 && n < 3 )
1066 {
1067 nextVertex = QgsVertexId();
1068 }
1069 else if ( vertex.vertex == n - 1 )
1070 {
1071 nextVertex = QgsVertexId( vertex.part, vertex.ring, 1 );
1072 }
1073 else
1074 {
1075 nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
1076 }
1077}
1078
1079void QgsCurvePolygon::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
1080{
1081 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1082 {
1083 previousVertex = QgsVertexId();
1085 return;
1086 }
1087
1088 if ( vertex.ring == 0 )
1089 {
1090 ringAdjacentVertices( mExteriorRing.get(), vertex, previousVertex, nextVertex );
1091 }
1092 else
1093 {
1094 ringAdjacentVertices( mInteriorRings.at( vertex.ring - 1 ), vertex, previousVertex, nextVertex );
1095 }
1096}
1097
1099{
1100 if ( !mExteriorRing || vId.ring < 0 || vId.ring >= 1 + mInteriorRings.size() )
1101 {
1102 return false;
1103 }
1104
1105 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( vId.ring - 1 );
1106 int n = ring->numPoints();
1107 bool success = ring->insertVertex( QgsVertexId( 0, 0, vId.vertex ), vertex );
1108 if ( !success )
1109 {
1110 return false;
1111 }
1112
1113 // If first or last vertex is inserted, re-sync the last/first vertex
1114 if ( vId.vertex == 0 )
1115 ring->moveVertex( QgsVertexId( 0, 0, n ), vertex );
1116 else if ( vId.vertex == n )
1117 ring->moveVertex( QgsVertexId( 0, 0, 0 ), vertex );
1118
1119 clearCache();
1120
1121 return true;
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->moveVertex( vId, newPos );
1134 if ( success )
1135 {
1136 // If first or last vertex is moved, also move the last/first vertex
1137 if ( vId.vertex == 0 )
1138 ring->moveVertex( QgsVertexId( vId.part, vId.ring, n - 1 ), newPos );
1139 else if ( vId.vertex == n - 1 )
1140 ring->moveVertex( QgsVertexId( vId.part, vId.ring, 0 ), newPos );
1141 clearCache();
1142 }
1143 return success;
1144}
1145
1147{
1148 const int interiorRingId = vId.ring - 1;
1149 if ( !mExteriorRing || vId.ring < 0 || interiorRingId >= mInteriorRings.size() )
1150 {
1151 return false;
1152 }
1153
1154 // cppcheck-suppress containerOutOfBounds
1155 QgsCurve *ring = vId.ring == 0 ? mExteriorRing.get() : mInteriorRings.at( interiorRingId );
1156 int n = ring->numPoints();
1157 if ( n <= 4 )
1158 {
1159 //no points will be left in ring, so remove whole ring
1160 if ( vId.ring == 0 )
1161 {
1162 mExteriorRing.reset();
1163 if ( !mInteriorRings.isEmpty() )
1164 {
1165 mExteriorRing.reset( mInteriorRings.takeFirst() );
1166 }
1167 }
1168 else
1169 {
1170 removeInteriorRing( vId.ring - 1 );
1171 }
1172 clearCache();
1173 return true;
1174 }
1175
1176 bool success = ring->deleteVertex( vId );
1177 if ( success )
1178 {
1179 // If first or last vertex is removed, re-sync the last/first vertex
1180 // Do not use "n - 2", but "ring->numPoints() - 1" as more than one vertex
1181 // may have been deleted (e.g. with CircularString)
1182 if ( vId.vertex == 0 )
1183 ring->moveVertex( QgsVertexId( 0, 0, ring->numPoints() - 1 ), ring->vertexAt( QgsVertexId( 0, 0, 0 ) ) );
1184 else if ( vId.vertex == n - 1 )
1185 ring->moveVertex( QgsVertexId( 0, 0, 0 ), ring->vertexAt( QgsVertexId( 0, 0, ring->numPoints() - 1 ) ) );
1186 clearCache();
1187 }
1188 return success;
1189}
1190
1192{
1193 if ( mExteriorRing && mExteriorRing->hasCurvedSegments() )
1194 {
1195 return true;
1196 }
1197
1198 for ( const QgsCurve *ring : mInteriorRings )
1199 {
1200 if ( ring->hasCurvedSegments() )
1201 {
1202 return true;
1203 }
1204 }
1205 return false;
1206}
1207
1209{
1210 return toPolygon( tolerance, toleranceType );
1211}
1212
1214{
1215 if ( !mExteriorRing || vertex.ring < 0 || vertex.ring >= 1 + mInteriorRings.size() )
1216 {
1217 //makes no sense - conversion of false to double!
1218 return false;
1219 }
1220
1221 QgsCurve *ring = vertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[vertex.ring - 1];
1222 return ring->vertexAngle( vertex );
1223}
1224
1225int QgsCurvePolygon::vertexCount( int /*part*/, int ring ) const
1226{
1227 return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
1228}
1229
1231{
1232 return ( nullptr != mExteriorRing ) + mInteriorRings.size();
1233}
1234
1236{
1237 return ringCount() > 0 ? 1 : 0;
1238}
1239
1241{
1242 return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
1243}
1244
1246{
1247 if ( !mExteriorRing || startVertex.ring < 0 || startVertex.ring >= 1 + mInteriorRings.size() )
1248 {
1249 return 0.0;
1250 }
1251
1252 const QgsCurve *ring = startVertex.ring == 0 ? mExteriorRing.get() : mInteriorRings[startVertex.ring - 1];
1253 return ring->segmentLength( startVertex );
1254}
1255
1256bool QgsCurvePolygon::addZValue( double zValue )
1257{
1258 if ( QgsWkbTypes::hasZ( mWkbType ) )
1259 return false;
1260
1262
1263 if ( mExteriorRing )
1264 mExteriorRing->addZValue( zValue );
1265 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1266 {
1267 curve->addZValue( zValue );
1268 }
1269 clearCache();
1270 return true;
1271}
1272
1273bool QgsCurvePolygon::addMValue( double mValue )
1274{
1275 if ( QgsWkbTypes::hasM( mWkbType ) )
1276 return false;
1277
1279
1280 if ( mExteriorRing )
1281 mExteriorRing->addMValue( mValue );
1282 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1283 {
1284 curve->addMValue( mValue );
1285 }
1286 clearCache();
1287 return true;
1288}
1289
1291{
1292 if ( !is3D() )
1293 return false;
1294
1296 if ( mExteriorRing )
1297 mExteriorRing->dropZValue();
1298 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1299 {
1300 curve->dropZValue();
1301 }
1302 clearCache();
1303 return true;
1304}
1305
1307{
1308 if ( !isMeasure() )
1309 return false;
1310
1312 if ( mExteriorRing )
1313 mExteriorRing->dropMValue();
1314 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1315 {
1316 curve->dropMValue();
1317 }
1318 clearCache();
1319 return true;
1320}
1321
1323{
1324 if ( mExteriorRing )
1325 mExteriorRing->swapXy();
1326 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1327 {
1328 curve->swapXy();
1329 }
1330 clearCache();
1331}
1332
1334{
1335 return clone();
1336}
1337
1339{
1340 if ( !transformer )
1341 return false;
1342
1343 bool res = true;
1344 if ( mExteriorRing )
1345 res = mExteriorRing->transform( transformer, feedback );
1346
1347 if ( !res || ( feedback && feedback->isCanceled() ) )
1348 {
1349 clearCache();
1350 return false;
1351 }
1352
1353 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1354 {
1355 res = curve->transform( transformer );
1356
1357 if ( feedback && feedback->isCanceled() )
1358 res = false;
1359
1360 if ( !res )
1361 break;
1362 }
1363 clearCache();
1364 return res;
1365}
1366
1367void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
1368{
1369 if ( mExteriorRing )
1370 mExteriorRing->filterVertices( filter );
1371
1372 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1373 {
1374 curve->filterVertices( filter );
1375 }
1376 clearCache();
1377}
1378
1379void QgsCurvePolygon::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
1380{
1381 if ( mExteriorRing )
1382 mExteriorRing->transformVertices( transform );
1383
1384 for ( QgsCurve *curve : std::as_const( mInteriorRings ) )
1385 {
1386 curve->transformVertices( transform );
1387 }
1388 clearCache();
1389}
1390
1392{
1393 return 1 + mInteriorRings.count();
1394}
1395
1397{
1398 if ( index == 0 )
1399 return mExteriorRing.get();
1400 else
1401 return mInteriorRings.at( index - 1 );
1402}
1403
1405{
1406 const QgsCurvePolygon *otherPolygon = qgsgeometry_cast<const QgsCurvePolygon *>( other );
1407 if ( !otherPolygon )
1408 return -1;
1409
1410 if ( mExteriorRing && !otherPolygon->mExteriorRing )
1411 return 1;
1412 else if ( !mExteriorRing && otherPolygon->mExteriorRing )
1413 return -1;
1414 else if ( mExteriorRing && otherPolygon->mExteriorRing )
1415 {
1416 int shellComp = mExteriorRing->compareTo( otherPolygon->mExteriorRing.get() );
1417 if ( shellComp != 0 )
1418 {
1419 return shellComp;
1420 }
1421 }
1422
1423 const int nHole1 = mInteriorRings.size();
1424 const int nHole2 = otherPolygon->mInteriorRings.size();
1425 if ( nHole1 < nHole2 )
1426 {
1427 return -1;
1428 }
1429 if ( nHole1 > nHole2 )
1430 {
1431 return 1;
1432 }
1433
1434 for ( int i = 0; i < nHole1; i++ )
1435 {
1436 const int holeComp = mInteriorRings.at( i )->compareTo( otherPolygon->mInteriorRings.at( i ) );
1437 if ( holeComp != 0 )
1438 {
1439 return holeComp;
1440 }
1441 }
1442
1443 return 0;
1444}
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
VertexType
Types of vertex.
Definition qgis.h:2614
@ Polygon
Polygons.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:201
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ Polygon
Polygon.
@ CircularString
CircularString.
@ CurvePolygon
CurvePolygon.
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2286
An abstract base class for classes which transform geometries by transforming input points to output ...
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
bool isMeasure() const
Returns true if the geometry contains m values.
QFlags< WkbFlag > WkbFlags
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
AxisOrder
Axis order for GML generation.
QString wktTypeStr() const
Returns the WKT type string of the geometry.
QgsAbstractGeometry & operator=(const QgsAbstractGeometry &geom)
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool insertVertex(QgsVertexId position, const QgsPoint &vertex)=0
Inserts a vertex into the geometry.
void setZMTypeFromSubGeometry(const QgsAbstractGeometry *subggeom, Qgis::WkbType baseGeomType)
Updates the geometry type based on whether sub geometries contain z or m values.
virtual bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
virtual double segmentLength(QgsVertexId startVertex) const =0
Returns the length of the segment of the geometry which begins at startVertex.
QgsGeometryConstPartIterator parts() const
Returns Java-style iterator for traversal of parts of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static endian_t endian()
Returns whether this machine uses big or little endian.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp: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.
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...
QgsCurvePolygon * snappedToGrid(double hSpacing, double vSpacing, double dSpacing=0, double mSpacing=0, bool removeRedundantPoints=false) const override
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
bool dropMValue() override
Drops any measure values which exist in the geometry.
QgsAbstractGeometry * childGeometry(int index) const override
Returns pointer to child geometry (for geometries with child geometries - i.e.
json asJsonObject(int precision=17) const override
Returns a json object representation of the geometry.
void setInteriorRings(const QVector< QgsCurve * > &rings)
Sets all interior rings (takes ownership)
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.
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
Definition qgscurve.cpp:70
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition qgscurve.cpp:198
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition qgscurve.cpp:87
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void reserve(int size)
Attempts to allocate memory for at least size geometries.
static json pointsToJson(const QgsPointSequence &points, int precision)
Returns coordinates as json object.
static QStringList wktGetChildBlocks(const QString &wkt, const QString &defaultType=QString())
Parses a WKT string and returns of list of blocks contained in the WKT.
static QPair< Qgis::WkbType, QString > wktReadBlock(const QString &wkt)
Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents (...
static double closestSegmentFromComponents(T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon)
Line string geometry type, with support for z-dimension and m-values.
Multi curve geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition qgspoint.cpp:522
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.
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.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Qgis::WkbType dropZ(Qgis::WkbType type)
Drops the z dimension (if present) for a WKB type and returns the new type.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5445
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