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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsinternalgeometryengine.cpp - QgsInternalGeometryEngine
       3                 :            : 
       4                 :            :  ---------------------
       5                 :            :  begin                : 13.1.2016
       6                 :            :  copyright            : (C) 2016 by Matthias Kuhn
       7                 :            :  email                : matthias@opengis.ch
       8                 :            :  ***************************************************************************
       9                 :            :  *                                                                         *
      10                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      11                 :            :  *   it under the terms of the GNU General Public License as published by  *
      12                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      13                 :            :  *   (at your option) any later version.                                   *
      14                 :            :  *                                                                         *
      15                 :            :  ***************************************************************************/
      16                 :            : 
      17                 :            : #include "qgsinternalgeometryengine.h"
      18                 :            : 
      19                 :            : #include "qgslinestring.h"
      20                 :            : #include "qgsmultipolygon.h"
      21                 :            : #include "qgspolygon.h"
      22                 :            : #include "qgsmulticurve.h"
      23                 :            : #include "qgscircularstring.h"
      24                 :            : #include "qgsgeometry.h"
      25                 :            : #include "qgsgeometryutils.h"
      26                 :            : #include "qgslinesegment.h"
      27                 :            : #include "qgscircle.h"
      28                 :            : #include "qgslogger.h"
      29                 :            : #include "qgstessellator.h"
      30                 :            : #include "qgsfeedback.h"
      31                 :            : #include "qgsgeometryengine.h"
      32                 :            : #include <QTransform>
      33                 :            : #include <functional>
      34                 :            : #include <memory>
      35                 :            : #include <queue>
      36                 :            : #include <random>
      37                 :            : 
      38                 :         14 : QgsInternalGeometryEngine::QgsInternalGeometryEngine( const QgsGeometry &geometry )
      39                 :         14 :   : mGeometry( geometry.constGet() )
      40                 :            : {
      41                 :            : 
      42                 :         14 : }
      43                 :            : 
      44                 :          2 : QString QgsInternalGeometryEngine::lastError() const
      45                 :            : {
      46                 :          2 :   return mLastError;
      47                 :            : }
      48                 :            : 
      49                 :            : /***************************************************************************
      50                 :            :  * This class is considered CRITICAL and any change MUST be accompanied with
      51                 :            :  * full unit tests.
      52                 :            :  * See details in QEP #17
      53                 :            :  ****************************************************************************/
      54                 :            : 
      55                 :          0 : QgsGeometry QgsInternalGeometryEngine::extrude( double x, double y ) const
      56                 :            : {
      57                 :          0 :   mLastError.clear();
      58                 :          0 :   QVector<QgsLineString *> linesToProcess;
      59                 :            : 
      60                 :          0 :   const QgsMultiCurve *multiCurve = qgsgeometry_cast< const QgsMultiCurve * >( mGeometry );
      61                 :          0 :   if ( multiCurve )
      62                 :            :   {
      63                 :          0 :     linesToProcess.reserve( multiCurve->partCount() );
      64                 :          0 :     for ( int i = 0; i < multiCurve->partCount(); ++i )
      65                 :            :     {
      66                 :          0 :       linesToProcess << static_cast<QgsLineString *>( multiCurve->geometryN( i )->clone() );
      67                 :          0 :     }
      68                 :          0 :   }
      69                 :            : 
      70                 :          0 :   const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( mGeometry );
      71                 :          0 :   if ( curve )
      72                 :            :   {
      73                 :          0 :     linesToProcess << static_cast<QgsLineString *>( curve->segmentize() );
      74                 :          0 :   }
      75                 :            : 
      76                 :          0 :   std::unique_ptr<QgsMultiPolygon> multipolygon( linesToProcess.size() > 1 ? new QgsMultiPolygon() : nullptr );
      77                 :          0 :   QgsPolygon *polygon = nullptr;
      78                 :            : 
      79                 :          0 :   if ( !linesToProcess.empty() )
      80                 :            :   {
      81                 :          0 :     std::unique_ptr< QgsLineString > secondline;
      82                 :          0 :     for ( QgsLineString *line : std::as_const( linesToProcess ) )
      83                 :            :     {
      84                 :          0 :       QTransform transform = QTransform::fromTranslate( x, y );
      85                 :            : 
      86                 :          0 :       secondline.reset( line->reversed() );
      87                 :          0 :       secondline->transform( transform );
      88                 :            : 
      89                 :          0 :       line->append( secondline.get() );
      90                 :          0 :       line->addVertex( line->pointN( 0 ) );
      91                 :            : 
      92                 :          0 :       polygon = new QgsPolygon();
      93                 :          0 :       polygon->setExteriorRing( line );
      94                 :            : 
      95                 :          0 :       if ( multipolygon )
      96                 :          0 :         multipolygon->addGeometry( polygon );
      97                 :            :     }
      98                 :            : 
      99                 :          0 :     if ( multipolygon )
     100                 :          0 :       return QgsGeometry( multipolygon.release() );
     101                 :            :     else
     102                 :          0 :       return QgsGeometry( polygon );
     103                 :          0 :   }
     104                 :            : 
     105                 :          0 :   return QgsGeometry();
     106                 :          0 : }
     107                 :            : 
     108                 :            : 
     109                 :            : 
     110                 :            : // polylabel implementation
     111                 :            : // ported from the original JavaScript implementation developed by Vladimir Agafonkin
     112                 :            : // originally licensed under the ISC License
     113                 :            : 
     114                 :            : /// @cond PRIVATE
     115                 :            : class Cell
     116                 :            : {
     117                 :            :   public:
     118                 :       2907 :     Cell( double x, double y, double h, const QgsPolygon *polygon )
     119                 :       2907 :       : x( x )
     120                 :       2907 :       , y( y )
     121                 :       2907 :       , h( h )
     122                 :       2907 :       , d( polygon->pointDistanceToBoundary( x, y ) )
     123                 :       2907 :       , max( d + h * M_SQRT2 )
     124                 :       2907 :     {}
     125                 :            : 
     126                 :            :     //! Cell center x
     127                 :            :     double x;
     128                 :            :     //! Cell center y
     129                 :            :     double y;
     130                 :            :     //! Half the cell size
     131                 :            :     double h;
     132                 :            :     //! Distance from cell center to polygon
     133                 :            :     double d;
     134                 :            :     //! Maximum distance to polygon within a cell
     135                 :            :     double max;
     136                 :            : };
     137                 :            : 
     138                 :            : struct GreaterThanByMax
     139                 :            : {
     140                 :      49103 :   bool operator()( const Cell *lhs, const Cell *rhs )
     141                 :            :   {
     142                 :      49103 :     return rhs->max > lhs->max;
     143                 :            :   }
     144                 :            : };
     145                 :            : 
     146                 :          7 : Cell *getCentroidCell( const QgsPolygon *polygon )
     147                 :            : {
     148                 :          7 :   double area = 0;
     149                 :          7 :   double x = 0;
     150                 :          7 :   double y = 0;
     151                 :            : 
     152                 :          7 :   const QgsLineString *exterior = static_cast< const QgsLineString *>( polygon->exteriorRing() );
     153                 :          7 :   int len = exterior->numPoints() - 1; //assume closed
     154                 :       1230 :   for ( int i = 0, j = len - 1; i < len; j = i++ )
     155                 :            :   {
     156                 :       1223 :     double aX = exterior->xAt( i );
     157                 :       1223 :     double aY = exterior->yAt( i );
     158                 :       1223 :     double bX = exterior->xAt( j );
     159                 :       1223 :     double bY = exterior->yAt( j );
     160                 :       1223 :     double f = aX * bY - bX * aY;
     161                 :       1223 :     x += ( aX + bX ) * f;
     162                 :       1223 :     y += ( aY + bY ) * f;
     163                 :       1223 :     area += f * 3;
     164                 :       1223 :   }
     165                 :          7 :   if ( area == 0 )
     166                 :          1 :     return new Cell( exterior->xAt( 0 ), exterior->yAt( 0 ), 0, polygon );
     167                 :            :   else
     168                 :          6 :     return new Cell( x / area, y / area, 0.0, polygon );
     169                 :          7 : }
     170                 :            : 
     171                 :          8 : QgsPoint surfacePoleOfInaccessibility( const QgsSurface *surface, double precision, double &distanceFromBoundary )
     172                 :            : {
     173                 :          8 :   std::unique_ptr< QgsPolygon > segmentizedPoly;
     174                 :          8 :   const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( surface );
     175                 :          8 :   if ( !polygon )
     176                 :            :   {
     177                 :          1 :     segmentizedPoly.reset( static_cast< QgsPolygon *>( surface->segmentize() ) );
     178                 :          1 :     polygon = segmentizedPoly.get();
     179                 :          1 :   }
     180                 :            : 
     181                 :            :   // start with the bounding box
     182                 :          8 :   QgsRectangle bounds = polygon->boundingBox();
     183                 :            : 
     184                 :            :   // initial parameters
     185                 :          8 :   double cellSize = std::min( bounds.width(), bounds.height() );
     186                 :            : 
     187                 :          8 :   if ( qgsDoubleNear( cellSize, 0.0 ) )
     188                 :          1 :     return QgsPoint( bounds.xMinimum(), bounds.yMinimum() );
     189                 :            : 
     190                 :          7 :   double h = cellSize / 2.0;
     191                 :          7 :   std::priority_queue< Cell *, std::vector<Cell *>, GreaterThanByMax > cellQueue;
     192                 :            : 
     193                 :            :   // cover polygon with initial cells
     194                 :         15 :   for ( double x = bounds.xMinimum(); x < bounds.xMaximum(); x += cellSize )
     195                 :            :   {
     196                 :         21 :     for ( double y = bounds.yMinimum(); y < bounds.yMaximum(); y += cellSize )
     197                 :            :     {
     198                 :         13 :       cellQueue.push( new Cell( x + h, y + h, h, polygon ) );
     199                 :         13 :     }
     200                 :          8 :   }
     201                 :            : 
     202                 :            :   // take centroid as the first best guess
     203                 :          7 :   std::unique_ptr< Cell > bestCell( getCentroidCell( polygon ) );
     204                 :            : 
     205                 :            :   // special case for rectangular polygons
     206                 :         14 :   std::unique_ptr< Cell > bboxCell( new Cell( bounds.xMinimum() + bounds.width() / 2.0,
     207                 :          7 :                                     bounds.yMinimum() + bounds.height() / 2.0,
     208                 :          7 :                                     0, polygon ) );
     209                 :          7 :   if ( bboxCell->d > bestCell->d )
     210                 :            :   {
     211                 :          1 :     bestCell = std::move( bboxCell );
     212                 :          1 :   }
     213                 :            : 
     214                 :       2900 :   while ( !cellQueue.empty() )
     215                 :            :   {
     216                 :            :     // pick the most promising cell from the queue
     217                 :       2893 :     std::unique_ptr< Cell > cell( cellQueue.top() );
     218                 :       2893 :     cellQueue.pop();
     219                 :       2893 :     Cell *currentCell = cell.get();
     220                 :            : 
     221                 :            :     // update the best cell if we found a better one
     222                 :       2893 :     if ( currentCell->d > bestCell->d )
     223                 :            :     {
     224                 :         22 :       bestCell = std::move( cell );
     225                 :         22 :     }
     226                 :            : 
     227                 :            :     // do not drill down further if there's no chance of a better solution
     228                 :       2893 :     if ( currentCell->max - bestCell->d <= precision )
     229                 :       2173 :       continue;
     230                 :            : 
     231                 :            :     // split the cell into four cells
     232                 :        720 :     h = currentCell->h / 2.0;
     233                 :        720 :     cellQueue.push( new Cell( currentCell->x - h, currentCell->y - h, h, polygon ) );
     234                 :        720 :     cellQueue.push( new Cell( currentCell->x + h, currentCell->y - h, h, polygon ) );
     235                 :        720 :     cellQueue.push( new Cell( currentCell->x - h, currentCell->y + h, h, polygon ) );
     236                 :        720 :     cellQueue.push( new Cell( currentCell->x + h, currentCell->y + h, h, polygon ) );
     237                 :       2893 :   }
     238                 :            : 
     239                 :          7 :   distanceFromBoundary = bestCell->d;
     240                 :            : 
     241                 :          7 :   return QgsPoint( bestCell->x, bestCell->y );
     242                 :          8 : }
     243                 :            : 
     244                 :            : ///@endcond
     245                 :            : 
     246                 :         11 : QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision, double *distanceFromBoundary ) const
     247                 :            : {
     248                 :         11 :   mLastError.clear();
     249                 :         11 :   if ( distanceFromBoundary )
     250                 :          2 :     *distanceFromBoundary = std::numeric_limits<double>::max();
     251                 :            : 
     252                 :         11 :   if ( !mGeometry || mGeometry->isEmpty() )
     253                 :          1 :     return QgsGeometry();
     254                 :            : 
     255                 :         10 :   if ( precision <= 0 )
     256                 :          2 :     return QgsGeometry();
     257                 :            : 
     258                 :          8 :   if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
     259                 :            :   {
     260                 :          1 :     int numGeom = gc->numGeometries();
     261                 :          1 :     double maxDist = 0;
     262                 :          1 :     QgsPoint bestPoint;
     263                 :          3 :     for ( int i = 0; i < numGeom; ++i )
     264                 :            :     {
     265                 :          2 :       const QgsSurface *surface = qgsgeometry_cast< const QgsSurface * >( gc->geometryN( i ) );
     266                 :          2 :       if ( !surface )
     267                 :          0 :         continue;
     268                 :            : 
     269                 :          2 :       double dist = std::numeric_limits<double>::max();
     270                 :          2 :       QgsPoint p = surfacePoleOfInaccessibility( surface, precision, dist );
     271                 :          2 :       if ( dist > maxDist )
     272                 :            :       {
     273                 :          2 :         maxDist = dist;
     274                 :          2 :         bestPoint = p;
     275                 :          2 :       }
     276                 :          2 :     }
     277                 :            : 
     278                 :          1 :     if ( bestPoint.isEmpty() )
     279                 :          0 :       return QgsGeometry();
     280                 :            : 
     281                 :          1 :     if ( distanceFromBoundary )
     282                 :          1 :       *distanceFromBoundary = maxDist;
     283                 :          1 :     return QgsGeometry( new QgsPoint( bestPoint ) );
     284                 :          1 :   }
     285                 :            :   else
     286                 :            :   {
     287                 :          7 :     const QgsSurface *surface = qgsgeometry_cast< const QgsSurface * >( mGeometry );
     288                 :          7 :     if ( !surface )
     289                 :          1 :       return QgsGeometry();
     290                 :            : 
     291                 :          6 :     double dist = std::numeric_limits<double>::max();
     292                 :          6 :     QgsPoint p = surfacePoleOfInaccessibility( surface, precision, dist );
     293                 :          6 :     if ( p.isEmpty() )
     294                 :          0 :       return QgsGeometry();
     295                 :            : 
     296                 :          6 :     if ( distanceFromBoundary )
     297                 :          1 :       *distanceFromBoundary = dist;
     298                 :          6 :     return QgsGeometry( new QgsPoint( p ) );
     299                 :          6 :   }
     300                 :         11 : }
     301                 :            : 
     302                 :            : 
     303                 :            : // helpers for orthogonalize
     304                 :            : // adapted from original code in potlatch/id osm editor
     305                 :            : 
     306                 :          0 : bool dotProductWithinAngleTolerance( double dotProduct, double lowerThreshold, double upperThreshold )
     307                 :            : {
     308                 :          0 :   return lowerThreshold > std::fabs( dotProduct ) || std::fabs( dotProduct ) > upperThreshold;
     309                 :            : }
     310                 :            : 
     311                 :          0 : double normalizedDotProduct( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c )
     312                 :            : {
     313                 :          0 :   QgsVector p = a - b;
     314                 :          0 :   QgsVector q = c - b;
     315                 :            : 
     316                 :          0 :   if ( p.length() > 0 )
     317                 :          0 :     p = p.normalized();
     318                 :          0 :   if ( q.length() > 0 )
     319                 :          0 :     q = q.normalized();
     320                 :            : 
     321                 :          0 :   return p * q;
     322                 :            : }
     323                 :            : 
     324                 :          0 : double squareness( QgsLineString *ring, double lowerThreshold, double upperThreshold )
     325                 :            : {
     326                 :          0 :   double sum = 0.0;
     327                 :            : 
     328                 :          0 :   bool isClosed = ring->isClosed();
     329                 :          0 :   int numPoints = ring->numPoints();
     330                 :          0 :   QgsPoint a;
     331                 :          0 :   QgsPoint b;
     332                 :          0 :   QgsPoint c;
     333                 :            : 
     334                 :          0 :   for ( int i = 0; i < numPoints; ++i )
     335                 :            :   {
     336                 :          0 :     if ( !isClosed && i == numPoints - 1 )
     337                 :          0 :       break;
     338                 :          0 :     else if ( !isClosed && i == 0 )
     339                 :            :     {
     340                 :          0 :       b = ring->pointN( 0 );
     341                 :          0 :       c = ring->pointN( 1 );
     342                 :          0 :     }
     343                 :            :     else
     344                 :            :     {
     345                 :          0 :       if ( i == 0 )
     346                 :            :       {
     347                 :          0 :         a = ring->pointN( numPoints - 1 );
     348                 :          0 :         b = ring->pointN( 0 );
     349                 :          0 :       }
     350                 :          0 :       if ( i == numPoints - 1 )
     351                 :          0 :         c = ring->pointN( 0 );
     352                 :            :       else
     353                 :          0 :         c = ring->pointN( i + 1 );
     354                 :            : 
     355                 :          0 :       double dotProduct = normalizedDotProduct( a, b, c );
     356                 :          0 :       if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) )
     357                 :          0 :         continue;
     358                 :            : 
     359                 :          0 :       sum += 2.0 * std::min( std::fabs( dotProduct - 1.0 ), std::min( std::fabs( dotProduct ), std::fabs( dotProduct + 1 ) ) );
     360                 :            :     }
     361                 :          0 :     a = b;
     362                 :          0 :     b = c;
     363                 :          0 :   }
     364                 :            : 
     365                 :          0 :   return sum;
     366                 :          0 : }
     367                 :            : 
     368                 :          0 : QgsVector calcMotion( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c,
     369                 :            :                       double lowerThreshold, double upperThreshold )
     370                 :            : {
     371                 :          0 :   QgsVector p = a - b;
     372                 :          0 :   QgsVector q = c - b;
     373                 :            : 
     374                 :          0 :   if ( qgsDoubleNear( p.length(), 0.0 ) || qgsDoubleNear( q.length(), 0.0 ) )
     375                 :          0 :     return QgsVector( 0, 0 );
     376                 :            : 
     377                 :            :   // 2.0 is a magic number from the original JOSM source code
     378                 :          0 :   double scale = 2.0 * std::min( p.length(), q.length() );
     379                 :            : 
     380                 :          0 :   p = p.normalized();
     381                 :          0 :   q = q.normalized();
     382                 :          0 :   double dotProduct = p * q;
     383                 :            : 
     384                 :          0 :   if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) )
     385                 :            :   {
     386                 :          0 :     return QgsVector( 0, 0 );
     387                 :            :   }
     388                 :            : 
     389                 :            :   // wonderful nasty hack which has survived through JOSM -> id -> QGIS
     390                 :            :   // to deal with almost-straight segments (angle is closer to 180 than to 90/270).
     391                 :          0 :   if ( dotProduct < -M_SQRT1_2 )
     392                 :          0 :     dotProduct += 1.0;
     393                 :            : 
     394                 :          0 :   QgsVector new_v = p + q;
     395                 :            :   // 0.1 magic number from JOSM implementation - think this is to limit each iterative step
     396                 :          0 :   return new_v.normalized() * ( 0.1 * dotProduct * scale );
     397                 :          0 : }
     398                 :            : 
     399                 :          0 : QgsLineString *doOrthogonalize( QgsLineString *ring, int iterations, double tolerance, double lowerThreshold, double upperThreshold )
     400                 :            : {
     401                 :          0 :   double minScore = std::numeric_limits<double>::max();
     402                 :            : 
     403                 :          0 :   bool isClosed = ring->isClosed();
     404                 :          0 :   int numPoints = ring->numPoints();
     405                 :            : 
     406                 :          0 :   std::unique_ptr< QgsLineString > best( ring->clone() );
     407                 :            : 
     408                 :          0 :   QVector< QgsVector > /* yep */ motions;
     409                 :          0 :   motions.reserve( numPoints );
     410                 :            : 
     411                 :          0 :   for ( int it = 0; it < iterations; ++it )
     412                 :            :   {
     413                 :          0 :     motions.resize( 0 ); // avoid re-allocations
     414                 :            : 
     415                 :            :     // first loop through an calculate all motions
     416                 :          0 :     QgsPoint a;
     417                 :          0 :     QgsPoint b;
     418                 :          0 :     QgsPoint c;
     419                 :          0 :     for ( int i = 0; i < numPoints; ++i )
     420                 :            :     {
     421                 :          0 :       if ( isClosed && i == numPoints - 1 )
     422                 :          0 :         motions << motions.at( 0 ); //closed ring, so last point follows first point motion
     423                 :          0 :       else if ( !isClosed && ( i == 0 || i == numPoints - 1 ) )
     424                 :            :       {
     425                 :          0 :         b = ring->pointN( 0 );
     426                 :          0 :         c = ring->pointN( 1 );
     427                 :          0 :         motions << QgsVector( 0, 0 ); //non closed line, leave first/last vertex untouched
     428                 :          0 :       }
     429                 :            :       else
     430                 :            :       {
     431                 :          0 :         if ( i == 0 )
     432                 :            :         {
     433                 :          0 :           a = ring->pointN( numPoints - 1 );
     434                 :          0 :           b = ring->pointN( 0 );
     435                 :          0 :         }
     436                 :          0 :         if ( i == numPoints - 1 )
     437                 :          0 :           c = ring->pointN( 0 );
     438                 :            :         else
     439                 :          0 :           c = ring->pointN( i + 1 );
     440                 :            : 
     441                 :          0 :         motions << calcMotion( a, b, c, lowerThreshold, upperThreshold );
     442                 :            :       }
     443                 :          0 :       a = b;
     444                 :          0 :       b = c;
     445                 :          0 :     }
     446                 :            : 
     447                 :            :     // then apply them
     448                 :          0 :     for ( int i = 0; i < numPoints; ++i )
     449                 :            :     {
     450                 :          0 :       ring->setXAt( i, ring->xAt( i ) + motions.at( i ).x() );
     451                 :          0 :       ring->setYAt( i, ring->yAt( i ) + motions.at( i ).y() );
     452                 :          0 :     }
     453                 :            : 
     454                 :          0 :     double newScore = squareness( ring, lowerThreshold, upperThreshold );
     455                 :          0 :     if ( newScore < minScore )
     456                 :            :     {
     457                 :          0 :       best.reset( ring->clone() );
     458                 :          0 :       minScore = newScore;
     459                 :          0 :     }
     460                 :            : 
     461                 :          0 :     if ( minScore < tolerance )
     462                 :          0 :       break;
     463                 :          0 :   }
     464                 :            : 
     465                 :          0 :   delete ring;
     466                 :            : 
     467                 :          0 :   return best.release();
     468                 :          0 : }
     469                 :            : 
     470                 :            : 
     471                 :          0 : QgsAbstractGeometry *orthogonalizeGeom( const QgsAbstractGeometry *geom, int maxIterations, double tolerance, double lowerThreshold, double upperThreshold )
     472                 :            : {
     473                 :          0 :   std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
     474                 :          0 :   if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
     475                 :            :   {
     476                 :          0 :     segmentizedCopy.reset( geom->segmentize() );
     477                 :          0 :     geom = segmentizedCopy.get();
     478                 :          0 :   }
     479                 :            : 
     480                 :          0 :   if ( QgsWkbTypes::geometryType( geom->wkbType() ) == QgsWkbTypes::LineGeometry )
     481                 :            :   {
     482                 :          0 :     return doOrthogonalize( static_cast< QgsLineString * >( geom->clone() ),
     483                 :          0 :                             maxIterations, tolerance, lowerThreshold, upperThreshold );
     484                 :            :   }
     485                 :            :   else
     486                 :            :   {
     487                 :            :     // polygon
     488                 :          0 :     const QgsPolygon *polygon = static_cast< const QgsPolygon * >( geom );
     489                 :          0 :     QgsPolygon *result = new QgsPolygon();
     490                 :            : 
     491                 :          0 :     result->setExteriorRing( doOrthogonalize( static_cast< QgsLineString * >( polygon->exteriorRing()->clone() ),
     492                 :          0 :                              maxIterations, tolerance, lowerThreshold, upperThreshold ) );
     493                 :          0 :     for ( int i = 0; i < polygon->numInteriorRings(); ++i )
     494                 :            :     {
     495                 :          0 :       result->addInteriorRing( doOrthogonalize( static_cast< QgsLineString * >( polygon->interiorRing( i )->clone() ),
     496                 :          0 :                                maxIterations, tolerance, lowerThreshold, upperThreshold ) );
     497                 :          0 :     }
     498                 :            : 
     499                 :          0 :     return result;
     500                 :            :   }
     501                 :          0 : }
     502                 :            : 
     503                 :          0 : QgsGeometry QgsInternalGeometryEngine::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
     504                 :            : {
     505                 :          0 :   mLastError.clear();
     506                 :          0 :   if ( !mGeometry || ( QgsWkbTypes::geometryType( mGeometry->wkbType() ) != QgsWkbTypes::LineGeometry
     507                 :          0 :                        && QgsWkbTypes::geometryType( mGeometry->wkbType() ) != QgsWkbTypes::PolygonGeometry ) )
     508                 :            :   {
     509                 :          0 :     return QgsGeometry();
     510                 :            :   }
     511                 :            : 
     512                 :          0 :   double lowerThreshold = std::cos( ( 90 - angleThreshold ) * M_PI / 180.00 );
     513                 :          0 :   double upperThreshold = std::cos( angleThreshold * M_PI / 180.0 );
     514                 :            : 
     515                 :          0 :   if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
     516                 :            :   {
     517                 :          0 :     int numGeom = gc->numGeometries();
     518                 :          0 :     QVector< QgsAbstractGeometry * > geometryList;
     519                 :          0 :     geometryList.reserve( numGeom );
     520                 :          0 :     for ( int i = 0; i < numGeom; ++i )
     521                 :            :     {
     522                 :          0 :       geometryList << orthogonalizeGeom( gc->geometryN( i ), maxIterations, tolerance, lowerThreshold, upperThreshold );
     523                 :          0 :     }
     524                 :            : 
     525                 :          0 :     QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
     526                 :          0 :     for ( QgsAbstractGeometry *g : std::as_const( geometryList ) )
     527                 :            :     {
     528                 :          0 :       first.addPart( g );
     529                 :            :     }
     530                 :          0 :     return first;
     531                 :          0 :   }
     532                 :            :   else
     533                 :            :   {
     534                 :          0 :     return QgsGeometry( orthogonalizeGeom( mGeometry, maxIterations, tolerance, lowerThreshold, upperThreshold ) );
     535                 :            :   }
     536                 :          0 : }
     537                 :            : 
     538                 :            : // if extraNodesPerSegment < 0, then use distance based mode
     539                 :          0 : QgsLineString *doDensify( const QgsLineString *ring, int extraNodesPerSegment = -1, double distance = 1 )
     540                 :            : {
     541                 :          0 :   QVector< double > outX;
     542                 :          0 :   QVector< double > outY;
     543                 :          0 :   QVector< double > outZ;
     544                 :          0 :   QVector< double > outM;
     545                 :          0 :   double multiplier = 1.0 / double( extraNodesPerSegment + 1 );
     546                 :            : 
     547                 :          0 :   int nPoints = ring->numPoints();
     548                 :          0 :   outX.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
     549                 :          0 :   outY.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
     550                 :          0 :   bool withZ = ring->is3D();
     551                 :          0 :   if ( withZ )
     552                 :          0 :     outZ.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
     553                 :          0 :   bool withM = ring->isMeasure();
     554                 :          0 :   if ( withM )
     555                 :          0 :     outM.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
     556                 :          0 :   double x1 = 0;
     557                 :          0 :   double x2 = 0;
     558                 :          0 :   double y1 = 0;
     559                 :          0 :   double y2 = 0;
     560                 :          0 :   double z1 = 0;
     561                 :          0 :   double z2 = 0;
     562                 :          0 :   double m1 = 0;
     563                 :          0 :   double m2 = 0;
     564                 :          0 :   double xOut = 0;
     565                 :          0 :   double yOut = 0;
     566                 :          0 :   double zOut = 0;
     567                 :          0 :   double mOut = 0;
     568                 :          0 :   int extraNodesThisSegment = extraNodesPerSegment;
     569                 :          0 :   for ( int i = 0; i < nPoints - 1; ++i )
     570                 :            :   {
     571                 :          0 :     x1 = ring->xAt( i );
     572                 :          0 :     x2 = ring->xAt( i + 1 );
     573                 :          0 :     y1 = ring->yAt( i );
     574                 :          0 :     y2 = ring->yAt( i + 1 );
     575                 :          0 :     if ( withZ )
     576                 :            :     {
     577                 :          0 :       z1 = ring->zAt( i );
     578                 :          0 :       z2 = ring->zAt( i + 1 );
     579                 :          0 :     }
     580                 :          0 :     if ( withM )
     581                 :            :     {
     582                 :          0 :       m1 = ring->mAt( i );
     583                 :          0 :       m2 = ring->mAt( i + 1 );
     584                 :          0 :     }
     585                 :            : 
     586                 :          0 :     outX << x1;
     587                 :          0 :     outY << y1;
     588                 :          0 :     if ( withZ )
     589                 :          0 :       outZ << z1;
     590                 :          0 :     if ( withM )
     591                 :          0 :       outM << m1;
     592                 :            : 
     593                 :          0 :     if ( extraNodesPerSegment < 0 )
     594                 :            :     {
     595                 :            :       // distance mode
     596                 :          0 :       extraNodesThisSegment = std::floor( std::sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ) / distance );
     597                 :          0 :       if ( extraNodesThisSegment >= 1 )
     598                 :          0 :         multiplier = 1.0 / ( extraNodesThisSegment + 1 );
     599                 :          0 :     }
     600                 :            : 
     601                 :          0 :     for ( int j = 0; j < extraNodesThisSegment; ++j )
     602                 :            :     {
     603                 :          0 :       double delta = multiplier * ( j + 1 );
     604                 :          0 :       xOut = x1 + delta * ( x2 - x1 );
     605                 :          0 :       yOut = y1 + delta * ( y2 - y1 );
     606                 :          0 :       if ( withZ )
     607                 :          0 :         zOut = z1 + delta * ( z2 - z1 );
     608                 :          0 :       if ( withM )
     609                 :          0 :         mOut = m1 + delta * ( m2 - m1 );
     610                 :            : 
     611                 :          0 :       outX << xOut;
     612                 :          0 :       outY << yOut;
     613                 :          0 :       if ( withZ )
     614                 :          0 :         outZ << zOut;
     615                 :          0 :       if ( withM )
     616                 :          0 :         outM << mOut;
     617                 :          0 :     }
     618                 :          0 :   }
     619                 :          0 :   outX << ring->xAt( nPoints - 1 );
     620                 :          0 :   outY << ring->yAt( nPoints - 1 );
     621                 :          0 :   if ( withZ )
     622                 :          0 :     outZ << ring->zAt( nPoints - 1 );
     623                 :          0 :   if ( withM )
     624                 :          0 :     outM << ring->mAt( nPoints - 1 );
     625                 :            : 
     626                 :          0 :   QgsLineString *result = new QgsLineString( outX, outY, outZ, outM );
     627                 :          0 :   return result;
     628                 :          0 : }
     629                 :            : 
     630                 :          0 : QgsAbstractGeometry *densifyGeometry( const QgsAbstractGeometry *geom, int extraNodesPerSegment = 1, double distance = 1 )
     631                 :            : {
     632                 :          0 :   std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
     633                 :          0 :   if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
     634                 :            :   {
     635                 :          0 :     segmentizedCopy.reset( geom->segmentize() );
     636                 :          0 :     geom = segmentizedCopy.get();
     637                 :          0 :   }
     638                 :            : 
     639                 :          0 :   if ( QgsWkbTypes::geometryType( geom->wkbType() ) == QgsWkbTypes::LineGeometry )
     640                 :            :   {
     641                 :          0 :     return doDensify( static_cast< const QgsLineString * >( geom ), extraNodesPerSegment, distance );
     642                 :            :   }
     643                 :            :   else
     644                 :            :   {
     645                 :            :     // polygon
     646                 :          0 :     const QgsPolygon *polygon = static_cast< const QgsPolygon * >( geom );
     647                 :          0 :     QgsPolygon *result = new QgsPolygon();
     648                 :            : 
     649                 :          0 :     result->setExteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->exteriorRing() ),
     650                 :          0 :                                         extraNodesPerSegment, distance ) );
     651                 :          0 :     for ( int i = 0; i < polygon->numInteriorRings(); ++i )
     652                 :            :     {
     653                 :          0 :       result->addInteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->interiorRing( i ) ),
     654                 :          0 :                                           extraNodesPerSegment, distance ) );
     655                 :          0 :     }
     656                 :            : 
     657                 :          0 :     return result;
     658                 :            :   }
     659                 :          0 : }
     660                 :            : 
     661                 :          0 : QgsGeometry QgsInternalGeometryEngine::densifyByCount( int extraNodesPerSegment ) const
     662                 :            : {
     663                 :          0 :   mLastError.clear();
     664                 :          0 :   if ( !mGeometry )
     665                 :            :   {
     666                 :          0 :     return QgsGeometry();
     667                 :            :   }
     668                 :            : 
     669                 :          0 :   if ( QgsWkbTypes::geometryType( mGeometry->wkbType() ) == QgsWkbTypes::PointGeometry )
     670                 :            :   {
     671                 :          0 :     return QgsGeometry( mGeometry->clone() ); // point geometry, nothing to do
     672                 :            :   }
     673                 :            : 
     674                 :          0 :   if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
     675                 :            :   {
     676                 :          0 :     int numGeom = gc->numGeometries();
     677                 :          0 :     QVector< QgsAbstractGeometry * > geometryList;
     678                 :          0 :     geometryList.reserve( numGeom );
     679                 :          0 :     for ( int i = 0; i < numGeom; ++i )
     680                 :            :     {
     681                 :          0 :       geometryList << densifyGeometry( gc->geometryN( i ), extraNodesPerSegment );
     682                 :          0 :     }
     683                 :            : 
     684                 :          0 :     QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
     685                 :          0 :     for ( QgsAbstractGeometry *g : std::as_const( geometryList ) )
     686                 :            :     {
     687                 :          0 :       first.addPart( g );
     688                 :            :     }
     689                 :          0 :     return first;
     690                 :          0 :   }
     691                 :            :   else
     692                 :            :   {
     693                 :          0 :     return QgsGeometry( densifyGeometry( mGeometry, extraNodesPerSegment ) );
     694                 :            :   }
     695                 :          0 : }
     696                 :            : 
     697                 :          0 : QgsGeometry QgsInternalGeometryEngine::densifyByDistance( double distance ) const
     698                 :            : {
     699                 :          0 :   mLastError.clear();
     700                 :          0 :   if ( !mGeometry )
     701                 :            :   {
     702                 :          0 :     return QgsGeometry();
     703                 :            :   }
     704                 :            : 
     705                 :          0 :   if ( QgsWkbTypes::geometryType( mGeometry->wkbType() ) == QgsWkbTypes::PointGeometry )
     706                 :            :   {
     707                 :          0 :     return QgsGeometry( mGeometry->clone() ); // point geometry, nothing to do
     708                 :            :   }
     709                 :            : 
     710                 :          0 :   if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
     711                 :            :   {
     712                 :          0 :     int numGeom = gc->numGeometries();
     713                 :          0 :     QVector< QgsAbstractGeometry * > geometryList;
     714                 :          0 :     geometryList.reserve( numGeom );
     715                 :          0 :     for ( int i = 0; i < numGeom; ++i )
     716                 :            :     {
     717                 :          0 :       geometryList << densifyGeometry( gc->geometryN( i ), -1, distance );
     718                 :          0 :     }
     719                 :            : 
     720                 :          0 :     QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
     721                 :          0 :     for ( QgsAbstractGeometry *g : std::as_const( geometryList ) )
     722                 :            :     {
     723                 :          0 :       first.addPart( g );
     724                 :            :     }
     725                 :          0 :     return first;
     726                 :          0 :   }
     727                 :            :   else
     728                 :            :   {
     729                 :          0 :     return QgsGeometry( densifyGeometry( mGeometry, -1, distance ) );
     730                 :            :   }
     731                 :          0 : }
     732                 :            : 
     733                 :            : ///@cond PRIVATE
     734                 :            : //
     735                 :            : // QgsLineSegmentDistanceComparer
     736                 :            : //
     737                 :            : 
     738                 :            : // adapted for QGIS geometry classes from original work at https://github.com/trylock/visibility by trylock
     739                 :          0 : bool QgsLineSegmentDistanceComparer::operator()( QgsLineSegment2D ab, QgsLineSegment2D cd ) const
     740                 :            : {
     741                 :            :   Q_ASSERT_X( ab.pointLeftOfLine( mOrigin ) != 0,
     742                 :            :               "line_segment_dist_comparer",
     743                 :            :               "AB must not be collinear with the origin." );
     744                 :            :   Q_ASSERT_X( cd.pointLeftOfLine( mOrigin ) != 0,
     745                 :            :               "line_segment_dist_comparer",
     746                 :            :               "CD must not be collinear with the origin." );
     747                 :            : 
     748                 :            :   // flip the segments so that if there are common endpoints,
     749                 :            :   // they will be the segment's start points
     750                 :          0 :   if ( ab.end() == cd.start() || ab.end() == cd.end() )
     751                 :          0 :     ab.reverse();
     752                 :          0 :   if ( ab.start() == cd.end() )
     753                 :          0 :     cd.reverse();
     754                 :            : 
     755                 :            :   // cases with common endpoints
     756                 :          0 :   if ( ab.start() == cd.start() )
     757                 :            :   {
     758                 :          0 :     const int oad = QgsGeometryUtils::leftOfLine( cd.endX(), cd.endY(), mOrigin.x(), mOrigin.y(), ab.startX(), ab.startY() );
     759                 :          0 :     const int oab = ab.pointLeftOfLine( mOrigin );
     760                 :          0 :     if ( ab.end() == cd.end() || oad != oab )
     761                 :          0 :       return false;
     762                 :            :     else
     763                 :          0 :       return ab.pointLeftOfLine( cd.end() ) != oab;
     764                 :            :   }
     765                 :            :   else
     766                 :            :   {
     767                 :            :     // cases without common endpoints
     768                 :          0 :     const int cda = cd.pointLeftOfLine( ab.start() );
     769                 :          0 :     const int cdb = cd.pointLeftOfLine( ab.end() );
     770                 :          0 :     if ( cdb == 0 && cda == 0 )
     771                 :            :     {
     772                 :          0 :       return mOrigin.sqrDist( ab.start() ) < mOrigin.sqrDist( cd.start() );
     773                 :            :     }
     774                 :          0 :     else if ( cda == cdb || cda == 0 || cdb == 0 )
     775                 :            :     {
     776                 :          0 :       const int cdo = cd.pointLeftOfLine( mOrigin );
     777                 :          0 :       return cdo == cda || cdo == cdb;
     778                 :            :     }
     779                 :            :     else
     780                 :            :     {
     781                 :          0 :       const int abo = ab.pointLeftOfLine( mOrigin );
     782                 :          0 :       return abo != ab.pointLeftOfLine( cd.start() );
     783                 :            :     }
     784                 :            :   }
     785                 :          0 : }
     786                 :            : 
     787                 :            : //
     788                 :            : // QgsClockwiseAngleComparer
     789                 :            : //
     790                 :            : 
     791                 :          0 : bool QgsClockwiseAngleComparer::operator()( const QgsPointXY &a, const QgsPointXY &b ) const
     792                 :            : {
     793                 :          0 :   const bool aIsLeft = a.x() < mVertex.x();
     794                 :          0 :   const bool bIsLeft = b.x() < mVertex.x();
     795                 :          0 :   if ( aIsLeft != bIsLeft )
     796                 :          0 :     return bIsLeft;
     797                 :            : 
     798                 :          0 :   if ( qgsDoubleNear( a.x(), mVertex.x() ) && qgsDoubleNear( b.x(), mVertex.x() ) )
     799                 :            :   {
     800                 :          0 :     if ( a.y() >= mVertex.y() || b.y() >= mVertex.y() )
     801                 :            :     {
     802                 :          0 :       return b.y() < a.y();
     803                 :            :     }
     804                 :            :     else
     805                 :            :     {
     806                 :          0 :       return a.y() < b.y();
     807                 :            :     }
     808                 :            :   }
     809                 :            :   else
     810                 :            :   {
     811                 :          0 :     const QgsVector oa = a - mVertex;
     812                 :          0 :     const QgsVector ob = b - mVertex;
     813                 :          0 :     const double det = oa.crossProduct( ob );
     814                 :          0 :     if ( qgsDoubleNear( det, 0.0 ) )
     815                 :            :     {
     816                 :          0 :       return oa.lengthSquared() < ob.lengthSquared();
     817                 :            :     }
     818                 :            :     else
     819                 :            :     {
     820                 :          0 :       return det < 0;
     821                 :            :     }
     822                 :            :   }
     823                 :          0 : }
     824                 :            : 
     825                 :            : ///@endcond PRIVATE
     826                 :            : 
     827                 :            : //
     828                 :            : // QgsRay2D
     829                 :            : //
     830                 :            : 
     831                 :          0 : bool QgsRay2D::intersects( const QgsLineSegment2D &segment, QgsPointXY &intersectPoint ) const
     832                 :            : {
     833                 :          0 :   const QgsVector ao = origin - segment.start();
     834                 :          0 :   const QgsVector ab = segment.end() - segment.start();
     835                 :          0 :   const double det = ab.crossProduct( direction );
     836                 :          0 :   if ( qgsDoubleNear( det, 0.0 ) )
     837                 :            :   {
     838                 :          0 :     const int abo = segment.pointLeftOfLine( origin );
     839                 :          0 :     if ( abo != 0 )
     840                 :            :     {
     841                 :          0 :       return false;
     842                 :            :     }
     843                 :            :     else
     844                 :            :     {
     845                 :          0 :       const double distA = ao * direction;
     846                 :          0 :       const double distB = ( origin - segment.end() ) * direction;
     847                 :            : 
     848                 :          0 :       if ( distA > 0 && distB > 0 )
     849                 :            :       {
     850                 :          0 :         return false;
     851                 :            :       }
     852                 :            :       else
     853                 :            :       {
     854                 :          0 :         if ( ( distA > 0 ) != ( distB > 0 ) )
     855                 :          0 :           intersectPoint = origin;
     856                 :          0 :         else if ( distA > distB ) // at this point, both distances are negative
     857                 :          0 :           intersectPoint = segment.start(); // hence the nearest point is A
     858                 :            :         else
     859                 :          0 :           intersectPoint = segment.end();
     860                 :          0 :         return true;
     861                 :            :       }
     862                 :            :     }
     863                 :            :   }
     864                 :            :   else
     865                 :            :   {
     866                 :          0 :     const double u = ao.crossProduct( direction ) / det;
     867                 :          0 :     if ( u < 0.0 || 1.0 < u )
     868                 :            :     {
     869                 :          0 :       return false;
     870                 :            :     }
     871                 :            :     else
     872                 :            :     {
     873                 :          0 :       const double t = -ab.crossProduct( ao ) / det;
     874                 :          0 :       intersectPoint = origin + direction * t;
     875                 :          0 :       return qgsDoubleNear( t, 0.0 ) || t > 0;
     876                 :            :     }
     877                 :            :   }
     878                 :          0 : }
     879                 :            : 
     880                 :          0 : QVector<QgsPointXY> generateSegmentCurve( const QgsPoint &center1, const double radius1, const QgsPoint &center2, const double radius2 )
     881                 :            : {
     882                 :            :   // ensure that first circle is smaller than second
     883                 :          0 :   if ( radius1 > radius2 )
     884                 :          0 :     return generateSegmentCurve( center2, radius2, center1, radius1 );
     885                 :            : 
     886                 :          0 :   QgsPointXY t1;
     887                 :          0 :   QgsPointXY t2;
     888                 :          0 :   QgsPointXY t3;
     889                 :          0 :   QgsPointXY t4;
     890                 :          0 :   QVector<QgsPointXY> points;
     891                 :          0 :   if ( QgsGeometryUtils::circleCircleOuterTangents( center1, radius1, center2, radius2, t1, t2, t3, t4 ) )
     892                 :            :   {
     893                 :          0 :     points << t1
     894                 :          0 :            << t2
     895                 :          0 :            << t4
     896                 :          0 :            << t3;
     897                 :          0 :   }
     898                 :          0 :   return points;
     899                 :          0 : }
     900                 :            : 
     901                 :            : // partially ported from JTS VariableWidthBuffer,
     902                 :            : // https://github.com/topobyte/jts/blob/master/jts-lab/src/main/java/com/vividsolutions/jts/operation/buffer/VariableWidthBuffer.java
     903                 :            : 
     904                 :          0 : QgsGeometry QgsInternalGeometryEngine::variableWidthBuffer( int segments, const std::function< std::unique_ptr< double[] >( const QgsLineString *line ) > &widthFunction ) const
     905                 :            : {
     906                 :          0 :   mLastError.clear();
     907                 :          0 :   if ( !mGeometry )
     908                 :            :   {
     909                 :          0 :     return QgsGeometry();
     910                 :            :   }
     911                 :            : 
     912                 :          0 :   std::vector< std::unique_ptr<QgsLineString > > linesToProcess;
     913                 :            : 
     914                 :          0 :   const QgsMultiCurve *multiCurve = qgsgeometry_cast< const QgsMultiCurve * >( mGeometry );
     915                 :          0 :   if ( multiCurve )
     916                 :            :   {
     917                 :          0 :     for ( int i = 0; i < multiCurve->partCount(); ++i )
     918                 :            :     {
     919                 :          0 :       if ( static_cast< const QgsCurve * >( multiCurve->geometryN( i ) )->nCoordinates() == 0 )
     920                 :          0 :         continue; // skip 0 length lines
     921                 :            : 
     922                 :          0 :       linesToProcess.emplace_back( static_cast<QgsLineString *>( multiCurve->geometryN( i )->clone() ) );
     923                 :          0 :     }
     924                 :          0 :   }
     925                 :            : 
     926                 :          0 :   const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( mGeometry );
     927                 :          0 :   if ( curve )
     928                 :            :   {
     929                 :          0 :     if ( curve->nCoordinates() > 0 )
     930                 :          0 :       linesToProcess.emplace_back( static_cast<QgsLineString *>( curve->segmentize() ) );
     931                 :          0 :   }
     932                 :            : 
     933                 :          0 :   if ( linesToProcess.empty() )
     934                 :            :   {
     935                 :          0 :     QgsGeometry g;
     936                 :          0 :     g.mLastError = QStringLiteral( "Input geometry was not a curve type geometry" );
     937                 :          0 :     return g;
     938                 :          0 :   }
     939                 :            : 
     940                 :          0 :   QVector<QgsGeometry> bufferedLines;
     941                 :          0 :   bufferedLines.reserve( linesToProcess.size() );
     942                 :            : 
     943                 :          0 :   for ( std::unique_ptr< QgsLineString > &line : linesToProcess )
     944                 :            :   {
     945                 :          0 :     QVector<QgsGeometry> parts;
     946                 :          0 :     QgsPoint prevPoint;
     947                 :          0 :     double prevRadius = 0;
     948                 :          0 :     QgsGeometry prevCircle;
     949                 :            : 
     950                 :          0 :     std::unique_ptr< double[] > widths = widthFunction( line.get() ) ;
     951                 :          0 :     for ( int i = 0; i < line->nCoordinates(); ++i )
     952                 :            :     {
     953                 :          0 :       QgsPoint thisPoint = line->pointN( i );
     954                 :          0 :       QgsGeometry thisCircle;
     955                 :          0 :       double thisRadius = widths[ i ] / 2.0;
     956                 :          0 :       if ( thisRadius > 0 )
     957                 :            :       {
     958                 :          0 :         QgsGeometry p = QgsGeometry( thisPoint.clone() );
     959                 :            : 
     960                 :          0 :         QgsCircle circ( thisPoint, thisRadius );
     961                 :          0 :         thisCircle = QgsGeometry( circ.toPolygon( segments * 4 ) );
     962                 :          0 :         parts << thisCircle;
     963                 :          0 :       }
     964                 :            :       else
     965                 :            :       {
     966                 :          0 :         thisCircle = QgsGeometry( thisPoint.clone() );
     967                 :            :       }
     968                 :            : 
     969                 :          0 :       if ( i > 0 )
     970                 :            :       {
     971                 :          0 :         if ( prevRadius > 0 || thisRadius > 0 )
     972                 :            :         {
     973                 :          0 :           QVector< QgsPointXY > points = generateSegmentCurve( prevPoint, prevRadius, thisPoint, thisRadius );
     974                 :          0 :           if ( !points.empty() )
     975                 :            :           {
     976                 :            :             // snap points to circle vertices
     977                 :            : 
     978                 :          0 :             int atVertex = 0;
     979                 :          0 :             int beforeVertex = 0;
     980                 :          0 :             int afterVertex = 0;
     981                 :          0 :             double sqrDist = 0;
     982                 :          0 :             double sqrDistPrev = 0;
     983                 :          0 :             for ( int j = 0; j < points.count(); ++j )
     984                 :            :             {
     985                 :          0 :               QgsPointXY pA = prevCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDistPrev );
     986                 :          0 :               QgsPointXY pB = thisCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDist );
     987                 :          0 :               points[j] = sqrDistPrev < sqrDist ? pA : pB;
     988                 :          0 :             }
     989                 :            :             // close ring
     990                 :          0 :             points.append( points.at( 0 ) );
     991                 :            : 
     992                 :          0 :             std::unique_ptr< QgsPolygon > poly = std::make_unique< QgsPolygon >();
     993                 :          0 :             poly->setExteriorRing( new QgsLineString( points ) );
     994                 :          0 :             if ( poly->area() > 0 )
     995                 :          0 :               parts << QgsGeometry( std::move( poly ) );
     996                 :          0 :           }
     997                 :          0 :         }
     998                 :          0 :       }
     999                 :          0 :       prevPoint = thisPoint;
    1000                 :          0 :       prevRadius = thisRadius;
    1001                 :          0 :       prevCircle = thisCircle;
    1002                 :          0 :     }
    1003                 :            : 
    1004                 :          0 :     bufferedLines << QgsGeometry::unaryUnion( parts );
    1005                 :          0 :   }
    1006                 :            : 
    1007                 :          0 :   return QgsGeometry::collectGeometry( bufferedLines );
    1008                 :          0 : }
    1009                 :            : 
    1010                 :          0 : QgsGeometry QgsInternalGeometryEngine::taperedBuffer( double start, double end, int segments ) const
    1011                 :            : {
    1012                 :          0 :   mLastError.clear();
    1013                 :          0 :   start = std::fabs( start );
    1014                 :          0 :   end = std::fabs( end );
    1015                 :            : 
    1016                 :          0 :   auto interpolateWidths = [ start, end ]( const QgsLineString * line )->std::unique_ptr< double [] >
    1017                 :            :   {
    1018                 :            :     // ported from JTS VariableWidthBuffer,
    1019                 :            :     // https://github.com/topobyte/jts/blob/master/jts-lab/src/main/java/com/vividsolutions/jts/operation/buffer/VariableWidthBuffer.java
    1020                 :          0 :     std::unique_ptr< double [] > widths( new double[ line->nCoordinates() ] );
    1021                 :          0 :     widths[0] = start;
    1022                 :          0 :     widths[line->nCoordinates() - 1] = end;
    1023                 :            : 
    1024                 :          0 :     double lineLength = line->length();
    1025                 :          0 :     double currentLength = 0;
    1026                 :          0 :     QgsPoint prevPoint = line->pointN( 0 );
    1027                 :          0 :     for ( int i = 1; i < line->nCoordinates() - 1; ++i )
    1028                 :            :     {
    1029                 :          0 :       QgsPoint point = line->pointN( i );
    1030                 :          0 :       double segmentLength = point.distance( prevPoint );
    1031                 :          0 :       currentLength += segmentLength;
    1032                 :          0 :       double lengthFraction = lineLength > 0 ? currentLength / lineLength : 1;
    1033                 :          0 :       double delta = lengthFraction * ( end - start );
    1034                 :          0 :       widths[i] = start + delta;
    1035                 :          0 :       prevPoint = point;
    1036                 :          0 :     }
    1037                 :          0 :     return widths;
    1038                 :          0 :   };
    1039                 :            : 
    1040                 :          0 :   return variableWidthBuffer( segments, interpolateWidths );
    1041                 :          0 : }
    1042                 :            : 
    1043                 :          0 : QgsGeometry QgsInternalGeometryEngine::variableWidthBufferByM( int segments ) const
    1044                 :            : {
    1045                 :          0 :   mLastError.clear();
    1046                 :          0 :   auto widthByM = []( const QgsLineString * line )->std::unique_ptr< double [] >
    1047                 :            :   {
    1048                 :          0 :     std::unique_ptr< double [] > widths( new double[ line->nCoordinates() ] );
    1049                 :          0 :     for ( int i = 0; i < line->nCoordinates(); ++i )
    1050                 :            :     {
    1051                 :          0 :       widths[ i ] = line->mAt( i );
    1052                 :          0 :     }
    1053                 :          0 :     return widths;
    1054                 :          0 :   };
    1055                 :            : 
    1056                 :          0 :   return variableWidthBuffer( segments, widthByM );
    1057                 :          0 : }
    1058                 :            : 
    1059                 :          8 : QVector<QgsPointXY> QgsInternalGeometryEngine::randomPointsInPolygon( const QgsGeometry &polygon, int count,
    1060                 :            :     const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback, int maxTriesPerPoint )
    1061                 :            : {
    1062                 :          8 :   if ( polygon.type() != QgsWkbTypes::PolygonGeometry || count == 0 )
    1063                 :          1 :     return QVector< QgsPointXY >();
    1064                 :            : 
    1065                 :            :   // step 1 - tessellate the polygon to triangles
    1066                 :          7 :   QgsRectangle bounds = polygon.boundingBox();
    1067                 :          7 :   QgsTessellator t( bounds, false, false, false, true );
    1068                 :            : 
    1069                 :          7 :   if ( polygon.isMultipart() )
    1070                 :            :   {
    1071                 :          6 :     const QgsMultiSurface *ms = qgsgeometry_cast< const QgsMultiSurface * >( polygon.constGet() );
    1072                 :         18 :     for ( int i = 0; i < ms->numGeometries(); ++i )
    1073                 :            :     {
    1074                 :         12 :       if ( feedback && feedback->isCanceled() )
    1075                 :          0 :         return QVector< QgsPointXY >();
    1076                 :            : 
    1077                 :         12 :       if ( QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( ms->geometryN( i ) ) )
    1078                 :            :       {
    1079                 :         12 :         t.addPolygon( *poly, 0 );
    1080                 :         12 :       }
    1081                 :            :       else
    1082                 :            :       {
    1083                 :          0 :         std::unique_ptr< QgsPolygon > p( qgsgeometry_cast< QgsPolygon * >( ms->geometryN( i )->segmentize() ) );
    1084                 :          0 :         t.addPolygon( *p, 0 );
    1085                 :          0 :       }
    1086                 :         12 :     }
    1087                 :          6 :   }
    1088                 :            :   else
    1089                 :            :   {
    1090                 :          1 :     if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( polygon.constGet() ) )
    1091                 :            :     {
    1092                 :          1 :       t.addPolygon( *poly, 0 );
    1093                 :          1 :     }
    1094                 :            :     else
    1095                 :            :     {
    1096                 :          0 :       std::unique_ptr< QgsPolygon > p( qgsgeometry_cast< QgsPolygon * >( polygon.constGet()->segmentize() ) );
    1097                 :          0 :       t.addPolygon( *p, 0 );
    1098                 :          0 :     }
    1099                 :            :   }
    1100                 :            : 
    1101                 :          7 :   if ( feedback && feedback->isCanceled() )
    1102                 :          0 :     return QVector< QgsPointXY >();
    1103                 :            : 
    1104                 :          7 :   const QVector<float> triangleData = t.data();
    1105                 :          7 :   if ( triangleData.empty() )
    1106                 :          0 :     return QVector< QgsPointXY >(); //hm
    1107                 :            : 
    1108                 :            :   // calculate running sum of triangle areas
    1109                 :          7 :   std::vector< double > cumulativeAreas;
    1110                 :          7 :   cumulativeAreas.reserve( t.dataVerticesCount() / 3 );
    1111                 :          7 :   double totalArea = 0;
    1112                 :         98 :   for ( auto it = triangleData.constBegin(); it != triangleData.constEnd(); )
    1113                 :            :   {
    1114                 :         91 :     if ( feedback && feedback->isCanceled() )
    1115                 :          0 :       return QVector< QgsPointXY >();
    1116                 :            : 
    1117                 :         91 :     const float aX = *it++;
    1118                 :         91 :     ( void )it++; // z
    1119                 :         91 :     const float aY = -( *it++ );
    1120                 :         91 :     const float bX = *it++;
    1121                 :         91 :     ( void )it++; // z
    1122                 :         91 :     const float bY = -( *it++ );
    1123                 :         91 :     const float cX = *it++;
    1124                 :         91 :     ( void )it++; // z
    1125                 :         91 :     const float cY = -( *it++ );
    1126                 :            : 
    1127                 :         91 :     const double area = QgsGeometryUtils::triangleArea( aX, aY, bX, bY, cX, cY );
    1128                 :         91 :     totalArea += area;
    1129                 :         91 :     cumulativeAreas.emplace_back( totalArea );
    1130                 :            :   }
    1131                 :            : 
    1132                 :          7 :   std::random_device rd;
    1133                 :          7 :   std::mt19937 mt( seed == 0 ? rd() : seed );
    1134                 :          7 :   std::uniform_real_distribution<> uniformDist( 0, 1 );
    1135                 :            : 
    1136                 :            :   // selects a random triangle, weighted by triangle area
    1137                 :      40162 :   auto selectRandomTriangle = [&cumulativeAreas, totalArea]( double random )->int
    1138                 :            :   {
    1139                 :      40155 :     int triangle = 0;
    1140                 :      40155 :     const double target = random * totalArea;
    1141                 :     256498 :     for ( auto it = cumulativeAreas.begin(); it != cumulativeAreas.end(); ++it, triangle++ )
    1142                 :            :     {
    1143                 :     256498 :       if ( *it > target )
    1144                 :      40155 :         return triangle;
    1145                 :     216343 :     }
    1146                 :            :     Q_ASSERT_X( false, "QgsInternalGeometryEngine::randomPointsInPolygon", "Invalid random triangle index" );
    1147                 :          0 :     return 0; // no warnings
    1148                 :      40155 :   };
    1149                 :            : 
    1150                 :            : 
    1151                 :          7 :   QVector<QgsPointXY> result;
    1152                 :          7 :   result.reserve( count );
    1153                 :          7 :   int tries = 0;
    1154                 :      40162 :   for ( int i = 0; i < count; )
    1155                 :            :   {
    1156                 :      40155 :     if ( feedback && feedback->isCanceled() )
    1157                 :          0 :       return QVector< QgsPointXY >();
    1158                 :            : 
    1159                 :      40155 :     const double triangleIndexRnd = uniformDist( mt );
    1160                 :            :     // pick random triangle, weighted by triangle area
    1161                 :      40155 :     const int triangleIndex = selectRandomTriangle( triangleIndexRnd );
    1162                 :            : 
    1163                 :            :     // generate a random point inside this triangle
    1164                 :      40155 :     const double weightB = uniformDist( mt );
    1165                 :      40155 :     const double weightC = uniformDist( mt );
    1166                 :            :     double x;
    1167                 :            :     double y;
    1168                 :            : 
    1169                 :            :     // get triangle
    1170                 :      40155 :     const double aX = triangleData.at( triangleIndex * 9 ) + bounds.xMinimum();
    1171                 :      40155 :     const double aY = -triangleData.at( triangleIndex * 9 + 2 ) + bounds.yMinimum();
    1172                 :      40155 :     const double bX = triangleData.at( triangleIndex * 9 + 3 ) + bounds.xMinimum();
    1173                 :      40155 :     const double bY = -triangleData.at( triangleIndex * 9 + 5 ) + bounds.yMinimum();
    1174                 :      40155 :     const double cX = triangleData.at( triangleIndex * 9 + 6 ) + bounds.xMinimum();
    1175                 :      40155 :     const double cY = -triangleData.at( triangleIndex * 9 + 8 ) + bounds.yMinimum();
    1176                 :      40155 :     QgsGeometryUtils::weightedPointInTriangle( aX, aY, bX, bY, cX, cY, weightB, weightC, x, y );
    1177                 :            : 
    1178                 :      40155 :     QgsPointXY candidate( x, y );
    1179                 :      40155 :     if ( acceptPoint( candidate ) )
    1180                 :            :     {
    1181                 :      30400 :       result << QgsPointXY( x, y );
    1182                 :      30400 :       i++;
    1183                 :      30400 :       tries = 0;
    1184                 :      30400 :     }
    1185                 :       9755 :     else if ( maxTriesPerPoint != 0 )
    1186                 :            :     {
    1187                 :          0 :       tries++;
    1188                 :            :       // Skip this point if maximum tries is reached
    1189                 :          0 :       if ( tries == maxTriesPerPoint )
    1190                 :            :       {
    1191                 :          0 :         tries = 0;
    1192                 :          0 :         i++;
    1193                 :          0 :       }
    1194                 :          0 :     }
    1195                 :            :   }
    1196                 :          7 :   return result;
    1197                 :          8 : }
    1198                 :            : 
    1199                 :            : // ported from PostGIS' lwgeom pta_unstroke
    1200                 :            : 
    1201                 :          0 : std::unique_ptr< QgsCompoundCurve > lineToCurve( const QgsLineString *lineString, double distanceTolerance,
    1202                 :            :     double pointSpacingAngleTolerance )
    1203                 :            : {
    1204                 :          0 :   std::unique_ptr< QgsCompoundCurve > out = std::make_unique< QgsCompoundCurve >();
    1205                 :            : 
    1206                 :            :   /* Minimum number of edges, per quadrant, required to define an arc */
    1207                 :          0 :   const unsigned int minQuadEdges = 2;
    1208                 :            : 
    1209                 :            :   /* Die on null input */
    1210                 :          0 :   if ( !lineString )
    1211                 :          0 :     return nullptr;
    1212                 :            : 
    1213                 :            :   /* Null on empty input? */
    1214                 :          0 :   if ( lineString->nCoordinates() == 0 )
    1215                 :          0 :     return nullptr;
    1216                 :            : 
    1217                 :            :   /* We can't desegmentize anything shorter than four points */
    1218                 :          0 :   if ( lineString->nCoordinates() < 4 )
    1219                 :            :   {
    1220                 :          0 :     out->addCurve( lineString->clone() );
    1221                 :          0 :     return out;
    1222                 :            :   }
    1223                 :            : 
    1224                 :            :   /* Allocate our result array of vertices that are part of arcs */
    1225                 :          0 :   int numEdges = lineString->nCoordinates() - 1;
    1226                 :          0 :   QVector< int > edgesInArcs( numEdges + 1, 0 );
    1227                 :            : 
    1228                 :          0 :   auto arcAngle = []( const QgsPoint & a, const QgsPoint & b, const QgsPoint & c )->double
    1229                 :            :   {
    1230                 :          0 :     double abX = b.x() - a.x();
    1231                 :          0 :     double abY = b.y() - a.y();
    1232                 :            : 
    1233                 :          0 :     double cbX = b.x() - c.x();
    1234                 :          0 :     double cbY = b.y() - c.y();
    1235                 :            : 
    1236                 :          0 :     double dot = ( abX * cbX + abY * cbY ); /* dot product */
    1237                 :          0 :     double cross = ( abX * cbY - abY * cbX ); /* cross product */
    1238                 :            : 
    1239                 :          0 :     double alpha = std::atan2( cross, dot );
    1240                 :            : 
    1241                 :          0 :     return alpha;
    1242                 :            :   };
    1243                 :            : 
    1244                 :            :   /* We make a candidate arc of the first two edges, */
    1245                 :            :   /* And then see if the next edge follows it */
    1246                 :          0 :   int i = 0;
    1247                 :          0 :   int j = 0;
    1248                 :          0 :   int k = 0;
    1249                 :          0 :   int currentArc = 1;
    1250                 :          0 :   QgsPoint a1;
    1251                 :          0 :   QgsPoint a2;
    1252                 :          0 :   QgsPoint a3;
    1253                 :          0 :   QgsPoint b;
    1254                 :          0 :   double centerX = 0.0;
    1255                 :          0 :   double centerY = 0.0;
    1256                 :          0 :   double radius = 0;
    1257                 :            : 
    1258                 :          0 :   while ( i < numEdges - 2 )
    1259                 :            :   {
    1260                 :          0 :     unsigned int arcEdges = 0;
    1261                 :          0 :     double numQuadrants = 0;
    1262                 :            :     double angle;
    1263                 :            : 
    1264                 :          0 :     bool foundArc = false;
    1265                 :            :     /* Make candidate arc */
    1266                 :          0 :     a1 = lineString->pointN( i );
    1267                 :          0 :     a2 = lineString->pointN( i + 1 );
    1268                 :          0 :     a3 = lineString->pointN( i + 2 );
    1269                 :          0 :     QgsPoint first = a1;
    1270                 :            : 
    1271                 :          0 :     for ( j = i + 3; j < numEdges + 1; j++ )
    1272                 :            :     {
    1273                 :          0 :       b = lineString->pointN( j );
    1274                 :            : 
    1275                 :            :       /* Does this point fall on our candidate arc? */
    1276                 :          0 :       if ( QgsGeometryUtils::pointContinuesArc( a1, a2, a3, b, distanceTolerance, pointSpacingAngleTolerance ) )
    1277                 :            :       {
    1278                 :            :         /* Yes. Mark this edge and the two preceding it as arc components */
    1279                 :          0 :         foundArc = true;
    1280                 :          0 :         for ( k = j - 1; k > j - 4; k-- )
    1281                 :          0 :           edgesInArcs[k] = currentArc;
    1282                 :          0 :       }
    1283                 :            :       else
    1284                 :            :       {
    1285                 :            :         /* No. So we're done with this candidate arc */
    1286                 :          0 :         currentArc++;
    1287                 :          0 :         break;
    1288                 :            :       }
    1289                 :            : 
    1290                 :          0 :       a1 = a2;
    1291                 :          0 :       a2 = a3;
    1292                 :          0 :       a3 = b;
    1293                 :          0 :     }
    1294                 :            :     /* Jump past all the edges that were added to the arc */
    1295                 :          0 :     if ( foundArc )
    1296                 :            :     {
    1297                 :            :       /* Check if an arc was composed by enough edges to be
    1298                 :            :        * really considered an arc
    1299                 :            :        * See http://trac.osgeo.org/postgis/ticket/2420
    1300                 :            :        */
    1301                 :          0 :       arcEdges = j - 1 - i;
    1302                 :          0 :       if ( first.x() == b.x() && first.y() == b.y() )
    1303                 :            :       {
    1304                 :          0 :         numQuadrants = 4;
    1305                 :          0 :       }
    1306                 :            :       else
    1307                 :            :       {
    1308                 :          0 :         QgsGeometryUtils::circleCenterRadius( first, b, a1, radius, centerX, centerY );
    1309                 :            : 
    1310                 :          0 :         angle = arcAngle( first, QgsPoint( centerX, centerY ), b );
    1311                 :          0 :         int p2Side = QgsGeometryUtils::leftOfLine( b.x(), b.y(), first.x(), first.y(), a1.x(), a1.y() );
    1312                 :          0 :         if ( p2Side >= 0 )
    1313                 :          0 :           angle = -angle;
    1314                 :            : 
    1315                 :          0 :         if ( angle < 0 )
    1316                 :          0 :           angle = 2 * M_PI + angle;
    1317                 :          0 :         numQuadrants = ( 4 * angle ) / ( 2 * M_PI );
    1318                 :            :       }
    1319                 :            :       /* a1 is first point, b is last point */
    1320                 :          0 :       if ( arcEdges < minQuadEdges * numQuadrants )
    1321                 :            :       {
    1322                 :            :         // LWDEBUGF( 4, "Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants );
    1323                 :          0 :         for ( k = j - 1; k >= i; k-- )
    1324                 :          0 :           edgesInArcs[k] = 0;
    1325                 :          0 :       }
    1326                 :            : 
    1327                 :          0 :       i = j - 1;
    1328                 :          0 :     }
    1329                 :            :     else
    1330                 :            :     {
    1331                 :            :       /* Mark this edge as a linear edge */
    1332                 :          0 :       edgesInArcs[i] = 0;
    1333                 :          0 :       i = i + 1;
    1334                 :            :     }
    1335                 :          0 :   }
    1336                 :            : 
    1337                 :          0 :   int start = 0;
    1338                 :          0 :   int end = 0;
    1339                 :            :   /* non-zero if edge is part of an arc */
    1340                 :          0 :   int edgeType = edgesInArcs[0];
    1341                 :            : 
    1342                 :          0 :   auto addPointsToCurve = [ lineString, &out ]( int start, int end, int type )
    1343                 :            :   {
    1344                 :          0 :     if ( type == 0 )
    1345                 :            :     {
    1346                 :            :       // straight segment
    1347                 :          0 :       QVector< QgsPoint > points;
    1348                 :          0 :       for ( int j = start; j < end + 2; ++ j )
    1349                 :            :       {
    1350                 :          0 :         points.append( lineString->pointN( j ) );
    1351                 :          0 :       }
    1352                 :          0 :       std::unique_ptr< QgsCurve > straightSegment = std::make_unique< QgsLineString >( points );
    1353                 :          0 :       out->addCurve( straightSegment.release() );
    1354                 :          0 :     }
    1355                 :            :     else
    1356                 :            :     {
    1357                 :            :       // curved segment
    1358                 :          0 :       QVector< QgsPoint > points;
    1359                 :          0 :       points.append( lineString->pointN( start ) );
    1360                 :          0 :       points.append( lineString->pointN( ( start + end + 1 ) / 2 ) );
    1361                 :          0 :       points.append( lineString->pointN( end + 1 ) );
    1362                 :          0 :       std::unique_ptr< QgsCircularString > curvedSegment = std::make_unique< QgsCircularString >();
    1363                 :          0 :       curvedSegment->setPoints( points );
    1364                 :          0 :       out->addCurve( curvedSegment.release() );
    1365                 :          0 :     }
    1366                 :          0 :   };
    1367                 :            : 
    1368                 :          0 :   for ( int i = 1; i < numEdges; i++ )
    1369                 :            :   {
    1370                 :          0 :     if ( edgeType != edgesInArcs[i] )
    1371                 :            :     {
    1372                 :          0 :       end = i - 1;
    1373                 :          0 :       addPointsToCurve( start, end, edgeType );
    1374                 :          0 :       start = i;
    1375                 :          0 :       edgeType = edgesInArcs[i];
    1376                 :          0 :     }
    1377                 :          0 :   }
    1378                 :            : 
    1379                 :            :   /* Roll out last item */
    1380                 :          0 :   end = numEdges - 1;
    1381                 :          0 :   addPointsToCurve( start, end, edgeType );
    1382                 :            : 
    1383                 :          0 :   return out;
    1384                 :          0 : }
    1385                 :            : 
    1386                 :          0 : std::unique_ptr< QgsAbstractGeometry > convertGeometryToCurves( const QgsAbstractGeometry *geom, double distanceTolerance, double angleTolerance )
    1387                 :            : {
    1388                 :          0 :   if ( QgsWkbTypes::geometryType( geom->wkbType() ) == QgsWkbTypes::LineGeometry )
    1389                 :            :   {
    1390                 :          0 :     return lineToCurve( static_cast< const QgsLineString * >( geom ), distanceTolerance, angleTolerance );
    1391                 :            :   }
    1392                 :            :   else
    1393                 :            :   {
    1394                 :            :     // polygon
    1395                 :          0 :     const QgsPolygon *polygon = static_cast< const QgsPolygon * >( geom );
    1396                 :          0 :     std::unique_ptr< QgsCurvePolygon > result = std::make_unique< QgsCurvePolygon>();
    1397                 :            : 
    1398                 :          0 :     result->setExteriorRing( lineToCurve( static_cast< const QgsLineString * >( polygon->exteriorRing() ),
    1399                 :          0 :                                           distanceTolerance, angleTolerance ).release() );
    1400                 :          0 :     for ( int i = 0; i < polygon->numInteriorRings(); ++i )
    1401                 :            :     {
    1402                 :          0 :       result->addInteriorRing( lineToCurve( static_cast< const QgsLineString * >( polygon->interiorRing( i ) ),
    1403                 :          0 :                                             distanceTolerance, angleTolerance ).release() );
    1404                 :          0 :     }
    1405                 :            : 
    1406                 :          0 :     return result;
    1407                 :          0 :   }
    1408                 :          0 : }
    1409                 :            : 
    1410                 :          0 : QgsGeometry QgsInternalGeometryEngine::convertToCurves( double distanceTolerance, double angleTolerance ) const
    1411                 :            : {
    1412                 :          0 :   mLastError.clear();
    1413                 :          0 :   if ( !mGeometry )
    1414                 :            :   {
    1415                 :          0 :     return QgsGeometry();
    1416                 :            :   }
    1417                 :            : 
    1418                 :          0 :   if ( QgsWkbTypes::geometryType( mGeometry->wkbType() ) == QgsWkbTypes::PointGeometry )
    1419                 :            :   {
    1420                 :          0 :     return QgsGeometry( mGeometry->clone() ); // point geometry, nothing to do
    1421                 :            :   }
    1422                 :            : 
    1423                 :          0 :   if ( QgsWkbTypes::isCurvedType( mGeometry->wkbType() ) )
    1424                 :            :   {
    1425                 :            :     // already curved. In future we may want to allow this, and convert additional candidate segments
    1426                 :            :     // in an already curved geometry to curves
    1427                 :          0 :     return QgsGeometry( mGeometry->clone() );
    1428                 :            :   }
    1429                 :            : 
    1430                 :          0 :   if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
    1431                 :            :   {
    1432                 :          0 :     int numGeom = gc->numGeometries();
    1433                 :          0 :     QVector< QgsAbstractGeometry * > geometryList;
    1434                 :          0 :     geometryList.reserve( numGeom );
    1435                 :          0 :     for ( int i = 0; i < numGeom; ++i )
    1436                 :            :     {
    1437                 :          0 :       geometryList << convertGeometryToCurves( gc->geometryN( i ), distanceTolerance, angleTolerance ).release();
    1438                 :          0 :     }
    1439                 :            : 
    1440                 :          0 :     QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
    1441                 :          0 :     for ( QgsAbstractGeometry *g : std::as_const( geometryList ) )
    1442                 :            :     {
    1443                 :          0 :       first.addPart( g );
    1444                 :            :     }
    1445                 :          0 :     return first;
    1446                 :          0 :   }
    1447                 :            :   else
    1448                 :            :   {
    1449                 :          0 :     return QgsGeometry( convertGeometryToCurves( mGeometry, distanceTolerance, angleTolerance ) );
    1450                 :            :   }
    1451                 :          0 : }
    1452                 :            : 
    1453                 :          3 : QgsGeometry QgsInternalGeometryEngine::orientedMinimumBoundingBox( double &area, double &angle, double &width, double &height ) const
    1454                 :            : {
    1455                 :          3 :   mLastError.clear();
    1456                 :            : 
    1457                 :          3 :   QgsRectangle minRect;
    1458                 :          3 :   area = std::numeric_limits<double>::max();
    1459                 :          3 :   angle = 0;
    1460                 :          3 :   width = std::numeric_limits<double>::max();
    1461                 :          3 :   height = std::numeric_limits<double>::max();
    1462                 :            : 
    1463                 :          3 :   if ( !mGeometry || mGeometry->nCoordinates() < 2 )
    1464                 :          2 :     return QgsGeometry();
    1465                 :            : 
    1466                 :          1 :   std::unique_ptr< QgsGeometryEngine >engine( QgsGeometry::createGeometryEngine( mGeometry ) );
    1467                 :          1 :   QString error;
    1468                 :          1 :   std::unique_ptr< QgsAbstractGeometry > hull( engine->convexHull( &mLastError ) );
    1469                 :          1 :   if ( !hull )
    1470                 :          0 :     return QgsGeometry();
    1471                 :            : 
    1472                 :          1 :   QgsVertexId vertexId;
    1473                 :          1 :   QgsPoint pt0;
    1474                 :          1 :   QgsPoint pt1;
    1475                 :          1 :   QgsPoint pt2;
    1476                 :            :   // get first point
    1477                 :          1 :   hull->nextVertex( vertexId, pt0 );
    1478                 :          1 :   pt1 = pt0;
    1479                 :          1 :   double totalRotation = 0;
    1480                 :         41 :   while ( hull->nextVertex( vertexId, pt2 ) )
    1481                 :            :   {
    1482                 :         40 :     double currentAngle = QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() );
    1483                 :         40 :     double rotateAngle = 180.0 / M_PI * currentAngle;
    1484                 :         40 :     totalRotation += rotateAngle;
    1485                 :            : 
    1486                 :         40 :     QTransform t = QTransform::fromTranslate( pt0.x(), pt0.y() );
    1487                 :         40 :     t.rotate( rotateAngle );
    1488                 :         40 :     t.translate( -pt0.x(), -pt0.y() );
    1489                 :            : 
    1490                 :         40 :     hull->transform( t );
    1491                 :            : 
    1492                 :         40 :     QgsRectangle bounds = hull->boundingBox();
    1493                 :         40 :     double currentArea = bounds.width() * bounds.height();
    1494                 :         40 :     if ( currentArea  < area )
    1495                 :            :     {
    1496                 :          6 :       minRect = bounds;
    1497                 :          6 :       area = currentArea;
    1498                 :          6 :       angle = totalRotation;
    1499                 :          6 :       width = bounds.width();
    1500                 :          6 :       height = bounds.height();
    1501                 :          6 :     }
    1502                 :            : 
    1503                 :         40 :     pt1 = hull->vertexAt( vertexId );
    1504                 :            :   }
    1505                 :            : 
    1506                 :          1 :   QgsGeometry minBounds = QgsGeometry::fromRect( minRect );
    1507                 :          1 :   minBounds.rotate( angle, QgsPointXY( pt0.x(), pt0.y() ) );
    1508                 :            : 
    1509                 :            :   // constrain angle to 0 - 180
    1510                 :          1 :   if ( angle > 180.0 )
    1511                 :          1 :     angle = std::fmod( angle, 180.0 );
    1512                 :            : 
    1513                 :          1 :   return minBounds;
    1514                 :          3 : }

Generated by: LCOV version 1.14