LCOV - code coverage report
Current view: top level - core/geometry - qgscurvepolygon.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 753 805 93.5 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

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

Generated by: LCOV version 1.14