LCOV - code coverage report
Current view: top level - ogr - ogrcircularstring.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 397 438 90.6 %
Date: 2024-05-03 15:49:35 Functions: 26 30 86.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  The OGRCircularString geometry class.
       5             :  * Author:   Even Rouault, even dot rouault at spatialys dot com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010, 2014, Even Rouault <even dot rouault at spatialys dot
       9             :  *com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "ogr_geometry.h"
      32             : 
      33             : #include <cmath>
      34             : #include <cstring>
      35             : 
      36             : #include <algorithm>
      37             : #include <limits>
      38             : #include <vector>
      39             : 
      40             : #include "cpl_error.h"
      41             : #include "ogr_core.h"
      42             : #include "ogr_geometry.h"
      43             : #include "ogr_p.h"
      44             : 
      45          44 : static inline double dist(double x0, double y0, double x1, double y1)
      46             : {
      47          44 :     return std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
      48             : }
      49             : 
      50             : /************************************************************************/
      51             : /*                         OGRCircularString()                          */
      52             : /************************************************************************/
      53             : 
      54             : /**
      55             :  * \brief Create an empty circular string.
      56             :  */
      57             : 
      58             : OGRCircularString::OGRCircularString() = default;
      59             : 
      60             : /************************************************************************/
      61             : /*              OGRCircularString( const OGRCircularString& )           */
      62             : /************************************************************************/
      63             : 
      64             : /**
      65             :  * \brief Copy constructor.
      66             :  *
      67             :  * Note: before GDAL 2.1, only the default implementation of the constructor
      68             :  * existed, which could be unsafe to use.
      69             :  *
      70             :  * @since GDAL 2.1
      71             :  */
      72             : 
      73             : OGRCircularString::OGRCircularString(const OGRCircularString &) = default;
      74             : 
      75             : /************************************************************************/
      76             : /*                        ~OGRCircularString()                          */
      77             : /************************************************************************/
      78             : 
      79             : OGRCircularString::~OGRCircularString() = default;
      80             : 
      81             : /************************************************************************/
      82             : /*                  operator=( const OGRCircularString& )               */
      83             : /************************************************************************/
      84             : 
      85             : /**
      86             :  * \brief Assignment operator.
      87             :  *
      88             :  * Note: before GDAL 2.1, only the default implementation of the operator
      89             :  * existed, which could be unsafe to use.
      90             :  *
      91             :  * @since GDAL 2.1
      92             :  */
      93             : 
      94           5 : OGRCircularString &OGRCircularString::operator=(const OGRCircularString &other)
      95             : {
      96           5 :     if (this != &other)
      97             :     {
      98           4 :         OGRSimpleCurve::operator=(other);
      99             :     }
     100           5 :     return *this;
     101             : }
     102             : 
     103             : /************************************************************************/
     104             : /*                          getGeometryType()                           */
     105             : /************************************************************************/
     106             : 
     107       10109 : OGRwkbGeometryType OGRCircularString::getGeometryType() const
     108             : 
     109             : {
     110       10109 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     111         580 :         return wkbCircularStringZM;
     112        9529 :     else if (flags & OGR_G_MEASURED)
     113          30 :         return wkbCircularStringM;
     114        9499 :     else if (flags & OGR_G_3D)
     115         467 :         return wkbCircularStringZ;
     116             :     else
     117        9032 :         return wkbCircularString;
     118             : }
     119             : 
     120             : /************************************************************************/
     121             : /*                          getGeometryName()                           */
     122             : /************************************************************************/
     123             : 
     124        4509 : const char *OGRCircularString::getGeometryName() const
     125             : 
     126             : {
     127        4509 :     return "CIRCULARSTRING";
     128             : }
     129             : 
     130             : /************************************************************************/
     131             : /*                               clone()                                */
     132             : /************************************************************************/
     133             : 
     134         391 : OGRCircularString *OGRCircularString::clone() const
     135             : 
     136             : {
     137         391 :     return new (std::nothrow) OGRCircularString(*this);
     138             : }
     139             : 
     140             : /************************************************************************/
     141             : /*                           importFromWkb()                            */
     142             : /*                                                                      */
     143             : /*      Initialize from serialized stream in well known binary          */
     144             : /*      format.                                                         */
     145             : /************************************************************************/
     146             : 
     147          47 : OGRErr OGRCircularString::importFromWkb(const unsigned char *pabyData,
     148             :                                         size_t nSize, OGRwkbVariant eWkbVariant,
     149             :                                         size_t &nBytesConsumedOut)
     150             : 
     151             : {
     152          47 :     OGRErr eErr = OGRSimpleCurve::importFromWkb(pabyData, nSize, eWkbVariant,
     153             :                                                 nBytesConsumedOut);
     154          47 :     if (eErr == OGRERR_NONE)
     155             :     {
     156          47 :         if (!IsValidFast())
     157             :         {
     158           0 :             empty();
     159           0 :             return OGRERR_CORRUPT_DATA;
     160             :         }
     161             :     }
     162          47 :     return eErr;
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                            exportToWkb()                             */
     167             : /*                                                                      */
     168             : /*      Build a well known binary representation of this object.        */
     169             : /************************************************************************/
     170             : 
     171             : OGRErr
     172          55 : OGRCircularString::exportToWkb(unsigned char *pabyData,
     173             :                                const OGRwkbExportOptions *psOptions) const
     174             : 
     175             : {
     176          55 :     if (!IsValidFast())
     177             :     {
     178           0 :         return OGRERR_FAILURE;
     179             :     }
     180             : 
     181             :     OGRwkbExportOptions sOptions(psOptions ? *psOptions
     182          55 :                                            : OGRwkbExportOptions());
     183             : 
     184             :     // Does not make sense for new geometries, so patch it.
     185          55 :     if (sOptions.eWkbVariant == wkbVariantOldOgc)
     186          11 :         sOptions.eWkbVariant = wkbVariantIso;
     187          55 :     return OGRSimpleCurve::exportToWkb(pabyData, &sOptions);
     188             : }
     189             : 
     190             : /************************************************************************/
     191             : /*                           importFromWkt()                            */
     192             : /*                                                                      */
     193             : /*      Instantiate from well known text format.  Currently this is     */
     194             : /*      `CIRCULARSTRING [Z] ( x y [z], x y [z], ...)',                  */
     195             : /************************************************************************/
     196             : 
     197        3503 : OGRErr OGRCircularString::importFromWkt(const char **ppszInput)
     198             : 
     199             : {
     200        3503 :     const OGRErr eErr = OGRSimpleCurve::importFromWkt(ppszInput);
     201        3503 :     if (eErr == OGRERR_NONE)
     202             :     {
     203        3503 :         if (!IsValidFast())
     204             :         {
     205           1 :             empty();
     206           1 :             return OGRERR_CORRUPT_DATA;
     207             :         }
     208             :     }
     209        3502 :     return eErr;
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /*                            exportToWkt()                             */
     214             : /************************************************************************/
     215             : 
     216         225 : std::string OGRCircularString::exportToWkt(const OGRWktOptions &opts,
     217             :                                            OGRErr *err) const
     218             : {
     219         225 :     if (!IsValidFast())
     220             :     {
     221           0 :         if (err)
     222           0 :             *err = OGRERR_FAILURE;
     223           0 :         return std::string();
     224             :     }
     225             : 
     226         225 :     OGRWktOptions optsModified(opts);
     227         225 :     optsModified.variant = wkbVariantIso;
     228         225 :     return OGRSimpleCurve::exportToWkt(optsModified, err);
     229             : }
     230             : 
     231             : /************************************************************************/
     232             : /*                             get_Length()                             */
     233             : /*                                                                      */
     234             : /*      For now we return a simple euclidean 2D distance.               */
     235             : /************************************************************************/
     236             : 
     237          16 : double OGRCircularString::get_Length() const
     238             : 
     239             : {
     240          16 :     double dfLength = 0.0;
     241          40 :     for (int i = 0; i < nPointCount - 2; i += 2)
     242             :     {
     243          24 :         const double x0 = paoPoints[i].x;
     244          24 :         const double y0 = paoPoints[i].y;
     245          24 :         const double x1 = paoPoints[i + 1].x;
     246          24 :         const double y1 = paoPoints[i + 1].y;
     247          24 :         const double x2 = paoPoints[i + 2].x;
     248          24 :         const double y2 = paoPoints[i + 2].y;
     249          24 :         double R = 0.0;
     250          24 :         double cx = 0.0;
     251          24 :         double cy = 0.0;
     252          24 :         double alpha0 = 0.0;
     253          24 :         double alpha1 = 0.0;
     254          24 :         double alpha2 = 0.0;
     255          24 :         if (OGRGeometryFactory::GetCurveParameters(
     256          24 :                 x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
     257             :         {
     258          17 :             dfLength += fabs(alpha2 - alpha0) * R;
     259             :         }
     260             :         else
     261             :         {
     262           7 :             dfLength += dist(x0, y0, x2, y2);
     263             :         }
     264             :     }
     265          16 :     return dfLength;
     266             : }
     267             : 
     268             : /************************************************************************/
     269             : /*                       ExtendEnvelopeWithCircular()                   */
     270             : /************************************************************************/
     271             : 
     272         269 : void OGRCircularString::ExtendEnvelopeWithCircular(
     273             :     OGREnvelope *psEnvelope) const
     274             : {
     275         269 :     if (!IsValidFast() || nPointCount == 0)
     276           0 :         return;
     277             : 
     278             :     // Loop through circular portions and determine if they include some
     279             :     // extremities of the circle.
     280         598 :     for (int i = 0; i < nPointCount - 2; i += 2)
     281             :     {
     282         329 :         const double x0 = paoPoints[i].x;
     283         329 :         const double y0 = paoPoints[i].y;
     284         329 :         const double x1 = paoPoints[i + 1].x;
     285         329 :         const double y1 = paoPoints[i + 1].y;
     286         329 :         const double x2 = paoPoints[i + 2].x;
     287         329 :         const double y2 = paoPoints[i + 2].y;
     288         329 :         double R = 0.0;
     289         329 :         double cx = 0.0;
     290         329 :         double cy = 0.0;
     291         329 :         double alpha0 = 0.0;
     292         329 :         double alpha1 = 0.0;
     293         329 :         double alpha2 = 0.0;
     294         329 :         if (OGRGeometryFactory::GetCurveParameters(
     295         329 :                 x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
     296             :         {
     297         329 :             if (CPLIsNan(alpha0) || CPLIsNan(alpha2))
     298             :             {
     299           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     300             :                          "GetCurveParameters returned NaN");
     301           0 :                 continue;
     302             :             }
     303         329 :             int quadrantStart =
     304         329 :                 static_cast<int>(std::floor(alpha0 / (M_PI / 2)));
     305         329 :             int quadrantEnd = static_cast<int>(std::floor(alpha2 / (M_PI / 2)));
     306         329 :             if (quadrantStart > quadrantEnd)
     307             :             {
     308         212 :                 std::swap(quadrantStart, quadrantEnd);
     309             :             }
     310             :             // Transition trough quadrants in counter-clock wise direction.
     311        1030 :             for (int j = quadrantStart + 1; j <= quadrantEnd; ++j)
     312             :             {
     313         701 :                 switch ((j + 8) % 4)
     314             :                 {
     315         153 :                     case 0:
     316         153 :                         psEnvelope->MaxX = std::max(psEnvelope->MaxX, cx + R);
     317         153 :                         break;
     318         221 :                     case 1:
     319         221 :                         psEnvelope->MaxY = std::max(psEnvelope->MaxY, cy + R);
     320         221 :                         break;
     321         207 :                     case 2:
     322         207 :                         psEnvelope->MinX = std::min(psEnvelope->MinX, cx - R);
     323         207 :                         break;
     324         120 :                     case 3:
     325         120 :                         psEnvelope->MinY = std::min(psEnvelope->MaxY, cy - R);
     326         120 :                         break;
     327           0 :                     default:
     328           0 :                         CPLAssert(false);
     329             :                         break;
     330             :                 }
     331             :             }
     332             :         }
     333             :     }
     334             : }
     335             : 
     336             : /************************************************************************/
     337             : /*                            getEnvelope()                             */
     338             : /************************************************************************/
     339             : 
     340         147 : void OGRCircularString::getEnvelope(OGREnvelope *psEnvelope) const
     341             : 
     342             : {
     343         147 :     OGRSimpleCurve::getEnvelope(psEnvelope);
     344         147 :     ExtendEnvelopeWithCircular(psEnvelope);
     345         147 : }
     346             : 
     347             : /************************************************************************/
     348             : /*                            getEnvelope()                             */
     349             : /************************************************************************/
     350             : 
     351         122 : void OGRCircularString::getEnvelope(OGREnvelope3D *psEnvelope) const
     352             : 
     353             : {
     354         122 :     OGRSimpleCurve::getEnvelope(psEnvelope);
     355         122 :     ExtendEnvelopeWithCircular(psEnvelope);
     356         122 : }
     357             : 
     358             : /************************************************************************/
     359             : /*                     OGRCircularString::segmentize()                  */
     360             : /************************************************************************/
     361             : 
     362          11 : void OGRCircularString::segmentize(double dfMaxLength)
     363             : {
     364          11 :     if (!IsValidFast() || nPointCount == 0)
     365           0 :         return;
     366             : 
     367             :     // So as to make sure that the same line followed in both directions
     368             :     // result in the same segmentized line.
     369          11 :     if (paoPoints[0].x < paoPoints[nPointCount - 1].x ||
     370           7 :         (paoPoints[0].x == paoPoints[nPointCount - 1].x &&
     371           2 :          paoPoints[0].y < paoPoints[nPointCount - 1].y))
     372             :     {
     373           4 :         reversePoints();
     374           4 :         segmentize(dfMaxLength);
     375           4 :         reversePoints();
     376             :     }
     377             : 
     378          22 :     std::vector<OGRRawPoint> aoRawPoint;
     379          22 :     std::vector<double> adfZ;
     380          44 :     for (int i = 0; i < nPointCount - 2; i += 2)
     381             :     {
     382          33 :         const double x0 = paoPoints[i].x;
     383          33 :         const double y0 = paoPoints[i].y;
     384          33 :         const double x1 = paoPoints[i + 1].x;
     385          33 :         const double y1 = paoPoints[i + 1].y;
     386          33 :         const double x2 = paoPoints[i + 2].x;
     387          33 :         const double y2 = paoPoints[i + 2].y;
     388          33 :         double R = 0.0;
     389          33 :         double cx = 0.0;
     390          33 :         double cy = 0.0;
     391          33 :         double alpha0 = 0.0;
     392          33 :         double alpha1 = 0.0;
     393          33 :         double alpha2 = 0.0;
     394             : 
     395          33 :         aoRawPoint.emplace_back(x0, y0);
     396          33 :         if (padfZ)
     397           8 :             adfZ.emplace_back(padfZ[i]);
     398             : 
     399             :         // We have strong constraints on the number of intermediate points
     400             :         // we can add.
     401             : 
     402          33 :         if (OGRGeometryFactory::GetCurveParameters(
     403          33 :                 x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
     404             :         {
     405             :             // It is an arc circle.
     406          19 :             const double dfSegmentLength1 = fabs(alpha1 - alpha0) * R;
     407          19 :             const double dfSegmentLength2 = fabs(alpha2 - alpha1) * R;
     408          19 :             if (dfSegmentLength1 > dfMaxLength ||
     409             :                 dfSegmentLength2 > dfMaxLength)
     410             :             {
     411           6 :                 const double dfVal =
     412           6 :                     1 + 2 * std::floor(dfSegmentLength1 / dfMaxLength / 2.0);
     413          12 :                 if (dfVal >= std::numeric_limits<int>::max() || dfVal < 0.0 ||
     414           6 :                     CPLIsNan(dfVal))
     415             :                 {
     416           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     417             :                              "segmentize nIntermediatePoints invalid: %lf",
     418             :                              dfVal);
     419           0 :                     break;
     420             :                 }
     421           6 :                 const int nIntermediatePoints = static_cast<int>(dfVal);
     422           6 :                 const double dfStep =
     423           6 :                     (alpha1 - alpha0) / (nIntermediatePoints + 1);
     424          28 :                 for (int j = 1; j <= nIntermediatePoints; ++j)
     425             :                 {
     426          22 :                     double alpha = alpha0 + dfStep * j;
     427          22 :                     const double x = cx + R * cos(alpha);
     428          22 :                     const double y = cy + R * sin(alpha);
     429          22 :                     aoRawPoint.emplace_back(x, y);
     430          22 :                     if (padfZ)
     431             :                     {
     432           3 :                         const double z = padfZ[i] + (padfZ[i + 1] - padfZ[i]) *
     433           3 :                                                         (alpha - alpha0) /
     434           3 :                                                         (alpha1 - alpha0);
     435           3 :                         adfZ.emplace_back(z);
     436             :                     }
     437             :                 }
     438             :             }
     439          19 :             aoRawPoint.emplace_back(x1, y1);
     440          19 :             if (padfZ)
     441           1 :                 adfZ.emplace_back(padfZ[i + 1]);
     442             : 
     443          19 :             if (dfSegmentLength1 > dfMaxLength ||
     444             :                 dfSegmentLength2 > dfMaxLength)
     445             :             {
     446           6 :                 const double dfVal =
     447           6 :                     1 + 2 * std::floor(dfSegmentLength2 / dfMaxLength / 2.0);
     448          12 :                 if (dfVal >= std::numeric_limits<int>::max() || dfVal < 0.0 ||
     449           6 :                     CPLIsNan(dfVal))
     450             :                 {
     451           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     452             :                              "segmentize nIntermediatePoints invalid 2: %lf",
     453             :                              dfVal);
     454           0 :                     break;
     455             :                 }
     456           6 :                 int nIntermediatePoints = static_cast<int>(dfVal);
     457           6 :                 const double dfStep =
     458           6 :                     (alpha2 - alpha1) / (nIntermediatePoints + 1);
     459          28 :                 for (int j = 1; j <= nIntermediatePoints; ++j)
     460             :                 {
     461          22 :                     const double alpha = alpha1 + dfStep * j;
     462          22 :                     const double x = cx + R * cos(alpha);
     463          22 :                     const double y = cy + R * sin(alpha);
     464          22 :                     aoRawPoint.emplace_back(x, y);
     465          22 :                     if (padfZ)
     466             :                     {
     467           3 :                         const double z =
     468           3 :                             padfZ[i + 1] + (padfZ[i + 2] - padfZ[i + 1]) *
     469           3 :                                                (alpha - alpha1) /
     470           3 :                                                (alpha2 - alpha1);
     471           3 :                         adfZ.emplace_back(z);
     472             :                     }
     473             :                 }
     474             :             }
     475             :         }
     476             :         else
     477             :         {
     478             :             // It is a straight line.
     479          14 :             const double dfSegmentLength1 = dist(x0, y0, x1, y1);
     480          14 :             const double dfSegmentLength2 = dist(x1, y1, x2, y2);
     481          14 :             if (dfSegmentLength1 > dfMaxLength ||
     482             :                 dfSegmentLength2 > dfMaxLength)
     483             :             {
     484           2 :                 const double dfVal =
     485           2 :                     1 + 2 * std::ceil(dfSegmentLength1 / dfMaxLength / 2.0);
     486           4 :                 if (dfVal >= std::numeric_limits<int>::max() || dfVal < 0.0 ||
     487           2 :                     CPLIsNan(dfVal))
     488             :                 {
     489           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     490             :                              "segmentize nIntermediatePoints invalid 2: %lf",
     491             :                              dfVal);
     492           0 :                     break;
     493             :                 }
     494           2 :                 int nIntermediatePoints = static_cast<int>(dfVal);
     495          12 :                 for (int j = 1; j <= nIntermediatePoints; ++j)
     496             :                 {
     497             :                     aoRawPoint.emplace_back(
     498           0 :                         x0 + j * (x1 - x0) / (nIntermediatePoints + 1),
     499          10 :                         y0 + j * (y1 - y0) / (nIntermediatePoints + 1));
     500          10 :                     if (padfZ)
     501           5 :                         adfZ.emplace_back(padfZ[i] +
     502           5 :                                           j * (padfZ[i + 1] - padfZ[i]) /
     503           5 :                                               (nIntermediatePoints + 1));
     504             :                 }
     505             :             }
     506             : 
     507          14 :             aoRawPoint.emplace_back(x1, y1);
     508          14 :             if (padfZ)
     509           7 :                 adfZ.emplace_back(padfZ[i + 1]);
     510             : 
     511          14 :             if (dfSegmentLength1 > dfMaxLength ||
     512             :                 dfSegmentLength2 > dfMaxLength)
     513             :             {
     514           2 :                 const double dfVal =
     515           2 :                     1 + 2 * std::ceil(dfSegmentLength2 / dfMaxLength / 2.0);
     516           4 :                 if (dfVal >= std::numeric_limits<int>::max() || dfVal < 0.0 ||
     517           2 :                     CPLIsNan(dfVal))
     518             :                 {
     519           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     520             :                              "segmentize nIntermediatePoints invalid 3: %lf",
     521             :                              dfVal);
     522           0 :                     break;
     523             :                 }
     524           2 :                 const int nIntermediatePoints = static_cast<int>(dfVal);
     525             : 
     526          12 :                 for (int j = 1; j <= nIntermediatePoints; ++j)
     527             :                 {
     528             :                     aoRawPoint.emplace_back(
     529           0 :                         x1 + j * (x2 - x1) / (nIntermediatePoints + 1),
     530          10 :                         y1 + j * (y2 - y1) / (nIntermediatePoints + 1));
     531          10 :                     if (padfZ)
     532           5 :                         adfZ.emplace_back(padfZ[i + 1] +
     533           5 :                                           j * (padfZ[i + 2] - padfZ[i + 1]) /
     534           5 :                                               (nIntermediatePoints + 1));
     535             :                 }
     536             :             }
     537             :         }
     538             :     }
     539          11 :     aoRawPoint.push_back(paoPoints[nPointCount - 1]);
     540          11 :     if (padfZ)
     541           3 :         adfZ.push_back(padfZ[nPointCount - 1]);
     542             : 
     543          11 :     CPLAssert(aoRawPoint.empty() ||
     544             :               (aoRawPoint.size() >= 3 && (aoRawPoint.size() % 2) == 1));
     545          11 :     if (padfZ)
     546             :     {
     547           3 :         CPLAssert(adfZ.size() == aoRawPoint.size());
     548             :     }
     549             : 
     550             :     // Is there actually something to modify?
     551          11 :     if (nPointCount < static_cast<int>(aoRawPoint.size()))
     552             :     {
     553           7 :         nPointCount = static_cast<int>(aoRawPoint.size());
     554           7 :         paoPoints = static_cast<OGRRawPoint *>(
     555           7 :             CPLRealloc(paoPoints, sizeof(OGRRawPoint) * nPointCount));
     556           7 :         memcpy(paoPoints, &aoRawPoint[0], sizeof(OGRRawPoint) * nPointCount);
     557           7 :         if (padfZ)
     558             :         {
     559           2 :             padfZ = static_cast<double *>(
     560           2 :                 CPLRealloc(padfZ, sizeof(double) * aoRawPoint.size()));
     561           2 :             memcpy(padfZ, &adfZ[0], sizeof(double) * nPointCount);
     562             :         }
     563             :     }
     564             : }
     565             : 
     566             : /************************************************************************/
     567             : /*                               Value()                                */
     568             : /*                                                                      */
     569             : /*      Get an interpolated point at some distance along the curve.     */
     570             : /************************************************************************/
     571             : 
     572          14 : void OGRCircularString::Value(double dfDistance, OGRPoint *poPoint) const
     573             : 
     574             : {
     575          14 :     if (dfDistance < 0)
     576             :     {
     577           1 :         StartPoint(poPoint);
     578           1 :         return;
     579             :     }
     580             : 
     581          13 :     double dfLength = 0;
     582             : 
     583          19 :     for (int i = 0; i < nPointCount - 2; i += 2)
     584             :     {
     585          18 :         const double x0 = paoPoints[i].x;
     586          18 :         const double y0 = paoPoints[i].y;
     587          18 :         const double x1 = paoPoints[i + 1].x;
     588          18 :         const double y1 = paoPoints[i + 1].y;
     589          18 :         const double x2 = paoPoints[i + 2].x;
     590          18 :         const double y2 = paoPoints[i + 2].y;
     591          18 :         double R = 0.0;
     592          18 :         double cx = 0.0;
     593          18 :         double cy = 0.0;
     594          18 :         double alpha0 = 0.0;
     595          18 :         double alpha1 = 0.0;
     596          18 :         double alpha2 = 0.0;
     597             : 
     598             :         // We have strong constraints on the number of intermediate points
     599             :         // we can add.
     600             : 
     601          18 :         if (OGRGeometryFactory::GetCurveParameters(
     602          18 :                 x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
     603             :         {
     604             :             // It is an arc circle.
     605           9 :             const double dfSegLength = fabs(alpha2 - alpha0) * R;
     606           9 :             if (dfSegLength > 0)
     607             :             {
     608           9 :                 if ((dfLength <= dfDistance) &&
     609           9 :                     ((dfLength + dfSegLength) >= dfDistance))
     610             :                 {
     611           8 :                     const double dfRatio =
     612           8 :                         (dfDistance - dfLength) / dfSegLength;
     613             : 
     614           8 :                     const double alpha =
     615           8 :                         alpha0 * (1 - dfRatio) + alpha2 * dfRatio;
     616           8 :                     const double x = cx + R * cos(alpha);
     617           8 :                     const double y = cy + R * sin(alpha);
     618             : 
     619           8 :                     poPoint->setX(x);
     620           8 :                     poPoint->setY(y);
     621             : 
     622           8 :                     if (getCoordinateDimension() == 3)
     623           2 :                         poPoint->setZ(padfZ[i] * (1 - dfRatio) +
     624           2 :                                       padfZ[i + 2] * dfRatio);
     625             : 
     626          12 :                     return;
     627             :                 }
     628             : 
     629           1 :                 dfLength += dfSegLength;
     630             :             }
     631             :         }
     632             :         else
     633             :         {
     634             :             // It is a straight line.
     635           9 :             const double dfSegLength = dist(x0, y0, x2, y2);
     636           9 :             if (dfSegLength > 0)
     637             :             {
     638           9 :                 if ((dfLength <= dfDistance) &&
     639           9 :                     ((dfLength + dfSegLength) >= dfDistance))
     640             :                 {
     641           4 :                     const double dfRatio =
     642           4 :                         (dfDistance - dfLength) / dfSegLength;
     643             : 
     644           4 :                     poPoint->setX(paoPoints[i].x * (1 - dfRatio) +
     645           4 :                                   paoPoints[i + 2].x * dfRatio);
     646           4 :                     poPoint->setY(paoPoints[i].y * (1 - dfRatio) +
     647           4 :                                   paoPoints[i + 2].y * dfRatio);
     648             : 
     649           4 :                     if (getCoordinateDimension() == 3)
     650           2 :                         poPoint->setZ(padfZ[i] * (1 - dfRatio) +
     651           2 :                                       padfZ[i + 2] * dfRatio);
     652             : 
     653           4 :                     return;
     654             :                 }
     655             : 
     656           5 :                 dfLength += dfSegLength;
     657             :             }
     658             :         }
     659             :     }
     660             : 
     661           1 :     EndPoint(poPoint);
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                          CurveToLine()                               */
     666             : /************************************************************************/
     667             : 
     668             : OGRLineString *
     669        4018 : OGRCircularString::CurveToLine(double dfMaxAngleStepSizeDegrees,
     670             :                                const char *const *papszOptions) const
     671             : {
     672        4018 :     OGRLineString *poLine = new OGRLineString();
     673        4018 :     poLine->assignSpatialReference(getSpatialReference());
     674             : 
     675        4018 :     const bool bHasZ = getCoordinateDimension() == 3;
     676        8227 :     for (int i = 0; i < nPointCount - 2; i += 2)
     677             :     {
     678       11871 :         OGRLineString *poArc = OGRGeometryFactory::curveToLineString(
     679        4209 :             paoPoints[i].x, paoPoints[i].y, padfZ ? padfZ[i] : 0.0,
     680        4209 :             paoPoints[i + 1].x, paoPoints[i + 1].y, padfZ ? padfZ[i + 1] : 0.0,
     681        8166 :             paoPoints[i + 2].x, paoPoints[i + 2].y, padfZ ? padfZ[i + 2] : 0.0,
     682             :             bHasZ, dfMaxAngleStepSizeDegrees, papszOptions);
     683        4209 :         poLine->addSubLineString(poArc, (i == 0) ? 0 : 1);
     684        4209 :         delete poArc;
     685             :     }
     686        4018 :     return poLine;
     687             : }
     688             : 
     689             : /************************************************************************/
     690             : /*                        IsValidFast()                                 */
     691             : /************************************************************************/
     692             : 
     693        4112 : OGRBoolean OGRCircularString::IsValidFast() const
     694             : 
     695             : {
     696        4112 :     if (nPointCount == 1 || nPointCount == 2 ||
     697        4111 :         (nPointCount >= 3 && (nPointCount % 2) == 0))
     698             :     {
     699           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     700           1 :                  "Bad number of points in circular string : %d", nPointCount);
     701           1 :         return FALSE;
     702             :     }
     703        4111 :     return TRUE;
     704             : }
     705             : 
     706             : /************************************************************************/
     707             : /*                            IsValid()                                 */
     708             : /************************************************************************/
     709             : 
     710           2 : OGRBoolean OGRCircularString::IsValid() const
     711             : 
     712             : {
     713           2 :     return IsValidFast() && OGRGeometry::IsValid();
     714             : }
     715             : 
     716             : /************************************************************************/
     717             : /*                         hasCurveGeometry()                           */
     718             : /************************************************************************/
     719             : 
     720             : OGRBoolean
     721        3844 : OGRCircularString::hasCurveGeometry(int /* bLookForNonLinear */) const
     722             : {
     723        3844 :     return TRUE;
     724             : }
     725             : 
     726             : /************************************************************************/
     727             : /*                         getLinearGeometry()                        */
     728             : /************************************************************************/
     729             : 
     730             : OGRGeometry *
     731        3288 : OGRCircularString::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
     732             :                                      const char *const *papszOptions) const
     733             : {
     734        3288 :     return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions);
     735             : }
     736             : 
     737             : //! @cond Doxygen_Suppress
     738             : /************************************************************************/
     739             : /*                     GetCasterToLineString()                          */
     740             : /************************************************************************/
     741             : 
     742           0 : static OGRLineString *CasterToLineString(OGRCurve *poGeom)
     743             : {
     744           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
     745           0 :              poGeom->getGeometryName());
     746           0 :     delete poGeom;
     747           0 :     return nullptr;
     748             : }
     749             : 
     750           0 : OGRCurveCasterToLineString OGRCircularString::GetCasterToLineString() const
     751             : {
     752           0 :     return ::CasterToLineString;
     753             : }
     754             : 
     755             : /************************************************************************/
     756             : /*                        GetCasterToLinearRing()                       */
     757             : /************************************************************************/
     758             : 
     759           0 : static OGRLinearRing *CasterToLinearRing(OGRCurve *poGeom)
     760             : {
     761           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
     762           0 :              poGeom->getGeometryName());
     763           0 :     delete poGeom;
     764           0 :     return nullptr;
     765             : }
     766             : 
     767           0 : OGRCurveCasterToLinearRing OGRCircularString::GetCasterToLinearRing() const
     768             : {
     769           0 :     return ::CasterToLinearRing;
     770             : }
     771             : 
     772             : //! @endcond
     773             : 
     774             : /************************************************************************/
     775             : /*                            IsFullCircle()                            */
     776             : /************************************************************************/
     777             : 
     778          12 : int OGRCircularString::IsFullCircle(double &cx, double &cy,
     779             :                                     double &square_R) const
     780             : {
     781          12 :     if (getNumPoints() == 3 && get_IsClosed())
     782             :     {
     783           7 :         const double x0 = getX(0);
     784           7 :         const double y0 = getY(0);
     785           7 :         const double x1 = getX(1);
     786           7 :         const double y1 = getY(1);
     787           7 :         cx = (x0 + x1) / 2;
     788           7 :         cy = (y0 + y1) / 2;
     789           7 :         square_R = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy);
     790           7 :         return TRUE;
     791             :     }
     792             :     // Full circle defined by 2 arcs?
     793           5 :     else if (getNumPoints() == 5 && get_IsClosed())
     794             :     {
     795           4 :         double R_1 = 0.0;
     796           4 :         double cx_1 = 0.0;
     797           4 :         double cy_1 = 0.0;
     798           4 :         double alpha0_1 = 0.0;
     799           4 :         double alpha1_1 = 0.0;
     800           4 :         double alpha2_1 = 0.0;
     801           4 :         double R_2 = 0.0;
     802           4 :         double cx_2 = 0.0;
     803           4 :         double cy_2 = 0.0;
     804           4 :         double alpha0_2 = 0.0;
     805           4 :         double alpha1_2 = 0.0;
     806           4 :         double alpha2_2 = 0.0;
     807           4 :         if (OGRGeometryFactory::GetCurveParameters(
     808             :                 getX(0), getY(0), getX(1), getY(1), getX(2), getY(2), R_1, cx_1,
     809           4 :                 cy_1, alpha0_1, alpha1_1, alpha2_1) &&
     810           4 :             OGRGeometryFactory::GetCurveParameters(
     811             :                 getX(2), getY(2), getX(3), getY(3), getX(4), getY(4), R_2, cx_2,
     812           4 :                 cy_2, alpha0_2, alpha1_2, alpha2_2) &&
     813           4 :             fabs(R_1 - R_2) < 1e-10 && fabs(cx_1 - cx_2) < 1e-10 &&
     814          12 :             fabs(cy_1 - cy_2) < 1e-10 &&
     815           4 :             (alpha2_1 - alpha0_1) * (alpha2_2 - alpha0_2) > 0)
     816             :         {
     817           3 :             cx = cx_1;
     818           3 :             cy = cy_1;
     819           3 :             square_R = R_1 * R_1;
     820           3 :             return TRUE;
     821             :         }
     822             :     }
     823           2 :     return FALSE;
     824             : }
     825             : 
     826             : /************************************************************************/
     827             : /*                       get_AreaOfCurveSegments()                      */
     828             : /************************************************************************/
     829             : 
     830             : //! @cond Doxygen_Suppress
     831          25 : double OGRCircularString::get_AreaOfCurveSegments() const
     832             : {
     833          25 :     double dfArea = 0.0;
     834          53 :     for (int i = 0; i < getNumPoints() - 2; i += 2)
     835             :     {
     836          28 :         const double x0 = getX(i);
     837          28 :         const double y0 = getY(i);
     838          28 :         const double x1 = getX(i + 1);
     839          28 :         const double y1 = getY(i + 1);
     840          28 :         const double x2 = getX(i + 2);
     841          28 :         const double y2 = getY(i + 2);
     842          28 :         double R = 0.0;
     843          28 :         double cx = 0.0;
     844          28 :         double cy = 0.0;
     845          28 :         double alpha0 = 0.0;
     846          28 :         double alpha1 = 0.0;
     847          28 :         double alpha2 = 0.0;
     848          28 :         if (OGRGeometryFactory::GetCurveParameters(
     849          28 :                 x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2))
     850             :         {
     851             :             // Should be <= PI in absolute value.
     852          26 :             const double delta_alpha01 = alpha1 - alpha0;
     853          26 :             const double delta_alpha12 = alpha2 - alpha1;  // Same.
     854             :             // http://en.wikipedia.org/wiki/Circular_segment
     855          26 :             dfArea += 0.5 * R * R *
     856          26 :                       fabs(delta_alpha01 - sin(delta_alpha01) + delta_alpha12 -
     857          26 :                            sin(delta_alpha12));
     858             :         }
     859             :     }
     860          25 :     return dfArea;
     861             : }
     862             : 
     863             : //! @endcond
     864             : 
     865             : /************************************************************************/
     866             : /*                           get_Area()                                 */
     867             : /************************************************************************/
     868             : 
     869           5 : double OGRCircularString::get_Area() const
     870             : {
     871           5 :     if (IsEmpty() || !get_IsClosed())
     872           1 :         return 0;
     873             : 
     874           4 :     double cx = 0.0;
     875           4 :     double cy = 0.0;
     876           4 :     double square_R = 0.0;
     877             : 
     878           4 :     if (IsFullCircle(cx, cy, square_R))
     879             :     {
     880           3 :         return M_PI * square_R;
     881             :     }
     882             : 
     883             :     // Optimization for convex rings.
     884           1 :     if (IsConvex())
     885             :     {
     886             :         // Compute area of shape without the circular segments.
     887           1 :         double dfArea = get_LinearArea();
     888             : 
     889             :         // Add the area of the spherical segments.
     890           1 :         dfArea += get_AreaOfCurveSegments();
     891             : 
     892           1 :         return dfArea;
     893             :     }
     894             : 
     895           0 :     OGRLineString *poLS = CurveToLine();
     896           0 :     const double dfArea = poLS->get_Area();
     897           0 :     delete poLS;
     898             : 
     899           0 :     return dfArea;
     900             : }
     901             : 
     902             : /************************************************************************/
     903             : /*                        get_GeodesicArea()                            */
     904             : /************************************************************************/
     905             : 
     906           4 : double OGRCircularString::get_GeodesicArea(
     907             :     const OGRSpatialReference *poSRSOverride) const
     908             : {
     909           4 :     if (IsEmpty())
     910           1 :         return 0;
     911             : 
     912           3 :     if (!get_IsClosed())
     913             :     {
     914           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Non-closed geometry");
     915           2 :         return -1;
     916             :     }
     917             : 
     918           1 :     if (!poSRSOverride)
     919           1 :         poSRSOverride = getSpatialReference();
     920             : 
     921           1 :     OGRLineString *poLS = CurveToLine();
     922           1 :     const double dfArea = poLS->get_GeodesicArea(poSRSOverride);
     923           1 :     delete poLS;
     924             : 
     925           1 :     return dfArea;
     926             : }
     927             : 
     928             : //! @cond Doxygen_Suppress
     929             : 
     930             : /************************************************************************/
     931             : /*                           ContainsPoint()                            */
     932             : /************************************************************************/
     933             : 
     934           6 : int OGRCircularString::ContainsPoint(const OGRPoint *p) const
     935             : {
     936           6 :     double cx = 0.0;
     937           6 :     double cy = 0.0;
     938           6 :     double square_R = 0.0;
     939           6 :     if (IsFullCircle(cx, cy, square_R))
     940             :     {
     941           5 :         const double square_dist = (p->getX() - cx) * (p->getX() - cx) +
     942           5 :                                    (p->getY() - cy) * (p->getY() - cy);
     943           5 :         return square_dist < square_R;
     944             :     }
     945           1 :     return -1;
     946             : }
     947             : 
     948             : /************************************************************************/
     949             : /*                       IntersectsPoint()                              */
     950             : /************************************************************************/
     951             : 
     952           2 : int OGRCircularString::IntersectsPoint(const OGRPoint *p) const
     953             : {
     954           2 :     double cx = 0.0;
     955           2 :     double cy = 0.0;
     956           2 :     double square_R = 0.0;
     957           2 :     if (IsFullCircle(cx, cy, square_R))
     958             :     {
     959           2 :         const double square_dist = (p->getX() - cx) * (p->getX() - cx) +
     960           2 :                                    (p->getY() - cy) * (p->getY() - cy);
     961           2 :         return square_dist <= square_R;
     962             :     }
     963           0 :     return -1;
     964             : }
     965             : 
     966             : //! @endcond

Generated by: LCOV version 1.14