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