LCOV - code coverage report
Current view: top level - ogr - ogrgeometryfactory.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2283 2515 90.8 %
Date: 2024-05-03 15:49:35 Functions: 74 78 94.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Factory for converting geometry to and from well known binary
       5             :  *           format.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Frank Warmerdam
      10             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys dot com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "cpl_port.h"
      32             : 
      33             : #include "cpl_conv.h"
      34             : #include "cpl_error.h"
      35             : #include "cpl_string.h"
      36             : #include "ogr_geometry.h"
      37             : #include "ogr_api.h"
      38             : #include "ogr_core.h"
      39             : #include "ogr_geos.h"
      40             : #include "ogr_sfcgal.h"
      41             : #include "ogr_p.h"
      42             : #include "ogr_spatialref.h"
      43             : #include "ogr_srs_api.h"
      44             : #ifdef HAVE_GEOS
      45             : #include "geos_c.h"
      46             : #endif
      47             : #include "ogrgeojsonreader.h"
      48             : 
      49             : #include <cassert>
      50             : #include <climits>
      51             : #include <cmath>
      52             : #include <cstdlib>
      53             : #include <cstring>
      54             : #include <cstddef>
      55             : 
      56             : #include <algorithm>
      57             : #include <limits>
      58             : #include <new>
      59             : #include <utility>
      60             : #include <vector>
      61             : 
      62             : #ifndef HAVE_GEOS
      63             : #define UNUSED_IF_NO_GEOS CPL_UNUSED
      64             : #else
      65             : #define UNUSED_IF_NO_GEOS
      66             : #endif
      67             : 
      68             : /************************************************************************/
      69             : /*                           createFromWkb()                            */
      70             : /************************************************************************/
      71             : 
      72             : /**
      73             :  * \brief Create a geometry object of the appropriate type from its
      74             :  * well known binary representation.
      75             :  *
      76             :  * Note that if nBytes is passed as zero, no checking can be done on whether
      77             :  * the pabyData is sufficient.  This can result in a crash if the input
      78             :  * data is corrupt.  This function returns no indication of the number of
      79             :  * bytes from the data source actually used to represent the returned
      80             :  * geometry object.  Use OGRGeometry::WkbSize() on the returned geometry to
      81             :  * establish the number of bytes it required in WKB format.
      82             :  *
      83             :  * Also note that this is a static method, and that there
      84             :  * is no need to instantiate an OGRGeometryFactory object.
      85             :  *
      86             :  * The C function OGR_G_CreateFromWkb() is the same as this method.
      87             :  *
      88             :  * @param pabyData pointer to the input BLOB data.
      89             :  * @param poSR pointer to the spatial reference to be assigned to the
      90             :  *             created geometry object.  This may be NULL.
      91             :  * @param ppoReturn the newly created geometry object will be assigned to the
      92             :  *                  indicated pointer on return.  This will be NULL in case
      93             :  *                  of failure. If not NULL, *ppoReturn should be freed with
      94             :  *                  OGRGeometryFactory::destroyGeometry() after use.
      95             :  * @param nBytes the number of bytes available in pabyData, or -1 if it isn't
      96             :  *               known
      97             :  * @param eWkbVariant WKB variant.
      98             :  *
      99             :  * @return OGRERR_NONE if all goes well, otherwise any of
     100             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
     101             :  * OGRERR_CORRUPT_DATA may be returned.
     102             :  */
     103             : 
     104       65558 : OGRErr OGRGeometryFactory::createFromWkb(const void *pabyData,
     105             :                                          const OGRSpatialReference *poSR,
     106             :                                          OGRGeometry **ppoReturn, size_t nBytes,
     107             :                                          OGRwkbVariant eWkbVariant)
     108             : 
     109             : {
     110       65558 :     size_t nBytesConsumedOutIgnored = 0;
     111       65558 :     return createFromWkb(pabyData, poSR, ppoReturn, nBytes, eWkbVariant,
     112      131114 :                          nBytesConsumedOutIgnored);
     113             : }
     114             : 
     115             : /**
     116             :  * \brief Create a geometry object of the appropriate type from its
     117             :  * well known binary representation.
     118             :  *
     119             :  * Note that if nBytes is passed as zero, no checking can be done on whether
     120             :  * the pabyData is sufficient.  This can result in a crash if the input
     121             :  * data is corrupt.  This function returns no indication of the number of
     122             :  * bytes from the data source actually used to represent the returned
     123             :  * geometry object.  Use OGRGeometry::WkbSize() on the returned geometry to
     124             :  * establish the number of bytes it required in WKB format.
     125             :  *
     126             :  * Also note that this is a static method, and that there
     127             :  * is no need to instantiate an OGRGeometryFactory object.
     128             :  *
     129             :  * The C function OGR_G_CreateFromWkb() is the same as this method.
     130             :  *
     131             :  * @param pabyData pointer to the input BLOB data.
     132             :  * @param poSR pointer to the spatial reference to be assigned to the
     133             :  *             created geometry object.  This may be NULL.
     134             :  * @param ppoReturn the newly created geometry object will be assigned to the
     135             :  *                  indicated pointer on return.  This will be NULL in case
     136             :  *                  of failure. If not NULL, *ppoReturn should be freed with
     137             :  *                  OGRGeometryFactory::destroyGeometry() after use.
     138             :  * @param nBytes the number of bytes available in pabyData, or -1 if it isn't
     139             :  *               known
     140             :  * @param eWkbVariant WKB variant.
     141             :  * @param nBytesConsumedOut output parameter. Number of bytes consumed.
     142             :  *
     143             :  * @return OGRERR_NONE if all goes well, otherwise any of
     144             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
     145             :  * OGRERR_CORRUPT_DATA may be returned.
     146             :  * @since GDAL 2.3
     147             :  */
     148             : 
     149      103065 : OGRErr OGRGeometryFactory::createFromWkb(const void *pabyData,
     150             :                                          const OGRSpatialReference *poSR,
     151             :                                          OGRGeometry **ppoReturn, size_t nBytes,
     152             :                                          OGRwkbVariant eWkbVariant,
     153             :                                          size_t &nBytesConsumedOut)
     154             : 
     155             : {
     156      103065 :     const GByte *l_pabyData = static_cast<const GByte *>(pabyData);
     157      103065 :     nBytesConsumedOut = 0;
     158      103065 :     *ppoReturn = nullptr;
     159             : 
     160      103065 :     if (nBytes < 9 && nBytes != static_cast<size_t>(-1))
     161        1379 :         return OGRERR_NOT_ENOUGH_DATA;
     162             : 
     163             :     /* -------------------------------------------------------------------- */
     164             :     /*      Get the byte order byte.  The extra tests are to work around    */
     165             :     /*      bug sin the WKB of DB2 v7.2 as identified by Safe Software.     */
     166             :     /* -------------------------------------------------------------------- */
     167      101686 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*l_pabyData);
     168      101686 :     if (nByteOrder != wkbXDR && nByteOrder != wkbNDR)
     169             :     {
     170         354 :         CPLDebug("OGR",
     171             :                  "OGRGeometryFactory::createFromWkb() - got corrupt data.\n"
     172             :                  "%02X%02X%02X%02X%02X%02X%02X%02X%02X",
     173         354 :                  l_pabyData[0], l_pabyData[1], l_pabyData[2], l_pabyData[3],
     174         354 :                  l_pabyData[4], l_pabyData[5], l_pabyData[6], l_pabyData[7],
     175         354 :                  l_pabyData[8]);
     176         354 :         return OGRERR_CORRUPT_DATA;
     177             :     }
     178             : 
     179             :     /* -------------------------------------------------------------------- */
     180             :     /*      Get the geometry feature type.  For now we assume that          */
     181             :     /*      geometry type is between 0 and 255 so we only have to fetch     */
     182             :     /*      one byte.                                                       */
     183             :     /* -------------------------------------------------------------------- */
     184             : 
     185      101332 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
     186             :     const OGRErr err =
     187      101332 :         OGRReadWKBGeometryType(l_pabyData, eWkbVariant, &eGeometryType);
     188             : 
     189      101333 :     if (err != OGRERR_NONE)
     190         563 :         return err;
     191             : 
     192             :     /* -------------------------------------------------------------------- */
     193             :     /*      Instantiate a geometry of the appropriate type, and             */
     194             :     /*      initialize from the input stream.                               */
     195             :     /* -------------------------------------------------------------------- */
     196      100770 :     OGRGeometry *poGeom = createGeometry(eGeometryType);
     197             : 
     198      100769 :     if (poGeom == nullptr)
     199           0 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
     200             : 
     201             :     /* -------------------------------------------------------------------- */
     202             :     /*      Import from binary.                                             */
     203             :     /* -------------------------------------------------------------------- */
     204      201539 :     const OGRErr eErr = poGeom->importFromWkb(l_pabyData, nBytes, eWkbVariant,
     205      100769 :                                               nBytesConsumedOut);
     206      100770 :     if (eErr != OGRERR_NONE)
     207             :     {
     208        7315 :         delete poGeom;
     209        7315 :         return eErr;
     210             :     }
     211             : 
     212             :     /* -------------------------------------------------------------------- */
     213             :     /*      Assign spatial reference system.                                */
     214             :     /* -------------------------------------------------------------------- */
     215             : 
     216       97011 :     if (poGeom->hasCurveGeometry() &&
     217        3556 :         CPLTestBool(CPLGetConfigOption("OGR_STROKE_CURVE", "FALSE")))
     218             :     {
     219           5 :         OGRGeometry *poNewGeom = poGeom->getLinearGeometry();
     220           5 :         delete poGeom;
     221           5 :         poGeom = poNewGeom;
     222             :     }
     223       93455 :     poGeom->assignSpatialReference(poSR);
     224       93454 :     *ppoReturn = poGeom;
     225             : 
     226       93454 :     return OGRERR_NONE;
     227             : }
     228             : 
     229             : /************************************************************************/
     230             : /*                        OGR_G_CreateFromWkb()                         */
     231             : /************************************************************************/
     232             : /**
     233             :  * \brief Create a geometry object of the appropriate type from its
     234             :  * well known binary representation.
     235             :  *
     236             :  * Note that if nBytes is passed as zero, no checking can be done on whether
     237             :  * the pabyData is sufficient.  This can result in a crash if the input
     238             :  * data is corrupt.  This function returns no indication of the number of
     239             :  * bytes from the data source actually used to represent the returned
     240             :  * geometry object.  Use OGR_G_WkbSize() on the returned geometry to
     241             :  * establish the number of bytes it required in WKB format.
     242             :  *
     243             :  * The OGRGeometryFactory::createFromWkb() CPP method is the same as this
     244             :  * function.
     245             :  *
     246             :  * @param pabyData pointer to the input BLOB data.
     247             :  * @param hSRS handle to the spatial reference to be assigned to the
     248             :  *             created geometry object.  This may be NULL.
     249             :  * @param phGeometry the newly created geometry object will
     250             :  * be assigned to the indicated handle on return.  This will be NULL in case
     251             :  * of failure. If not NULL, *phGeometry should be freed with
     252             :  * OGR_G_DestroyGeometry() after use.
     253             :  * @param nBytes the number of bytes of data available in pabyData, or -1
     254             :  * if it is not known, but assumed to be sufficient.
     255             :  *
     256             :  * @return OGRERR_NONE if all goes well, otherwise any of
     257             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
     258             :  * OGRERR_CORRUPT_DATA may be returned.
     259             :  */
     260             : 
     261           2 : OGRErr CPL_DLL OGR_G_CreateFromWkb(const void *pabyData,
     262             :                                    OGRSpatialReferenceH hSRS,
     263             :                                    OGRGeometryH *phGeometry, int nBytes)
     264             : 
     265             : {
     266           2 :     return OGRGeometryFactory::createFromWkb(
     267           2 :         pabyData, OGRSpatialReference::FromHandle(hSRS),
     268           2 :         reinterpret_cast<OGRGeometry **>(phGeometry), nBytes);
     269             : }
     270             : 
     271             : /************************************************************************/
     272             : /*                      OGR_G_CreateFromWkbEx()                         */
     273             : /************************************************************************/
     274             : /**
     275             :  * \brief Create a geometry object of the appropriate type from its
     276             :  * well known binary representation.
     277             :  *
     278             :  * Note that if nBytes is passed as zero, no checking can be done on whether
     279             :  * the pabyData is sufficient.  This can result in a crash if the input
     280             :  * data is corrupt.  This function returns no indication of the number of
     281             :  * bytes from the data source actually used to represent the returned
     282             :  * geometry object.  Use OGR_G_WkbSizeEx() on the returned geometry to
     283             :  * establish the number of bytes it required in WKB format.
     284             :  *
     285             :  * The OGRGeometryFactory::createFromWkb() CPP method is the same as this
     286             :  * function.
     287             :  *
     288             :  * @param pabyData pointer to the input BLOB data.
     289             :  * @param hSRS handle to the spatial reference to be assigned to the
     290             :  *             created geometry object.  This may be NULL.
     291             :  * @param phGeometry the newly created geometry object will
     292             :  * be assigned to the indicated handle on return.  This will be NULL in case
     293             :  * of failure. If not NULL, *phGeometry should be freed with
     294             :  * OGR_G_DestroyGeometry() after use.
     295             :  * @param nBytes the number of bytes of data available in pabyData, or -1
     296             :  * if it is not known, but assumed to be sufficient.
     297             :  *
     298             :  * @return OGRERR_NONE if all goes well, otherwise any of
     299             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
     300             :  * OGRERR_CORRUPT_DATA may be returned.
     301             :  * @since GDAL 3.3
     302             :  */
     303             : 
     304       31028 : OGRErr CPL_DLL OGR_G_CreateFromWkbEx(const void *pabyData,
     305             :                                      OGRSpatialReferenceH hSRS,
     306             :                                      OGRGeometryH *phGeometry, size_t nBytes)
     307             : 
     308             : {
     309       31028 :     return OGRGeometryFactory::createFromWkb(
     310       31028 :         pabyData, OGRSpatialReference::FromHandle(hSRS),
     311       31028 :         reinterpret_cast<OGRGeometry **>(phGeometry), nBytes);
     312             : }
     313             : 
     314             : /************************************************************************/
     315             : /*                           createFromWkt()                            */
     316             : /************************************************************************/
     317             : 
     318             : /**
     319             :  * \brief Create a geometry object of the appropriate type from its
     320             :  * well known text representation.
     321             :  *
     322             :  * The C function OGR_G_CreateFromWkt() is the same as this method.
     323             :  *
     324             :  * @param ppszData input zero terminated string containing well known text
     325             :  *                representation of the geometry to be created.  The pointer
     326             :  *                is updated to point just beyond that last character consumed.
     327             :  * @param poSR pointer to the spatial reference to be assigned to the
     328             :  *             created geometry object.  This may be NULL.
     329             :  * @param ppoReturn the newly created geometry object will be assigned to the
     330             :  *                  indicated pointer on return.  This will be NULL if the
     331             :  *                  method fails. If not NULL, *ppoReturn should be freed with
     332             :  *                  OGRGeometryFactory::destroyGeometry() after use.
     333             :  *
     334             :  *  <b>Example:</b>
     335             :  *
     336             :  * \code{.cpp}
     337             :  *    const char* wkt= "POINT(0 0)";
     338             :  *
     339             :  *    // cast because OGR_G_CreateFromWkt will move the pointer
     340             :  *    char* pszWkt = (char*) wkt;
     341             :  *    OGRSpatialReferenceH ref = OSRNewSpatialReference(NULL);
     342             :  *    OGRGeometryH new_geom;
     343             :  *    OSRSetAxisMappingStrategy(poSR, OAMS_TRADITIONAL_GIS_ORDER);
     344             :  *    OGRErr err = OGR_G_CreateFromWkt(&pszWkt, ref, &new_geom);
     345             :  * \endcode
     346             :  *
     347             :  *
     348             :  *
     349             :  * @return OGRERR_NONE if all goes well, otherwise any of
     350             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
     351             :  * OGRERR_CORRUPT_DATA may be returned.
     352             :  */
     353             : 
     354      123304 : OGRErr OGRGeometryFactory::createFromWkt(const char **ppszData,
     355             :                                          const OGRSpatialReference *poSR,
     356             :                                          OGRGeometry **ppoReturn)
     357             : 
     358             : {
     359      123304 :     const char *pszInput = *ppszData;
     360      123304 :     *ppoReturn = nullptr;
     361             : 
     362             :     /* -------------------------------------------------------------------- */
     363             :     /*      Get the first token, which should be the geometry type.         */
     364             :     /* -------------------------------------------------------------------- */
     365      123304 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
     366      123304 :     if (OGRWktReadToken(pszInput, szToken) == nullptr)
     367           0 :         return OGRERR_CORRUPT_DATA;
     368             : 
     369             :     /* -------------------------------------------------------------------- */
     370             :     /*      Instantiate a geometry of the appropriate type.                 */
     371             :     /* -------------------------------------------------------------------- */
     372      123304 :     OGRGeometry *poGeom = nullptr;
     373      123304 :     if (STARTS_WITH_CI(szToken, "POINT"))
     374             :     {
     375       97491 :         poGeom = new OGRPoint();
     376             :     }
     377       25813 :     else if (STARTS_WITH_CI(szToken, "LINESTRING"))
     378             :     {
     379        1602 :         poGeom = new OGRLineString();
     380             :     }
     381       24211 :     else if (STARTS_WITH_CI(szToken, "POLYGON"))
     382             :     {
     383       15739 :         poGeom = new OGRPolygon();
     384             :     }
     385        8472 :     else if (STARTS_WITH_CI(szToken, "TRIANGLE"))
     386             :     {
     387          61 :         poGeom = new OGRTriangle();
     388             :     }
     389        8411 :     else if (STARTS_WITH_CI(szToken, "GEOMETRYCOLLECTION"))
     390             :     {
     391         523 :         poGeom = new OGRGeometryCollection();
     392             :     }
     393        7888 :     else if (STARTS_WITH_CI(szToken, "MULTIPOLYGON"))
     394             :     {
     395         937 :         poGeom = new OGRMultiPolygon();
     396             :     }
     397        6951 :     else if (STARTS_WITH_CI(szToken, "MULTIPOINT"))
     398             :     {
     399         578 :         poGeom = new OGRMultiPoint();
     400             :     }
     401        6373 :     else if (STARTS_WITH_CI(szToken, "MULTILINESTRING"))
     402             :     {
     403         637 :         poGeom = new OGRMultiLineString();
     404             :     }
     405        5736 :     else if (STARTS_WITH_CI(szToken, "CIRCULARSTRING"))
     406             :     {
     407        3501 :         poGeom = new OGRCircularString();
     408             :     }
     409        2235 :     else if (STARTS_WITH_CI(szToken, "COMPOUNDCURVE"))
     410             :     {
     411         289 :         poGeom = new OGRCompoundCurve();
     412             :     }
     413        1946 :     else if (STARTS_WITH_CI(szToken, "CURVEPOLYGON"))
     414             :     {
     415         318 :         poGeom = new OGRCurvePolygon();
     416             :     }
     417        1628 :     else if (STARTS_WITH_CI(szToken, "MULTICURVE"))
     418             :     {
     419         135 :         poGeom = new OGRMultiCurve();
     420             :     }
     421        1493 :     else if (STARTS_WITH_CI(szToken, "MULTISURFACE"))
     422             :     {
     423         152 :         poGeom = new OGRMultiSurface();
     424             :     }
     425             : 
     426        1341 :     else if (STARTS_WITH_CI(szToken, "POLYHEDRALSURFACE"))
     427             :     {
     428          63 :         poGeom = new OGRPolyhedralSurface();
     429             :     }
     430             : 
     431        1278 :     else if (STARTS_WITH_CI(szToken, "TIN"))
     432             :     {
     433         200 :         poGeom = new OGRTriangulatedSurface();
     434             :     }
     435             : 
     436             :     else
     437             :     {
     438        1078 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
     439             :     }
     440             : 
     441             :     /* -------------------------------------------------------------------- */
     442             :     /*      Do the import.                                                  */
     443             :     /* -------------------------------------------------------------------- */
     444      122226 :     const OGRErr eErr = poGeom->importFromWkt(&pszInput);
     445             : 
     446             :     /* -------------------------------------------------------------------- */
     447             :     /*      Assign spatial reference system.                                */
     448             :     /* -------------------------------------------------------------------- */
     449      122226 :     if (eErr == OGRERR_NONE)
     450             :     {
     451      126356 :         if (poGeom->hasCurveGeometry() &&
     452        4367 :             CPLTestBool(CPLGetConfigOption("OGR_STROKE_CURVE", "FALSE")))
     453             :         {
     454           9 :             OGRGeometry *poNewGeom = poGeom->getLinearGeometry();
     455           9 :             delete poGeom;
     456           9 :             poGeom = poNewGeom;
     457             :         }
     458      121989 :         poGeom->assignSpatialReference(poSR);
     459      121989 :         *ppoReturn = poGeom;
     460      121989 :         *ppszData = pszInput;
     461             :     }
     462             :     else
     463             :     {
     464         237 :         delete poGeom;
     465             :     }
     466             : 
     467      122226 :     return eErr;
     468             : }
     469             : 
     470             : /**
     471             :  * \brief Create a geometry object of the appropriate type from its
     472             :  * well known text representation.
     473             :  *
     474             :  * The C function OGR_G_CreateFromWkt() is the same as this method.
     475             :  *
     476             :  * @param pszData input zero terminated string containing well known text
     477             :  *                representation of the geometry to be created.
     478             :  * @param poSR pointer to the spatial reference to be assigned to the
     479             :  *             created geometry object.  This may be NULL.
     480             :  * @param ppoReturn the newly created geometry object will be assigned to the
     481             :  *                  indicated pointer on return.  This will be NULL if the
     482             :  *                  method fails. If not NULL, *ppoReturn should be freed with
     483             :  *                  OGRGeometryFactory::destroyGeometry() after use.
     484             : 
     485             :  * @return OGRERR_NONE if all goes well, otherwise any of
     486             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
     487             :  * OGRERR_CORRUPT_DATA may be returned.
     488             :  * @since GDAL 2.3
     489             :  */
     490             : 
     491        5652 : OGRErr OGRGeometryFactory::createFromWkt(const char *pszData,
     492             :                                          const OGRSpatialReference *poSR,
     493             :                                          OGRGeometry **ppoReturn)
     494             : 
     495             : {
     496        5652 :     return createFromWkt(&pszData, poSR, ppoReturn);
     497             : }
     498             : 
     499             : /************************************************************************/
     500             : /*                        OGR_G_CreateFromWkt()                         */
     501             : /************************************************************************/
     502             : /**
     503             :  * \brief Create a geometry object of the appropriate type from its well known
     504             :  * text representation.
     505             :  *
     506             :  * The OGRGeometryFactory::createFromWkt CPP method is the same as this
     507             :  * function.
     508             :  *
     509             :  * @param ppszData input zero terminated string containing well known text
     510             :  *                representation of the geometry to be created.  The pointer
     511             :  *                is updated to point just beyond that last character consumed.
     512             :  * @param hSRS handle to the spatial reference to be assigned to the
     513             :  *             created geometry object.  This may be NULL.
     514             :  * @param phGeometry the newly created geometry object will be assigned to the
     515             :  *                  indicated handle on return.  This will be NULL if the
     516             :  *                  method fails. If not NULL, *phGeometry should be freed with
     517             :  *                  OGR_G_DestroyGeometry() after use.
     518             :  *
     519             :  * @return OGRERR_NONE if all goes well, otherwise any of
     520             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
     521             :  * OGRERR_CORRUPT_DATA may be returned.
     522             :  */
     523             : 
     524      115929 : OGRErr CPL_DLL OGR_G_CreateFromWkt(char **ppszData, OGRSpatialReferenceH hSRS,
     525             :                                    OGRGeometryH *phGeometry)
     526             : 
     527             : {
     528      115929 :     return OGRGeometryFactory::createFromWkt(
     529             :         const_cast<const char **>(ppszData),
     530      115929 :         OGRSpatialReference::FromHandle(hSRS),
     531      115929 :         reinterpret_cast<OGRGeometry **>(phGeometry));
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                           createGeometry()                           */
     536             : /************************************************************************/
     537             : 
     538             : /**
     539             :  * \brief Create an empty geometry of desired type.
     540             :  *
     541             :  * This is equivalent to allocating the desired geometry with new, but
     542             :  * the allocation is guaranteed to take place in the context of the
     543             :  * GDAL/OGR heap.
     544             :  *
     545             :  * This method is the same as the C function OGR_G_CreateGeometry().
     546             :  *
     547             :  * @param eGeometryType the type code of the geometry class to be instantiated.
     548             :  *
     549             :  * @return the newly create geometry or NULL on failure. Should be freed with
     550             :  *          OGRGeometryFactory::destroyGeometry() after use.
     551             :  */
     552             : 
     553             : OGRGeometry *
     554      289759 : OGRGeometryFactory::createGeometry(OGRwkbGeometryType eGeometryType)
     555             : 
     556             : {
     557      289759 :     OGRGeometry *poGeom = nullptr;
     558      289759 :     switch (wkbFlatten(eGeometryType))
     559             :     {
     560      192274 :         case wkbPoint:
     561      384548 :             poGeom = new (std::nothrow) OGRPoint();
     562      192274 :             break;
     563             : 
     564       23395 :         case wkbLineString:
     565       46790 :             poGeom = new (std::nothrow) OGRLineString();
     566       23395 :             break;
     567             : 
     568       30066 :         case wkbPolygon:
     569       60133 :             poGeom = new (std::nothrow) OGRPolygon();
     570       30067 :             break;
     571             : 
     572        1953 :         case wkbGeometryCollection:
     573        3906 :             poGeom = new (std::nothrow) OGRGeometryCollection();
     574        1953 :             break;
     575             : 
     576        2720 :         case wkbMultiPolygon:
     577        5440 :             poGeom = new (std::nothrow) OGRMultiPolygon();
     578        2720 :             break;
     579             : 
     580        1356 :         case wkbMultiPoint:
     581        2712 :             poGeom = new (std::nothrow) OGRMultiPoint();
     582        1356 :             break;
     583             : 
     584        1655 :         case wkbMultiLineString:
     585        3310 :             poGeom = new (std::nothrow) OGRMultiLineString();
     586        1655 :             break;
     587             : 
     588        3706 :         case wkbLinearRing:
     589        7412 :             poGeom = new (std::nothrow) OGRLinearRing();
     590        3706 :             break;
     591             : 
     592          68 :         case wkbCircularString:
     593         136 :             poGeom = new (std::nothrow) OGRCircularString();
     594          68 :             break;
     595             : 
     596        1980 :         case wkbCompoundCurve:
     597        3960 :             poGeom = new (std::nothrow) OGRCompoundCurve();
     598        1980 :             break;
     599             : 
     600          44 :         case wkbCurvePolygon:
     601          88 :             poGeom = new (std::nothrow) OGRCurvePolygon();
     602          44 :             break;
     603             : 
     604        1120 :         case wkbMultiCurve:
     605        2240 :             poGeom = new (std::nothrow) OGRMultiCurve();
     606        1120 :             break;
     607             : 
     608        1182 :         case wkbMultiSurface:
     609        2364 :             poGeom = new (std::nothrow) OGRMultiSurface();
     610        1182 :             break;
     611             : 
     612       14638 :         case wkbTriangle:
     613       29276 :             poGeom = new (std::nothrow) OGRTriangle();
     614       14638 :             break;
     615             : 
     616        7378 :         case wkbPolyhedralSurface:
     617       14756 :             poGeom = new (std::nothrow) OGRPolyhedralSurface();
     618        7378 :             break;
     619             : 
     620        6223 :         case wkbTIN:
     621       12446 :             poGeom = new (std::nothrow) OGRTriangulatedSurface();
     622        6223 :             break;
     623             : 
     624           1 :         case wkbUnknown:
     625           1 :             break;
     626             : 
     627           0 :         default:
     628           0 :             CPLAssert(false);
     629             :             break;
     630             :     }
     631      289760 :     if (poGeom)
     632             :     {
     633      289759 :         if (OGR_GT_HasZ(eGeometryType))
     634       63967 :             poGeom->set3D(true);
     635      289758 :         if (OGR_GT_HasM(eGeometryType))
     636       59799 :             poGeom->setMeasured(true);
     637             :     }
     638      289760 :     return poGeom;
     639             : }
     640             : 
     641             : /************************************************************************/
     642             : /*                        OGR_G_CreateGeometry()                        */
     643             : /************************************************************************/
     644             : /**
     645             :  * \brief Create an empty geometry of desired type.
     646             :  *
     647             :  * This is equivalent to allocating the desired geometry with new, but
     648             :  * the allocation is guaranteed to take place in the context of the
     649             :  * GDAL/OGR heap.
     650             :  *
     651             :  * This function is the same as the CPP method
     652             :  * OGRGeometryFactory::createGeometry.
     653             :  *
     654             :  * @param eGeometryType the type code of the geometry to be created.
     655             :  *
     656             :  * @return handle to the newly create geometry or NULL on failure. Should be
     657             :  *         freed with OGR_G_DestroyGeometry() after use.
     658             :  */
     659             : 
     660      185971 : OGRGeometryH OGR_G_CreateGeometry(OGRwkbGeometryType eGeometryType)
     661             : 
     662             : {
     663      185971 :     return OGRGeometry::ToHandle(
     664      185971 :         OGRGeometryFactory::createGeometry(eGeometryType));
     665             : }
     666             : 
     667             : /************************************************************************/
     668             : /*                          destroyGeometry()                           */
     669             : /************************************************************************/
     670             : 
     671             : /**
     672             :  * \brief Destroy geometry object.
     673             :  *
     674             :  * Equivalent to invoking delete on a geometry, but it guaranteed to take
     675             :  * place within the context of the GDAL/OGR heap.
     676             :  *
     677             :  * This method is the same as the C function OGR_G_DestroyGeometry().
     678             :  *
     679             :  * @param poGeom the geometry to deallocate.
     680             :  */
     681             : 
     682          18 : void OGRGeometryFactory::destroyGeometry(OGRGeometry *poGeom)
     683             : 
     684             : {
     685          18 :     delete poGeom;
     686          18 : }
     687             : 
     688             : /************************************************************************/
     689             : /*                        OGR_G_DestroyGeometry()                       */
     690             : /************************************************************************/
     691             : /**
     692             :  * \brief Destroy geometry object.
     693             :  *
     694             :  * Equivalent to invoking delete on a geometry, but it guaranteed to take
     695             :  * place within the context of the GDAL/OGR heap.
     696             :  *
     697             :  * This function is the same as the CPP method
     698             :  * OGRGeometryFactory::destroyGeometry.
     699             :  *
     700             :  * @param hGeom handle to the geometry to delete.
     701             :  */
     702             : 
     703      290562 : void OGR_G_DestroyGeometry(OGRGeometryH hGeom)
     704             : 
     705             : {
     706      290562 :     delete OGRGeometry::FromHandle(hGeom);
     707      290562 : }
     708             : 
     709             : /************************************************************************/
     710             : /*                           forceToPolygon()                           */
     711             : /************************************************************************/
     712             : 
     713             : /**
     714             :  * \brief Convert to polygon.
     715             :  *
     716             :  * Tries to force the provided geometry to be a polygon. This effects a change
     717             :  * on multipolygons.
     718             :  * Starting with GDAL 2.0, curve polygons or closed curves will be changed to
     719             :  * polygons.  The passed in geometry is consumed and a new one returned (or
     720             :  * potentially the same one).
     721             :  *
     722             :  * Note: the resulting polygon may break the Simple Features rules for polygons,
     723             :  * for example when converting from a multi-part multipolygon.
     724             :  *
     725             :  * @param poGeom the input geometry - ownership is passed to the method.
     726             :  * @return new geometry.
     727             :  */
     728             : 
     729         148 : OGRGeometry *OGRGeometryFactory::forceToPolygon(OGRGeometry *poGeom)
     730             : 
     731             : {
     732         148 :     if (poGeom == nullptr)
     733           0 :         return nullptr;
     734             : 
     735         148 :     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
     736             : 
     737         148 :     if (eGeomType == wkbCurvePolygon)
     738             :     {
     739          34 :         OGRCurvePolygon *poCurve = poGeom->toCurvePolygon();
     740             : 
     741          34 :         if (!poGeom->hasCurveGeometry(TRUE))
     742          14 :             return OGRSurface::CastToPolygon(poCurve);
     743             : 
     744          20 :         OGRPolygon *poPoly = poCurve->CurvePolyToPoly();
     745          20 :         delete poGeom;
     746          20 :         return poPoly;
     747             :     }
     748             : 
     749             :     // base polygon or triangle
     750         114 :     if (OGR_GT_IsSubClassOf(eGeomType, wkbPolygon))
     751             :     {
     752           7 :         return OGRSurface::CastToPolygon(poGeom->toSurface());
     753             :     }
     754             : 
     755         107 :     if (OGR_GT_IsCurve(eGeomType))
     756             :     {
     757          60 :         OGRCurve *poCurve = poGeom->toCurve();
     758          60 :         if (poCurve->getNumPoints() >= 3 && poCurve->get_IsClosed())
     759             :         {
     760          40 :             OGRPolygon *poPolygon = new OGRPolygon();
     761          40 :             poPolygon->assignSpatialReference(poGeom->getSpatialReference());
     762             : 
     763          40 :             if (!poGeom->hasCurveGeometry(TRUE))
     764             :             {
     765          26 :                 poPolygon->addRingDirectly(OGRCurve::CastToLinearRing(poCurve));
     766             :             }
     767             :             else
     768             :             {
     769          14 :                 OGRLineString *poLS = poCurve->CurveToLine();
     770          14 :                 poPolygon->addRingDirectly(OGRCurve::CastToLinearRing(poLS));
     771          14 :                 delete poGeom;
     772             :             }
     773          40 :             return poPolygon;
     774             :         }
     775             :     }
     776             : 
     777          67 :     if (OGR_GT_IsSubClassOf(eGeomType, wkbPolyhedralSurface))
     778             :     {
     779           6 :         OGRPolyhedralSurface *poPS = poGeom->toPolyhedralSurface();
     780           6 :         if (poPS->getNumGeometries() == 1)
     781             :         {
     782           5 :             poGeom = OGRSurface::CastToPolygon(
     783           5 :                 poPS->getGeometryRef(0)->clone()->toSurface());
     784           5 :             delete poPS;
     785           5 :             return poGeom;
     786             :         }
     787             :     }
     788             : 
     789          62 :     if (eGeomType != wkbGeometryCollection && eGeomType != wkbMultiPolygon &&
     790             :         eGeomType != wkbMultiSurface)
     791          38 :         return poGeom;
     792             : 
     793             :     // Build an aggregated polygon from all the polygon rings in the container.
     794          24 :     OGRPolygon *poPolygon = new OGRPolygon();
     795          24 :     OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
     796          24 :     if (poGeom->hasCurveGeometry())
     797             :     {
     798             :         OGRGeometryCollection *poNewGC =
     799           5 :             poGC->getLinearGeometry()->toGeometryCollection();
     800           5 :         delete poGC;
     801           5 :         poGeom = poNewGC;
     802           5 :         poGC = poNewGC;
     803             :     }
     804             : 
     805          24 :     poPolygon->assignSpatialReference(poGeom->getSpatialReference());
     806             : 
     807          53 :     for (int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++)
     808             :     {
     809          29 :         if (wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType()) !=
     810             :             wkbPolygon)
     811          12 :             continue;
     812             : 
     813          17 :         OGRPolygon *poOldPoly = poGC->getGeometryRef(iGeom)->toPolygon();
     814             : 
     815          17 :         if (poOldPoly->getExteriorRing() == nullptr)
     816           3 :             continue;
     817             : 
     818          14 :         poPolygon->addRingDirectly(poOldPoly->stealExteriorRing());
     819             : 
     820          22 :         for (int iRing = 0; iRing < poOldPoly->getNumInteriorRings(); iRing++)
     821           8 :             poPolygon->addRingDirectly(poOldPoly->stealInteriorRing(iRing));
     822             :     }
     823             : 
     824          24 :     delete poGC;
     825             : 
     826          24 :     return poPolygon;
     827             : }
     828             : 
     829             : /************************************************************************/
     830             : /*                        OGR_G_ForceToPolygon()                        */
     831             : /************************************************************************/
     832             : 
     833             : /**
     834             :  * \brief Convert to polygon.
     835             :  *
     836             :  * This function is the same as the C++ method
     837             :  * OGRGeometryFactory::forceToPolygon().
     838             :  *
     839             :  * @param hGeom handle to the geometry to convert (ownership surrendered).
     840             :  * @return the converted geometry (ownership to caller).
     841             :  *
     842             :  * @since GDAL/OGR 1.8.0
     843             :  */
     844             : 
     845          46 : OGRGeometryH OGR_G_ForceToPolygon(OGRGeometryH hGeom)
     846             : 
     847             : {
     848          46 :     return OGRGeometry::ToHandle(
     849          46 :         OGRGeometryFactory::forceToPolygon(OGRGeometry::FromHandle(hGeom)));
     850             : }
     851             : 
     852             : /************************************************************************/
     853             : /*                        forceToMultiPolygon()                         */
     854             : /************************************************************************/
     855             : 
     856             : /**
     857             :  * \brief Convert to multipolygon.
     858             :  *
     859             :  * Tries to force the provided geometry to be a multipolygon.  Currently
     860             :  * this just effects a change on polygons.  The passed in geometry is
     861             :  * consumed and a new one returned (or potentially the same one).
     862             :  *
     863             :  * @return new geometry.
     864             :  */
     865             : 
     866        2941 : OGRGeometry *OGRGeometryFactory::forceToMultiPolygon(OGRGeometry *poGeom)
     867             : 
     868             : {
     869        2941 :     if (poGeom == nullptr)
     870           0 :         return nullptr;
     871             : 
     872        2941 :     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
     873             : 
     874             :     /* -------------------------------------------------------------------- */
     875             :     /*      If this is already a MultiPolygon, nothing to do                */
     876             :     /* -------------------------------------------------------------------- */
     877        2941 :     if (eGeomType == wkbMultiPolygon)
     878             :     {
     879          60 :         return poGeom;
     880             :     }
     881             : 
     882             :     /* -------------------------------------------------------------------- */
     883             :     /*      If this is already a MultiSurface with compatible content,      */
     884             :     /*      just cast                                                       */
     885             :     /* -------------------------------------------------------------------- */
     886        2881 :     if (eGeomType == wkbMultiSurface)
     887             :     {
     888           9 :         OGRMultiSurface *poMS = poGeom->toMultiSurface();
     889           9 :         if (!poMS->hasCurveGeometry(TRUE))
     890             :         {
     891           4 :             return OGRMultiSurface::CastToMultiPolygon(poMS);
     892             :         }
     893             :     }
     894             : 
     895             :     /* -------------------------------------------------------------------- */
     896             :     /*      Check for the case of a geometrycollection that can be          */
     897             :     /*      promoted to MultiPolygon.                                       */
     898             :     /* -------------------------------------------------------------------- */
     899        2877 :     if (eGeomType == wkbGeometryCollection || eGeomType == wkbMultiSurface)
     900             :     {
     901          91 :         bool bAllPoly = true;
     902          91 :         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
     903          91 :         if (poGeom->hasCurveGeometry())
     904             :         {
     905             :             OGRGeometryCollection *poNewGC =
     906           6 :                 poGC->getLinearGeometry()->toGeometryCollection();
     907           6 :             delete poGC;
     908           6 :             poGeom = poNewGC;
     909           6 :             poGC = poNewGC;
     910             :         }
     911             : 
     912          91 :         bool bCanConvertToMultiPoly = true;
     913         372 :         for (int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++)
     914             :         {
     915             :             OGRwkbGeometryType eSubGeomType =
     916         281 :                 wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType());
     917         281 :             if (eSubGeomType != wkbPolygon)
     918         252 :                 bAllPoly = false;
     919         281 :             if (eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon &&
     920         194 :                 eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN)
     921             :             {
     922          16 :                 bCanConvertToMultiPoly = false;
     923             :             }
     924             :         }
     925             : 
     926          91 :         if (!bCanConvertToMultiPoly)
     927          12 :             return poGeom;
     928             : 
     929          79 :         OGRMultiPolygon *poMP = new OGRMultiPolygon();
     930          79 :         poMP->assignSpatialReference(poGeom->getSpatialReference());
     931             : 
     932         342 :         while (poGC->getNumGeometries() > 0)
     933             :         {
     934         263 :             OGRGeometry *poSubGeom = poGC->getGeometryRef(0);
     935         263 :             poGC->removeGeometry(0, FALSE);
     936         263 :             if (bAllPoly)
     937             :             {
     938          27 :                 poMP->addGeometryDirectly(poSubGeom);
     939             :             }
     940             :             else
     941             :             {
     942         236 :                 poSubGeom = forceToMultiPolygon(poSubGeom);
     943         236 :                 OGRMultiPolygon *poSubMP = poSubGeom->toMultiPolygon();
     944         614 :                 while (poSubMP != nullptr && poSubMP->getNumGeometries() > 0)
     945             :                 {
     946         378 :                     poMP->addGeometryDirectly(poSubMP->getGeometryRef(0));
     947         378 :                     poSubMP->removeGeometry(0, FALSE);
     948             :                 }
     949         236 :                 delete poSubMP;
     950             :             }
     951             :         }
     952             : 
     953          79 :         delete poGC;
     954             : 
     955          79 :         return poMP;
     956             :     }
     957             : 
     958        2786 :     if (eGeomType == wkbCurvePolygon)
     959             :     {
     960           5 :         OGRPolygon *poPoly = poGeom->toCurvePolygon()->CurvePolyToPoly();
     961           5 :         OGRMultiPolygon *poMP = new OGRMultiPolygon();
     962           5 :         poMP->assignSpatialReference(poGeom->getSpatialReference());
     963           5 :         poMP->addGeometryDirectly(poPoly);
     964           5 :         delete poGeom;
     965           5 :         return poMP;
     966             :     }
     967             : 
     968             :     /* -------------------------------------------------------------------- */
     969             :     /*      If it is PolyhedralSurface or TIN, then pretend it is a         */
     970             :     /*      multipolygon.                                                   */
     971             :     /* -------------------------------------------------------------------- */
     972        2781 :     if (OGR_GT_IsSubClassOf(eGeomType, wkbPolyhedralSurface))
     973             :     {
     974         274 :         return OGRPolyhedralSurface::CastToMultiPolygon(
     975         274 :             poGeom->toPolyhedralSurface());
     976             :     }
     977             : 
     978        2507 :     if (eGeomType == wkbTriangle)
     979             :     {
     980           2 :         return forceToMultiPolygon(forceToPolygon(poGeom));
     981             :     }
     982             : 
     983             :     /* -------------------------------------------------------------------- */
     984             :     /*      Eventually we should try to split the polygon into component    */
     985             :     /*      island polygons.  But that is a lot of work and can be put off. */
     986             :     /* -------------------------------------------------------------------- */
     987        2505 :     if (eGeomType != wkbPolygon)
     988          30 :         return poGeom;
     989             : 
     990        2475 :     OGRMultiPolygon *poMP = new OGRMultiPolygon();
     991        2475 :     poMP->assignSpatialReference(poGeom->getSpatialReference());
     992        2475 :     poMP->addGeometryDirectly(poGeom);
     993             : 
     994        2475 :     return poMP;
     995             : }
     996             : 
     997             : /************************************************************************/
     998             : /*                     OGR_G_ForceToMultiPolygon()                      */
     999             : /************************************************************************/
    1000             : 
    1001             : /**
    1002             :  * \brief Convert to multipolygon.
    1003             :  *
    1004             :  * This function is the same as the C++ method
    1005             :  * OGRGeometryFactory::forceToMultiPolygon().
    1006             :  *
    1007             :  * @param hGeom handle to the geometry to convert (ownership surrendered).
    1008             :  * @return the converted geometry (ownership to caller).
    1009             :  *
    1010             :  * @since GDAL/OGR 1.8.0
    1011             :  */
    1012             : 
    1013          47 : OGRGeometryH OGR_G_ForceToMultiPolygon(OGRGeometryH hGeom)
    1014             : 
    1015             : {
    1016          47 :     return OGRGeometry::ToHandle(OGRGeometryFactory::forceToMultiPolygon(
    1017          47 :         OGRGeometry::FromHandle(hGeom)));
    1018             : }
    1019             : 
    1020             : /************************************************************************/
    1021             : /*                        forceToMultiPoint()                           */
    1022             : /************************************************************************/
    1023             : 
    1024             : /**
    1025             :  * \brief Convert to multipoint.
    1026             :  *
    1027             :  * Tries to force the provided geometry to be a multipoint.  Currently
    1028             :  * this just effects a change on points or collection of points.
    1029             :  * The passed in geometry is
    1030             :  * consumed and a new one returned (or potentially the same one).
    1031             :  *
    1032             :  * @return new geometry.
    1033             :  */
    1034             : 
    1035          63 : OGRGeometry *OGRGeometryFactory::forceToMultiPoint(OGRGeometry *poGeom)
    1036             : 
    1037             : {
    1038          63 :     if (poGeom == nullptr)
    1039           0 :         return nullptr;
    1040             : 
    1041          63 :     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
    1042             : 
    1043             :     /* -------------------------------------------------------------------- */
    1044             :     /*      If this is already a MultiPoint, nothing to do                  */
    1045             :     /* -------------------------------------------------------------------- */
    1046          63 :     if (eGeomType == wkbMultiPoint)
    1047             :     {
    1048           2 :         return poGeom;
    1049             :     }
    1050             : 
    1051             :     /* -------------------------------------------------------------------- */
    1052             :     /*      Check for the case of a geometrycollection that can be          */
    1053             :     /*      promoted to MultiPoint.                                         */
    1054             :     /* -------------------------------------------------------------------- */
    1055          61 :     if (eGeomType == wkbGeometryCollection)
    1056             :     {
    1057          14 :         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    1058          18 :         for (const auto &poMember : poGC)
    1059             :         {
    1060          14 :             if (wkbFlatten(poMember->getGeometryType()) != wkbPoint)
    1061          10 :                 return poGeom;
    1062             :         }
    1063             : 
    1064           4 :         OGRMultiPoint *poMP = new OGRMultiPoint();
    1065           4 :         poMP->assignSpatialReference(poGeom->getSpatialReference());
    1066             : 
    1067           8 :         while (poGC->getNumGeometries() > 0)
    1068             :         {
    1069           4 :             poMP->addGeometryDirectly(poGC->getGeometryRef(0));
    1070           4 :             poGC->removeGeometry(0, FALSE);
    1071             :         }
    1072             : 
    1073           4 :         delete poGC;
    1074             : 
    1075           4 :         return poMP;
    1076             :     }
    1077             : 
    1078          47 :     if (eGeomType != wkbPoint)
    1079          44 :         return poGeom;
    1080             : 
    1081           3 :     OGRMultiPoint *poMP = new OGRMultiPoint();
    1082           3 :     poMP->assignSpatialReference(poGeom->getSpatialReference());
    1083           3 :     poMP->addGeometryDirectly(poGeom);
    1084             : 
    1085           3 :     return poMP;
    1086             : }
    1087             : 
    1088             : /************************************************************************/
    1089             : /*                      OGR_G_ForceToMultiPoint()                       */
    1090             : /************************************************************************/
    1091             : 
    1092             : /**
    1093             :  * \brief Convert to multipoint.
    1094             :  *
    1095             :  * This function is the same as the C++ method
    1096             :  * OGRGeometryFactory::forceToMultiPoint().
    1097             :  *
    1098             :  * @param hGeom handle to the geometry to convert (ownership surrendered).
    1099             :  * @return the converted geometry (ownership to caller).
    1100             :  *
    1101             :  * @since GDAL/OGR 1.8.0
    1102             :  */
    1103             : 
    1104          41 : OGRGeometryH OGR_G_ForceToMultiPoint(OGRGeometryH hGeom)
    1105             : 
    1106             : {
    1107          41 :     return OGRGeometry::ToHandle(
    1108          41 :         OGRGeometryFactory::forceToMultiPoint(OGRGeometry::FromHandle(hGeom)));
    1109             : }
    1110             : 
    1111             : /************************************************************************/
    1112             : /*                        forceToMultiLinestring()                      */
    1113             : /************************************************************************/
    1114             : 
    1115             : /**
    1116             :  * \brief Convert to multilinestring.
    1117             :  *
    1118             :  * Tries to force the provided geometry to be a multilinestring.
    1119             :  *
    1120             :  * - linestrings are placed in a multilinestring.
    1121             :  * - circularstrings and compoundcurves will be approximated and placed in a
    1122             :  * multilinestring.
    1123             :  * - geometry collections will be converted to multilinestring if they only
    1124             :  * contain linestrings.
    1125             :  * - polygons will be changed to a collection of linestrings (one per ring).
    1126             :  * - curvepolygons will be approximated and changed to a collection of
    1127             :  ( linestrings (one per ring).
    1128             :  *
    1129             :  * The passed in geometry is
    1130             :  * consumed and a new one returned (or potentially the same one).
    1131             :  *
    1132             :  * @return new geometry.
    1133             :  */
    1134             : 
    1135        2130 : OGRGeometry *OGRGeometryFactory::forceToMultiLineString(OGRGeometry *poGeom)
    1136             : 
    1137             : {
    1138        2130 :     if (poGeom == nullptr)
    1139           0 :         return nullptr;
    1140             : 
    1141        2130 :     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
    1142             : 
    1143             :     /* -------------------------------------------------------------------- */
    1144             :     /*      If this is already a MultiLineString, nothing to do             */
    1145             :     /* -------------------------------------------------------------------- */
    1146        2130 :     if (eGeomType == wkbMultiLineString)
    1147             :     {
    1148           2 :         return poGeom;
    1149             :     }
    1150             : 
    1151             :     /* -------------------------------------------------------------------- */
    1152             :     /*      Check for the case of a geometrycollection that can be          */
    1153             :     /*      promoted to MultiLineString.                                    */
    1154             :     /* -------------------------------------------------------------------- */
    1155        2128 :     if (eGeomType == wkbGeometryCollection)
    1156             :     {
    1157          16 :         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    1158          16 :         if (poGeom->hasCurveGeometry())
    1159             :         {
    1160             :             OGRGeometryCollection *poNewGC =
    1161           1 :                 poGC->getLinearGeometry()->toGeometryCollection();
    1162           1 :             delete poGC;
    1163           1 :             poGeom = poNewGC;
    1164           1 :             poGC = poNewGC;
    1165             :         }
    1166             : 
    1167          24 :         for (auto &&poMember : poGC)
    1168             :         {
    1169          18 :             if (wkbFlatten(poMember->getGeometryType()) != wkbLineString)
    1170             :             {
    1171          10 :                 return poGeom;
    1172             :             }
    1173             :         }
    1174             : 
    1175           6 :         OGRMultiLineString *poMP = new OGRMultiLineString();
    1176           6 :         poMP->assignSpatialReference(poGeom->getSpatialReference());
    1177             : 
    1178          14 :         while (poGC->getNumGeometries() > 0)
    1179             :         {
    1180           8 :             poMP->addGeometryDirectly(poGC->getGeometryRef(0));
    1181           8 :             poGC->removeGeometry(0, FALSE);
    1182             :         }
    1183             : 
    1184           6 :         delete poGC;
    1185             : 
    1186           6 :         return poMP;
    1187             :     }
    1188             : 
    1189             :     /* -------------------------------------------------------------------- */
    1190             :     /*      Turn a linestring into a multilinestring.                       */
    1191             :     /* -------------------------------------------------------------------- */
    1192        2112 :     if (eGeomType == wkbLineString)
    1193             :     {
    1194        2029 :         OGRMultiLineString *poMP = new OGRMultiLineString();
    1195        2029 :         poMP->assignSpatialReference(poGeom->getSpatialReference());
    1196        2029 :         poMP->addGeometryDirectly(poGeom);
    1197        2029 :         return poMP;
    1198             :     }
    1199             : 
    1200             :     /* -------------------------------------------------------------------- */
    1201             :     /*      Convert polygons into a multilinestring.                        */
    1202             :     /* -------------------------------------------------------------------- */
    1203          83 :     if (OGR_GT_IsSubClassOf(eGeomType, wkbCurvePolygon))
    1204             :     {
    1205          25 :         OGRMultiLineString *poMLS = new OGRMultiLineString();
    1206          25 :         poMLS->assignSpatialReference(poGeom->getSpatialReference());
    1207             : 
    1208          51 :         const auto AddRingFromSrcPoly = [poMLS](const OGRPolygon *poPoly)
    1209             :         {
    1210          55 :             for (int iRing = 0; iRing < poPoly->getNumInteriorRings() + 1;
    1211             :                  iRing++)
    1212             :             {
    1213             :                 const OGRLineString *poLR;
    1214             : 
    1215          32 :                 if (iRing == 0)
    1216             :                 {
    1217          25 :                     poLR = poPoly->getExteriorRing();
    1218          25 :                     if (poLR == nullptr)
    1219           2 :                         break;
    1220             :                 }
    1221             :                 else
    1222           7 :                     poLR = poPoly->getInteriorRing(iRing - 1);
    1223             : 
    1224          30 :                 if (poLR == nullptr || poLR->getNumPoints() == 0)
    1225           4 :                     continue;
    1226             : 
    1227          26 :                 auto poNewLS = new OGRLineString();
    1228          26 :                 poNewLS->addSubLineString(poLR);
    1229          26 :                 poMLS->addGeometryDirectly(poNewLS);
    1230             :             }
    1231          25 :         };
    1232             : 
    1233          25 :         if (OGR_GT_IsSubClassOf(eGeomType, wkbPolygon))
    1234             :         {
    1235          21 :             AddRingFromSrcPoly(poGeom->toPolygon());
    1236             :         }
    1237             :         else
    1238             :         {
    1239             :             auto poTmpPoly = std::unique_ptr<OGRPolygon>(
    1240           8 :                 poGeom->toCurvePolygon()->CurvePolyToPoly());
    1241           4 :             AddRingFromSrcPoly(poTmpPoly.get());
    1242             :         }
    1243             : 
    1244          25 :         delete poGeom;
    1245             : 
    1246          25 :         return poMLS;
    1247             :     }
    1248             : 
    1249             :     /* -------------------------------------------------------------------- */
    1250             :     /*      If it is PolyhedralSurface or TIN, then pretend it is a         */
    1251             :     /*      multipolygon.                                                   */
    1252             :     /* -------------------------------------------------------------------- */
    1253          58 :     if (OGR_GT_IsSubClassOf(eGeomType, wkbPolyhedralSurface))
    1254             :     {
    1255           0 :         poGeom = CPLAssertNotNull(forceToMultiPolygon(poGeom));
    1256           0 :         eGeomType = wkbMultiPolygon;
    1257             :     }
    1258             : 
    1259             :     /* -------------------------------------------------------------------- */
    1260             :     /*      Convert multi-polygons into a multilinestring.                  */
    1261             :     /* -------------------------------------------------------------------- */
    1262          58 :     if (eGeomType == wkbMultiPolygon || eGeomType == wkbMultiSurface)
    1263             :     {
    1264           9 :         OGRMultiLineString *poMLS = new OGRMultiLineString();
    1265           9 :         poMLS->assignSpatialReference(poGeom->getSpatialReference());
    1266             : 
    1267          22 :         const auto AddRingFromSrcMP = [poMLS](const OGRMultiPolygon *poSrcMP)
    1268             :         {
    1269          21 :             for (auto &&poPoly : poSrcMP)
    1270             :             {
    1271          27 :                 for (auto &&poLR : poPoly)
    1272             :                 {
    1273          15 :                     if (poLR->IsEmpty())
    1274           2 :                         continue;
    1275             : 
    1276          13 :                     OGRLineString *poNewLS = new OGRLineString();
    1277          13 :                     poNewLS->addSubLineString(poLR);
    1278          13 :                     poMLS->addGeometryDirectly(poNewLS);
    1279             :                 }
    1280             :             }
    1281           9 :         };
    1282             : 
    1283           9 :         if (eGeomType == wkbMultiPolygon)
    1284             :         {
    1285           6 :             AddRingFromSrcMP(poGeom->toMultiPolygon());
    1286             :         }
    1287             :         else
    1288             :         {
    1289             :             auto poTmpMPoly = std::unique_ptr<OGRMultiPolygon>(
    1290           6 :                 poGeom->getLinearGeometry()->toMultiPolygon());
    1291           3 :             AddRingFromSrcMP(poTmpMPoly.get());
    1292             :         }
    1293             : 
    1294           9 :         delete poGeom;
    1295           9 :         return poMLS;
    1296             :     }
    1297             : 
    1298             :     /* -------------------------------------------------------------------- */
    1299             :     /*      If it is a curve line, approximate it and wrap in a multilinestring
    1300             :      */
    1301             :     /* -------------------------------------------------------------------- */
    1302          49 :     if (eGeomType == wkbCircularString || eGeomType == wkbCompoundCurve)
    1303             :     {
    1304          20 :         OGRMultiLineString *poMP = new OGRMultiLineString();
    1305          20 :         poMP->assignSpatialReference(poGeom->getSpatialReference());
    1306          20 :         poMP->addGeometryDirectly(poGeom->toCurve()->CurveToLine());
    1307          20 :         delete poGeom;
    1308          20 :         return poMP;
    1309             :     }
    1310             : 
    1311             :     /* -------------------------------------------------------------------- */
    1312             :     /*      If this is already a MultiCurve with compatible content,        */
    1313             :     /*      just cast                                                       */
    1314             :     /* -------------------------------------------------------------------- */
    1315          38 :     if (eGeomType == wkbMultiCurve &&
    1316           9 :         !poGeom->toMultiCurve()->hasCurveGeometry(TRUE))
    1317             :     {
    1318           3 :         return OGRMultiCurve::CastToMultiLineString(poGeom->toMultiCurve());
    1319             :     }
    1320             : 
    1321             :     /* -------------------------------------------------------------------- */
    1322             :     /*      If it is a multicurve, call getLinearGeometry()                */
    1323             :     /* -------------------------------------------------------------------- */
    1324          26 :     if (eGeomType == wkbMultiCurve)
    1325             :     {
    1326           6 :         OGRGeometry *poNewGeom = poGeom->getLinearGeometry();
    1327           6 :         CPLAssert(wkbFlatten(poNewGeom->getGeometryType()) ==
    1328             :                   wkbMultiLineString);
    1329           6 :         delete poGeom;
    1330           6 :         return poNewGeom->toMultiLineString();
    1331             :     }
    1332             : 
    1333          20 :     return poGeom;
    1334             : }
    1335             : 
    1336             : /************************************************************************/
    1337             : /*                    OGR_G_ForceToMultiLineString()                    */
    1338             : /************************************************************************/
    1339             : 
    1340             : /**
    1341             :  * \brief Convert to multilinestring.
    1342             :  *
    1343             :  * This function is the same as the C++ method
    1344             :  * OGRGeometryFactory::forceToMultiLineString().
    1345             :  *
    1346             :  * @param hGeom handle to the geometry to convert (ownership surrendered).
    1347             :  * @return the converted geometry (ownership to caller).
    1348             :  *
    1349             :  * @since GDAL/OGR 1.8.0
    1350             :  */
    1351             : 
    1352          50 : OGRGeometryH OGR_G_ForceToMultiLineString(OGRGeometryH hGeom)
    1353             : 
    1354             : {
    1355          50 :     return OGRGeometry::ToHandle(OGRGeometryFactory::forceToMultiLineString(
    1356          50 :         OGRGeometry::FromHandle(hGeom)));
    1357             : }
    1358             : 
    1359             : /************************************************************************/
    1360             : /*                      removeLowerDimensionSubGeoms()                  */
    1361             : /************************************************************************/
    1362             : 
    1363             : /** \brief Remove sub-geometries from a geometry collection that do not have
    1364             :  *         the maximum topological dimensionality of the collection.
    1365             :  *
    1366             :  * This is typically to be used as a cleanup phase after running
    1367             :  * OGRGeometry::MakeValid()
    1368             :  *
    1369             :  * For example, MakeValid() on a polygon can return a geometry collection of
    1370             :  * polygons and linestrings. Calling this method will return either a polygon
    1371             :  * or multipolygon by dropping those linestrings.
    1372             :  *
    1373             :  * On a non-geometry collection, this will return a clone of the passed
    1374             :  * geometry.
    1375             :  *
    1376             :  * @param poGeom input geometry
    1377             :  * @return a new geometry.
    1378             :  *
    1379             :  * @since GDAL 3.1.0
    1380             :  */
    1381             : OGRGeometry *
    1382          20 : OGRGeometryFactory::removeLowerDimensionSubGeoms(const OGRGeometry *poGeom)
    1383             : {
    1384          20 :     if (poGeom == nullptr)
    1385           0 :         return nullptr;
    1386          34 :     if (wkbFlatten(poGeom->getGeometryType()) != wkbGeometryCollection ||
    1387          14 :         poGeom->IsEmpty())
    1388             :     {
    1389           7 :         return poGeom->clone();
    1390             :     }
    1391          13 :     const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    1392          13 :     int nMaxDim = 0;
    1393          13 :     OGRBoolean bHasCurve = FALSE;
    1394          36 :     for (const auto poSubGeom : *poGC)
    1395             :     {
    1396          23 :         nMaxDim = std::max(nMaxDim, poSubGeom->getDimension());
    1397          23 :         bHasCurve |= poSubGeom->hasCurveGeometry();
    1398             :     }
    1399          13 :     int nCountAtMaxDim = 0;
    1400          13 :     const OGRGeometry *poGeomAtMaxDim = nullptr;
    1401          36 :     for (const auto poSubGeom : *poGC)
    1402             :     {
    1403          23 :         if (poSubGeom->getDimension() == nMaxDim)
    1404             :         {
    1405          18 :             poGeomAtMaxDim = poSubGeom;
    1406          18 :             nCountAtMaxDim++;
    1407             :         }
    1408             :     }
    1409          13 :     if (nCountAtMaxDim == 1 && poGeomAtMaxDim != nullptr)
    1410             :     {
    1411           8 :         return poGeomAtMaxDim->clone();
    1412             :     }
    1413             :     OGRGeometryCollection *poRet =
    1414           5 :         (nMaxDim == 0)
    1415          10 :             ? static_cast<OGRGeometryCollection *>(new OGRMultiPoint())
    1416           5 :         : (nMaxDim == 1)
    1417          10 :             ? (!bHasCurve
    1418           4 :                    ? static_cast<OGRGeometryCollection *>(
    1419           1 :                          new OGRMultiLineString())
    1420           1 :                    : static_cast<OGRGeometryCollection *>(new OGRMultiCurve()))
    1421           3 :         : (nMaxDim == 2 && !bHasCurve)
    1422           6 :             ? static_cast<OGRGeometryCollection *>(new OGRMultiPolygon())
    1423           1 :             : static_cast<OGRGeometryCollection *>(new OGRMultiSurface());
    1424          15 :     for (const auto poSubGeom : *poGC)
    1425             :     {
    1426          10 :         if (poSubGeom->getDimension() == nMaxDim)
    1427             :         {
    1428          10 :             if (OGR_GT_IsSubClassOf(poSubGeom->getGeometryType(),
    1429          10 :                                     wkbGeometryCollection))
    1430             :             {
    1431             :                 const OGRGeometryCollection *poSubGeomAsGC =
    1432           1 :                     poSubGeom->toGeometryCollection();
    1433           2 :                 for (const auto poSubSubGeom : *poSubGeomAsGC)
    1434             :                 {
    1435           1 :                     if (poSubSubGeom->getDimension() == nMaxDim)
    1436             :                     {
    1437           1 :                         poRet->addGeometryDirectly(poSubSubGeom->clone());
    1438             :                     }
    1439             :                 }
    1440             :             }
    1441             :             else
    1442             :             {
    1443           9 :                 poRet->addGeometryDirectly(poSubGeom->clone());
    1444             :             }
    1445             :         }
    1446             :     }
    1447           5 :     return poRet;
    1448             : }
    1449             : 
    1450             : /************************************************************************/
    1451             : /*                  OGR_G_RemoveLowerDimensionSubGeoms()                */
    1452             : /************************************************************************/
    1453             : 
    1454             : /** \brief Remove sub-geometries from a geometry collection that do not have
    1455             :  *         the maximum topological dimensionality of the collection.
    1456             :  *
    1457             :  * This function is the same as the C++ method
    1458             :  * OGRGeometryFactory::removeLowerDimensionSubGeoms().
    1459             :  *
    1460             :  * @param hGeom handle to the geometry to convert
    1461             :  * @return a new geometry.
    1462             :  *
    1463             :  * @since GDAL 3.1.0
    1464             :  */
    1465             : 
    1466          18 : OGRGeometryH OGR_G_RemoveLowerDimensionSubGeoms(const OGRGeometryH hGeom)
    1467             : 
    1468             : {
    1469          18 :     return OGRGeometry::ToHandle(
    1470             :         OGRGeometryFactory::removeLowerDimensionSubGeoms(
    1471          36 :             OGRGeometry::FromHandle(hGeom)));
    1472             : }
    1473             : 
    1474             : /************************************************************************/
    1475             : /*                          organizePolygons()                          */
    1476             : /************************************************************************/
    1477             : 
    1478        4025 : struct sPolyExtended
    1479             : {
    1480             :     CPL_DISALLOW_COPY_ASSIGN(sPolyExtended)
    1481        2264 :     sPolyExtended() = default;
    1482        4971 :     sPolyExtended(sPolyExtended &&) = default;
    1483             :     sPolyExtended &operator=(sPolyExtended &&) = default;
    1484             : 
    1485             :     OGRGeometry *poGeometry = nullptr;
    1486             :     OGRCurvePolygon *poPolygon = nullptr;
    1487             :     OGREnvelope sEnvelope{};
    1488             :     OGRCurve *poExteriorRing = nullptr;
    1489             :     OGRPoint poAPoint{};
    1490             :     int nInitialIndex = 0;
    1491             :     OGRCurvePolygon *poEnclosingPolygon = nullptr;
    1492             :     double dfArea = 0.0;
    1493             :     bool bIsTopLevel = false;
    1494             :     bool bIsCW = false;
    1495             :     bool bIsPolygon = false;
    1496             : };
    1497             : 
    1498        3313 : static bool OGRGeometryFactoryCompareArea(const sPolyExtended &sPoly1,
    1499             :                                           const sPolyExtended &sPoly2)
    1500             : {
    1501        3313 :     return sPoly2.dfArea < sPoly1.dfArea;
    1502             : }
    1503             : 
    1504        3327 : static bool OGRGeometryFactoryCompareByIndex(const sPolyExtended &sPoly1,
    1505             :                                              const sPolyExtended &sPoly2)
    1506             : {
    1507        3327 :     return sPoly1.nInitialIndex < sPoly2.nInitialIndex;
    1508             : }
    1509             : 
    1510             : constexpr int N_CRITICAL_PART_NUMBER = 100;
    1511             : 
    1512             : enum OrganizePolygonMethod
    1513             : {
    1514             :     METHOD_NORMAL,
    1515             :     METHOD_SKIP,
    1516             :     METHOD_ONLY_CCW,
    1517             :     METHOD_CCW_INNER_JUST_AFTER_CW_OUTER
    1518             : };
    1519             : 
    1520             : /**
    1521             :  * \brief Organize polygons based on geometries.
    1522             :  *
    1523             :  * Analyse a set of rings (passed as simple polygons), and based on a
    1524             :  * geometric analysis convert them into a polygon with inner rings,
    1525             :  * (or a MultiPolygon if dealing with more than one polygon) that follow the
    1526             :  * OGC Simple Feature specification.
    1527             :  *
    1528             :  * All the input geometries must be OGRPolygon/OGRCurvePolygon with only a valid
    1529             :  * exterior ring (at least 4 points) and no interior rings.
    1530             :  *
    1531             :  * The passed in geometries become the responsibility of the method, but the
    1532             :  * papoPolygons "pointer array" remains owned by the caller.
    1533             :  *
    1534             :  * For faster computation, a polygon is considered to be inside
    1535             :  * another one if a single point of its external ring is included into the other
    1536             :  * one. (unless 'OGR_DEBUG_ORGANIZE_POLYGONS' configuration option is set to
    1537             :  * TRUE. In that case, a slower algorithm that tests exact topological
    1538             :  * relationships is used if GEOS is available.)
    1539             :  *
    1540             :  * In cases where a big number of polygons is passed to this function, the
    1541             :  * default processing may be really slow. You can skip the processing by adding
    1542             :  * METHOD=SKIP to the option list (the result of the function will be a
    1543             :  * multi-polygon with all polygons as toplevel polygons) or only make it analyze
    1544             :  * counterclockwise polygons by adding METHOD=ONLY_CCW to the option list if you
    1545             :  * can assume that the outline of holes is counterclockwise defined (this is the
    1546             :  * convention for example in shapefiles, Personal Geodatabases or File
    1547             :  * Geodatabases).
    1548             :  *
    1549             :  * For FileGDB, in most cases, but not always, a faster method than ONLY_CCW can
    1550             :  * be used. It is CCW_INNER_JUST_AFTER_CW_OUTER. When using it, inner rings are
    1551             :  * assumed to be counterclockwise oriented, and following immediately the outer
    1552             :  * ring (clockwise oriented) that they belong to. If that assumption is not met,
    1553             :  * an inner ring could be attached to the wrong outer ring, so this method must
    1554             :  * be used with care.
    1555             :  *
    1556             :  * If the OGR_ORGANIZE_POLYGONS configuration option is defined, its value will
    1557             :  * override the value of the METHOD option of papszOptions (useful to modify the
    1558             :  * behavior of the shapefile driver)
    1559             :  *
    1560             :  * @param papoPolygons array of geometry pointers - should all be OGRPolygons
    1561             :  * or OGRCurvePolygons. Ownership of the geometries is passed, but not of the
    1562             :  * array itself.
    1563             :  * @param nPolygonCount number of items in papoPolygons
    1564             :  * @param pbIsValidGeometry value may be set to FALSE if an invalid result is
    1565             :  * detected. Validity checks vary according to the method used and are are limited
    1566             :  * to what is needed to link inner rings to outer rings, so a result of TRUE
    1567             :  * does not mean that OGRGeometry::IsValid() returns TRUE.
    1568             :  * @param papszOptions a list of strings for passing options
    1569             :  *
    1570             :  * @return a single resulting geometry (either OGRPolygon, OGRCurvePolygon,
    1571             :  * OGRMultiPolygon, OGRMultiSurface or OGRGeometryCollection). Returns a
    1572             :  * POLYGON EMPTY in the case of nPolygonCount being 0.
    1573             :  */
    1574             : 
    1575         811 : OGRGeometry *OGRGeometryFactory::organizePolygons(OGRGeometry **papoPolygons,
    1576             :                                                   int nPolygonCount,
    1577             :                                                   int *pbIsValidGeometry,
    1578             :                                                   const char **papszOptions)
    1579             : {
    1580         811 :     if (nPolygonCount == 0)
    1581             :     {
    1582           4 :         if (pbIsValidGeometry)
    1583           0 :             *pbIsValidGeometry = TRUE;
    1584             : 
    1585           4 :         return new OGRPolygon();
    1586             :     }
    1587             : 
    1588         807 :     OGRGeometry *geom = nullptr;
    1589         807 :     OrganizePolygonMethod method = METHOD_NORMAL;
    1590         807 :     bool bHasCurves = false;
    1591             : 
    1592             :     /* -------------------------------------------------------------------- */
    1593             :     /*      Trivial case of a single polygon.                               */
    1594             :     /* -------------------------------------------------------------------- */
    1595         807 :     if (nPolygonCount == 1)
    1596             :     {
    1597             :         OGRwkbGeometryType eType =
    1598          20 :             wkbFlatten(papoPolygons[0]->getGeometryType());
    1599             : 
    1600          20 :         bool bIsValid = true;
    1601             : 
    1602          20 :         if (eType != wkbPolygon && eType != wkbCurvePolygon)
    1603             :         {
    1604           3 :             CPLError(CE_Warning, CPLE_AppDefined,
    1605             :                      "organizePolygons() received a non-Polygon geometry.");
    1606           3 :             bIsValid = false;
    1607           3 :             delete papoPolygons[0];
    1608           3 :             geom = new OGRPolygon();
    1609             :         }
    1610             :         else
    1611             :         {
    1612          17 :             geom = papoPolygons[0];
    1613             :         }
    1614             : 
    1615          20 :         papoPolygons[0] = nullptr;
    1616             : 
    1617          20 :         if (pbIsValidGeometry)
    1618           9 :             *pbIsValidGeometry = bIsValid;
    1619             : 
    1620          20 :         return geom;
    1621             :     }
    1622             : 
    1623         787 :     bool bUseFastVersion = true;
    1624         787 :     if (CPLTestBool(CPLGetConfigOption("OGR_DEBUG_ORGANIZE_POLYGONS", "NO")))
    1625             :     {
    1626             :         /* ------------------------------------------------------------------ */
    1627             :         /*      A wee bit of a warning.                                       */
    1628             :         /* ------------------------------------------------------------------ */
    1629             :         static int firstTime = 1;
    1630             :         // cppcheck-suppress knownConditionTrueFalse
    1631           0 :         if (!haveGEOS() && firstTime)
    1632             :         {
    1633           0 :             CPLDebug(
    1634             :                 "OGR",
    1635             :                 "In OGR_DEBUG_ORGANIZE_POLYGONS mode, GDAL should be built "
    1636             :                 "with GEOS support enabled in order "
    1637             :                 "OGRGeometryFactory::organizePolygons to provide reliable "
    1638             :                 "results on complex polygons.");
    1639           0 :             firstTime = 0;
    1640             :         }
    1641             :         // cppcheck-suppress knownConditionTrueFalse
    1642           0 :         bUseFastVersion = !haveGEOS();
    1643             :     }
    1644             : 
    1645             :     /* -------------------------------------------------------------------- */
    1646             :     /*      Setup per polygon envelope and area information.                */
    1647             :     /* -------------------------------------------------------------------- */
    1648        1574 :     std::vector<sPolyExtended> asPolyEx;
    1649         787 :     asPolyEx.reserve(nPolygonCount);
    1650             : 
    1651         787 :     bool bValidTopology = true;
    1652         787 :     bool bMixedUpGeometries = false;
    1653         787 :     bool bFoundCCW = false;
    1654             : 
    1655         787 :     const char *pszMethodValue = CSLFetchNameValue(papszOptions, "METHOD");
    1656             :     const char *pszMethodValueOption =
    1657         787 :         CPLGetConfigOption("OGR_ORGANIZE_POLYGONS", nullptr);
    1658         787 :     if (pszMethodValueOption != nullptr && pszMethodValueOption[0] != '\0')
    1659           3 :         pszMethodValue = pszMethodValueOption;
    1660             : 
    1661         787 :     if (pszMethodValue != nullptr)
    1662             :     {
    1663         307 :         if (EQUAL(pszMethodValue, "SKIP"))
    1664             :         {
    1665           7 :             method = METHOD_SKIP;
    1666           7 :             bMixedUpGeometries = true;
    1667             :         }
    1668         300 :         else if (EQUAL(pszMethodValue, "ONLY_CCW"))
    1669             :         {
    1670         246 :             method = METHOD_ONLY_CCW;
    1671             :         }
    1672          54 :         else if (EQUAL(pszMethodValue, "CCW_INNER_JUST_AFTER_CW_OUTER"))
    1673             :         {
    1674           0 :             method = METHOD_CCW_INNER_JUST_AFTER_CW_OUTER;
    1675             :         }
    1676          54 :         else if (!EQUAL(pszMethodValue, "DEFAULT"))
    1677             :         {
    1678           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1679             :                      "Unrecognized value for METHOD option : %s",
    1680             :                      pszMethodValue);
    1681             :         }
    1682             :     }
    1683             : 
    1684         787 :     int nCountCWPolygon = 0;
    1685         787 :     int indexOfCWPolygon = -1;
    1686             : 
    1687        3054 :     for (int i = 0; i < nPolygonCount; i++)
    1688             :     {
    1689             :         OGRwkbGeometryType eType =
    1690        2267 :             wkbFlatten(papoPolygons[i]->getGeometryType());
    1691             : 
    1692        2267 :         if (eType != wkbPolygon && eType != wkbCurvePolygon)
    1693             :         {
    1694             :             // Ignore any points or lines that find their way in here.
    1695           3 :             CPLError(CE_Warning, CPLE_AppDefined,
    1696             :                      "organizePolygons() received a non-Polygon geometry.");
    1697           3 :             delete papoPolygons[i];
    1698           3 :             continue;
    1699             :         }
    1700             : 
    1701        4528 :         sPolyExtended sPolyEx;
    1702             : 
    1703        2264 :         sPolyEx.nInitialIndex = i;
    1704        2264 :         sPolyEx.poGeometry = papoPolygons[i];
    1705        2264 :         sPolyEx.poPolygon = papoPolygons[i]->toCurvePolygon();
    1706             : 
    1707        2264 :         papoPolygons[i]->getEnvelope(&sPolyEx.sEnvelope);
    1708             : 
    1709        2264 :         if (eType == wkbCurvePolygon)
    1710          33 :             bHasCurves = true;
    1711        2264 :         if (!sPolyEx.poPolygon->IsEmpty() &&
    1712        4528 :             sPolyEx.poPolygon->getNumInteriorRings() == 0 &&
    1713        2264 :             sPolyEx.poPolygon->getExteriorRingCurve()->getNumPoints() >= 4)
    1714             :         {
    1715        2264 :             if (method != METHOD_CCW_INNER_JUST_AFTER_CW_OUTER)
    1716        2264 :                 sPolyEx.dfArea = sPolyEx.poPolygon->get_Area();
    1717        2264 :             sPolyEx.poExteriorRing = sPolyEx.poPolygon->getExteriorRingCurve();
    1718        2264 :             sPolyEx.poExteriorRing->StartPoint(&sPolyEx.poAPoint);
    1719        2264 :             if (eType == wkbPolygon)
    1720             :             {
    1721        2231 :                 sPolyEx.bIsCW = CPL_TO_BOOL(
    1722        2231 :                     sPolyEx.poExteriorRing->toLinearRing()->isClockwise());
    1723        2231 :                 sPolyEx.bIsPolygon = true;
    1724             :             }
    1725             :             else
    1726             :             {
    1727          33 :                 OGRLineString *poLS = sPolyEx.poExteriorRing->CurveToLine();
    1728          66 :                 OGRLinearRing oLR;
    1729          33 :                 oLR.addSubLineString(poLS);
    1730          33 :                 sPolyEx.bIsCW = CPL_TO_BOOL(oLR.isClockwise());
    1731          33 :                 sPolyEx.bIsPolygon = false;
    1732          33 :                 delete poLS;
    1733             :             }
    1734        2264 :             if (sPolyEx.bIsCW)
    1735             :             {
    1736        1426 :                 indexOfCWPolygon = i;
    1737        1426 :                 nCountCWPolygon++;
    1738             :             }
    1739        2264 :             if (!bFoundCCW)
    1740        1549 :                 bFoundCCW = !(sPolyEx.bIsCW);
    1741             :         }
    1742             :         else
    1743             :         {
    1744           0 :             if (!bMixedUpGeometries)
    1745             :             {
    1746           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1747             :                          "organizePolygons() received an unexpected geometry.  "
    1748             :                          "Either a polygon with interior rings, or a polygon "
    1749             :                          "with less than 4 points, or a non-Polygon geometry.  "
    1750             :                          "Return arguments as a collection.");
    1751           0 :                 bMixedUpGeometries = true;
    1752             :             }
    1753             :         }
    1754             : 
    1755        2264 :         asPolyEx.push_back(std::move(sPolyEx));
    1756             :     }
    1757             : 
    1758             :     // If we are in ONLY_CCW mode and that we have found that there is only one
    1759             :     // outer ring, then it is pretty easy : we can assume that all other rings
    1760             :     // are inside.
    1761         787 :     if ((method == METHOD_ONLY_CCW ||
    1762         246 :          method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER) &&
    1763          77 :         nCountCWPolygon == 1 && bUseFastVersion)
    1764             :     {
    1765          77 :         OGRCurvePolygon *poCP = asPolyEx[indexOfCWPolygon].poPolygon;
    1766         279 :         for (int i = 0; i < static_cast<int>(asPolyEx.size()); i++)
    1767             :         {
    1768         202 :             if (i != indexOfCWPolygon)
    1769             :             {
    1770         125 :                 poCP->addRingDirectly(
    1771         125 :                     asPolyEx[i].poPolygon->stealExteriorRingCurve());
    1772         125 :                 delete asPolyEx[i].poPolygon;
    1773             :             }
    1774             :         }
    1775             : 
    1776          77 :         if (pbIsValidGeometry)
    1777          75 :             *pbIsValidGeometry = TRUE;
    1778          77 :         return poCP;
    1779             :     }
    1780             : 
    1781         710 :     if (method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER && asPolyEx[0].bIsCW)
    1782             :     {
    1783             :         // Inner rings are CCW oriented and follow immediately the outer
    1784             :         // ring (that is CW oriented) in which they are included.
    1785           0 :         OGRMultiSurface *poMulti = nullptr;
    1786           0 :         OGRCurvePolygon *poCur = asPolyEx[0].poPolygon;
    1787           0 :         OGRGeometry *poRet = poCur;
    1788             :         // We have already checked that the first ring is CW.
    1789           0 :         OGREnvelope *psEnvelope = &(asPolyEx[0].sEnvelope);
    1790           0 :         for (std::size_t i = 1; i < asPolyEx.size(); i++)
    1791             :         {
    1792           0 :             if (asPolyEx[i].bIsCW)
    1793             :             {
    1794           0 :                 if (poMulti == nullptr)
    1795             :                 {
    1796           0 :                     if (bHasCurves)
    1797           0 :                         poMulti = new OGRMultiSurface();
    1798             :                     else
    1799           0 :                         poMulti = new OGRMultiPolygon();
    1800           0 :                     poRet = poMulti;
    1801           0 :                     poMulti->addGeometryDirectly(poCur);
    1802             :                 }
    1803           0 :                 poCur = asPolyEx[i].poPolygon;
    1804           0 :                 poMulti->addGeometryDirectly(poCur);
    1805           0 :                 psEnvelope = &(asPolyEx[i].sEnvelope);
    1806             :             }
    1807             :             else
    1808             :             {
    1809           0 :                 poCur->addRingDirectly(
    1810           0 :                     asPolyEx[i].poPolygon->stealExteriorRingCurve());
    1811           0 :                 if (!(asPolyEx[i].poAPoint.getX() >= psEnvelope->MinX &&
    1812           0 :                       asPolyEx[i].poAPoint.getX() <= psEnvelope->MaxX &&
    1813           0 :                       asPolyEx[i].poAPoint.getY() >= psEnvelope->MinY &&
    1814           0 :                       asPolyEx[i].poAPoint.getY() <= psEnvelope->MaxY))
    1815             :                 {
    1816           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1817             :                              "Part %d does not respect "
    1818             :                              "CCW_INNER_JUST_AFTER_CW_OUTER rule",
    1819             :                              static_cast<int>(i));
    1820             :                 }
    1821           0 :                 delete asPolyEx[i].poPolygon;
    1822             :             }
    1823             :         }
    1824             : 
    1825           0 :         if (pbIsValidGeometry)
    1826           0 :             *pbIsValidGeometry = TRUE;
    1827           0 :         return poRet;
    1828             :     }
    1829         710 :     else if (method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER)
    1830             :     {
    1831           0 :         method = METHOD_ONLY_CCW;
    1832           0 :         for (std::size_t i = 0; i < asPolyEx.size(); i++)
    1833           0 :             asPolyEx[i].dfArea = asPolyEx[i].poPolygon->get_Area();
    1834             :     }
    1835             : 
    1836             :     // Emits a warning if the number of parts is sufficiently big to anticipate
    1837             :     // for very long computation time, and the user didn't specify an explicit
    1838             :     // method.
    1839         710 :     if (nPolygonCount > N_CRITICAL_PART_NUMBER && method == METHOD_NORMAL &&
    1840             :         pszMethodValue == nullptr)
    1841             :     {
    1842             :         static int firstTime = 1;
    1843           0 :         if (firstTime)
    1844             :         {
    1845           0 :             if (bFoundCCW)
    1846             :             {
    1847           0 :                 CPLError(
    1848             :                     CE_Warning, CPLE_AppDefined,
    1849             :                     "organizePolygons() received a polygon with more than %d "
    1850             :                     "parts. The processing may be really slow.  "
    1851             :                     "You can skip the processing by setting METHOD=SKIP, "
    1852             :                     "or only make it analyze counter-clock wise parts by "
    1853             :                     "setting METHOD=ONLY_CCW if you can assume that the "
    1854             :                     "outline of holes is counter-clock wise defined",
    1855             :                     N_CRITICAL_PART_NUMBER);
    1856             :             }
    1857             :             else
    1858             :             {
    1859           0 :                 CPLError(
    1860             :                     CE_Warning, CPLE_AppDefined,
    1861             :                     "organizePolygons() received a polygon with more than %d "
    1862             :                     "parts.  The processing may be really slow.  "
    1863             :                     "You can skip the processing by setting METHOD=SKIP.",
    1864             :                     N_CRITICAL_PART_NUMBER);
    1865             :             }
    1866           0 :             firstTime = 0;
    1867             :         }
    1868             :     }
    1869             : 
    1870             :     /* This a nulti-step algorithm :
    1871             :        1) Sort polygons by descending areas
    1872             :        2) For each polygon of rank i, find its smallest enclosing polygon
    1873             :           among the polygons of rank [i-1 ... 0]. If there are no such polygon,
    1874             :           this is a top-level polygon. Otherwise, depending on if the enclosing
    1875             :           polygon is top-level or not, we can decide if we are top-level or not
    1876             :        3) Re-sort the polygons to retrieve their initial order (nicer for
    1877             :           some applications)
    1878             :        4) For each non top-level polygon (= inner ring), add it to its
    1879             :           outer ring
    1880             :        5) Add the top-level polygons to the multipolygon
    1881             : 
    1882             :        Complexity : O(nPolygonCount^2)
    1883             :     */
    1884             : 
    1885             :     /* Compute how each polygon relate to the other ones
    1886             :        To save a bit of computation we always begin the computation by a test
    1887             :        on the envelope. We also take into account the areas to avoid some
    1888             :        useless tests.  (A contains B implies envelop(A) contains envelop(B)
    1889             :        and area(A) > area(B)) In practice, we can hope that few full geometry
    1890             :        intersection of inclusion test is done:
    1891             :        * if the polygons are well separated geographically (a set of islands
    1892             :        for example), no full geometry intersection or inclusion test is done.
    1893             :        (the envelopes don't intersect each other)
    1894             : 
    1895             :        * if the polygons are 'lake inside an island inside a lake inside an
    1896             :        area' and that each polygon is much smaller than its enclosing one,
    1897             :        their bounding boxes are strictly contained into each other, and thus,
    1898             :        no full geometry intersection or inclusion test is done
    1899             :     */
    1900             : 
    1901         710 :     if (!bMixedUpGeometries)
    1902             :     {
    1903             :         // STEP 1: Sort polygons by descending area.
    1904         703 :         std::sort(asPolyEx.begin(), asPolyEx.end(),
    1905             :                   OGRGeometryFactoryCompareArea);
    1906             :     }
    1907         710 :     papoPolygons = nullptr;  // Just to use to avoid it afterwards.
    1908             : 
    1909             :     /* -------------------------------------------------------------------- */
    1910             :     /*      Compute relationships, if things seem well structured.          */
    1911             :     /* -------------------------------------------------------------------- */
    1912             : 
    1913             :     // The first (largest) polygon is necessarily top-level.
    1914         710 :     asPolyEx[0].bIsTopLevel = true;
    1915         710 :     asPolyEx[0].poEnclosingPolygon = nullptr;
    1916             : 
    1917         710 :     int nCountTopLevel = 1;
    1918             : 
    1919             :     // STEP 2.
    1920        4103 :     for (int i = 1; !bMixedUpGeometries && bValidTopology &&
    1921        2048 :                     i < static_cast<int>(asPolyEx.size());
    1922             :          i++)
    1923             :     {
    1924        1345 :         if (method == METHOD_ONLY_CCW && asPolyEx[i].bIsCW)
    1925             :         {
    1926         196 :             nCountTopLevel++;
    1927         196 :             asPolyEx[i].bIsTopLevel = true;
    1928         196 :             asPolyEx[i].poEnclosingPolygon = nullptr;
    1929         196 :             continue;
    1930             :         }
    1931             : 
    1932        1149 :         int j = i - 1;  // Used after for.
    1933        2439 :         for (; bValidTopology && j >= 0; j--)
    1934             :         {
    1935        1968 :             bool b_i_inside_j = false;
    1936             : 
    1937        1968 :             if (method == METHOD_ONLY_CCW && asPolyEx[j].bIsCW == false)
    1938             :             {
    1939             :                 // In that mode, i which is CCW if we reach here can only be
    1940             :                 // included in a CW polygon.
    1941           8 :                 continue;
    1942             :             }
    1943             : 
    1944        1960 :             if (asPolyEx[j].sEnvelope.Contains(asPolyEx[i].sEnvelope))
    1945             :             {
    1946         684 :                 if (bUseFastVersion)
    1947             :                 {
    1948         684 :                     if (method == METHOD_ONLY_CCW && j == 0)
    1949             :                     {
    1950             :                         // We are testing if a CCW ring is in the biggest CW
    1951             :                         // ring It *must* be inside as this is the last
    1952             :                         // candidate, otherwise the winding order rules is
    1953             :                         // broken.
    1954         123 :                         b_i_inside_j = true;
    1955             :                     }
    1956        1122 :                     else if (asPolyEx[i].bIsPolygon && asPolyEx[j].bIsPolygon &&
    1957         561 :                              asPolyEx[j]
    1958         561 :                                  .poExteriorRing->toLinearRing()
    1959         561 :                                  ->isPointOnRingBoundary(&asPolyEx[i].poAPoint,
    1960             :                                                          FALSE))
    1961             :                     {
    1962             :                         OGRLinearRing *poLR_i =
    1963          16 :                             asPolyEx[i].poExteriorRing->toLinearRing();
    1964             :                         OGRLinearRing *poLR_j =
    1965          16 :                             asPolyEx[j].poExteriorRing->toLinearRing();
    1966             : 
    1967             :                         // If the point of i is on the boundary of j, we will
    1968             :                         // iterate over the other points of i.
    1969          16 :                         const int nPoints = poLR_i->getNumPoints();
    1970          16 :                         int k = 1;  // Used after for.
    1971          32 :                         OGRPoint previousPoint = asPolyEx[i].poAPoint;
    1972          31 :                         for (; k < nPoints; k++)
    1973             :                         {
    1974          30 :                             OGRPoint point;
    1975          30 :                             poLR_i->getPoint(k, &point);
    1976          32 :                             if (point.getX() == previousPoint.getX() &&
    1977           2 :                                 point.getY() == previousPoint.getY())
    1978             :                             {
    1979           0 :                                 continue;
    1980             :                             }
    1981          30 :                             if (poLR_j->isPointOnRingBoundary(&point, FALSE))
    1982             :                             {
    1983             :                                 // If it is on the boundary of j, iterate again.
    1984             :                             }
    1985          15 :                             else if (poLR_j->isPointInRing(&point, FALSE))
    1986             :                             {
    1987             :                                 // If then point is strictly included in j, then
    1988             :                                 // i is considered inside j.
    1989          13 :                                 b_i_inside_j = true;
    1990          13 :                                 break;
    1991             :                             }
    1992             :                             else
    1993             :                             {
    1994             :                                 // If it is outside, then i cannot be inside j.
    1995           2 :                                 break;
    1996             :                             }
    1997          15 :                             previousPoint = point;
    1998             :                         }
    1999          16 :                         if (!b_i_inside_j && k == nPoints && nPoints > 2)
    2000             :                         {
    2001             :                             // All points of i are on the boundary of j.
    2002             :                             // Take a point in the middle of a segment of i and
    2003             :                             // test it against j.
    2004           1 :                             poLR_i->getPoint(0, &previousPoint);
    2005           2 :                             for (k = 1; k < nPoints; k++)
    2006             :                             {
    2007           2 :                                 OGRPoint point;
    2008           2 :                                 poLR_i->getPoint(k, &point);
    2009           2 :                                 if (point.getX() == previousPoint.getX() &&
    2010           0 :                                     point.getY() == previousPoint.getY())
    2011             :                                 {
    2012           0 :                                     continue;
    2013             :                                 }
    2014           2 :                                 OGRPoint pointMiddle;
    2015           2 :                                 pointMiddle.setX(
    2016           2 :                                     (point.getX() + previousPoint.getX()) / 2);
    2017           2 :                                 pointMiddle.setY(
    2018           2 :                                     (point.getY() + previousPoint.getY()) / 2);
    2019           2 :                                 if (poLR_j->isPointOnRingBoundary(&pointMiddle,
    2020           2 :                                                                   FALSE))
    2021             :                                 {
    2022             :                                     // If it is on the boundary of j, iterate
    2023             :                                     // again.
    2024             :                                 }
    2025           1 :                                 else if (poLR_j->isPointInRing(&pointMiddle,
    2026           1 :                                                                FALSE))
    2027             :                                 {
    2028             :                                     // If then point is strictly included in j,
    2029             :                                     // then i is considered inside j.
    2030           1 :                                     b_i_inside_j = true;
    2031           1 :                                     break;
    2032             :                                 }
    2033             :                                 else
    2034             :                                 {
    2035             :                                     // If it is outside, then i cannot be inside
    2036             :                                     // j.
    2037           0 :                                     break;
    2038             :                                 }
    2039           1 :                                 previousPoint = point;
    2040             :                             }
    2041             :                         }
    2042             :                     }
    2043             :                     // Note that isPointInRing only test strict inclusion in the
    2044             :                     // ring.
    2045        1090 :                     else if (asPolyEx[i].bIsPolygon && asPolyEx[j].bIsPolygon &&
    2046         545 :                              asPolyEx[j]
    2047         545 :                                  .poExteriorRing->toLinearRing()
    2048         545 :                                  ->isPointInRing(&asPolyEx[i].poAPoint, FALSE))
    2049             :                     {
    2050         541 :                         b_i_inside_j = true;
    2051             :                     }
    2052             :                 }
    2053           0 :                 else if (asPolyEx[j].poPolygon->Contains(asPolyEx[i].poPolygon))
    2054             :                 {
    2055           0 :                     b_i_inside_j = true;
    2056             :                 }
    2057             :             }
    2058             : 
    2059        1960 :             if (b_i_inside_j)
    2060             :             {
    2061         678 :                 if (asPolyEx[j].bIsTopLevel)
    2062             :                 {
    2063             :                     // We are a lake.
    2064         677 :                     asPolyEx[i].bIsTopLevel = false;
    2065         677 :                     asPolyEx[i].poEnclosingPolygon = asPolyEx[j].poPolygon;
    2066             :                 }
    2067             :                 else
    2068             :                 {
    2069             :                     // We are included in a something not toplevel (a lake),
    2070             :                     // so in OGCSF we are considered as toplevel too.
    2071           1 :                     nCountTopLevel++;
    2072           1 :                     asPolyEx[i].bIsTopLevel = true;
    2073           1 :                     asPolyEx[i].poEnclosingPolygon = nullptr;
    2074             :                 }
    2075         678 :                 break;
    2076             :             }
    2077             :             // Use Overlaps instead of Intersects to be more
    2078             :             // tolerant about touching polygons.
    2079           0 :             else if (bUseFastVersion ||
    2080        1282 :                      !asPolyEx[i].sEnvelope.Intersects(asPolyEx[j].sEnvelope) ||
    2081           0 :                      !asPolyEx[i].poPolygon->Overlaps(asPolyEx[j].poPolygon))
    2082             :             {
    2083             :             }
    2084             :             else
    2085             :             {
    2086             :                 // Bad... The polygons are intersecting but no one is
    2087             :                 // contained inside the other one. This is a really broken
    2088             :                 // case. We just make a multipolygon with the whole set of
    2089             :                 // polygons.
    2090           0 :                 bValidTopology = false;
    2091             : #ifdef DEBUG
    2092           0 :                 char *wkt1 = nullptr;
    2093           0 :                 char *wkt2 = nullptr;
    2094           0 :                 asPolyEx[i].poPolygon->exportToWkt(&wkt1);
    2095           0 :                 asPolyEx[j].poPolygon->exportToWkt(&wkt2);
    2096           0 :                 CPLDebug("OGR",
    2097             :                          "Bad intersection for polygons %d and %d\n"
    2098             :                          "geom %d: %s\n"
    2099             :                          "geom %d: %s",
    2100             :                          static_cast<int>(i), j, static_cast<int>(i), wkt1, j,
    2101             :                          wkt2);
    2102           0 :                 CPLFree(wkt1);
    2103           0 :                 CPLFree(wkt2);
    2104             : #endif
    2105             :             }
    2106             :         }
    2107             : 
    2108        1149 :         if (j < 0)
    2109             :         {
    2110             :             // We come here because we are not included in anything.
    2111             :             // We are toplevel.
    2112         471 :             nCountTopLevel++;
    2113         471 :             asPolyEx[i].bIsTopLevel = true;
    2114         471 :             asPolyEx[i].poEnclosingPolygon = nullptr;
    2115             :         }
    2116             :     }
    2117             : 
    2118         710 :     if (pbIsValidGeometry)
    2119         220 :         *pbIsValidGeometry = bValidTopology && !bMixedUpGeometries;
    2120             : 
    2121             :     /* --------------------------------------------------------------------- */
    2122             :     /*      Things broke down - just mark everything as top-level so it gets */
    2123             :     /*      turned into a multipolygon.                                      */
    2124             :     /* --------------------------------------------------------------------- */
    2125         710 :     if (!bValidTopology || bMixedUpGeometries)
    2126             :     {
    2127          21 :         for (auto &sPolyEx : asPolyEx)
    2128             :         {
    2129          14 :             sPolyEx.bIsTopLevel = true;
    2130             :         }
    2131           7 :         nCountTopLevel = static_cast<int>(asPolyEx.size());
    2132             :     }
    2133             : 
    2134             :     /* -------------------------------------------------------------------- */
    2135             :     /*      Try to turn into one or more polygons based on the ring         */
    2136             :     /*      relationships.                                                  */
    2137             :     /* -------------------------------------------------------------------- */
    2138             :     // STEP 3: Sort again in initial order.
    2139         710 :     std::sort(asPolyEx.begin(), asPolyEx.end(),
    2140             :               OGRGeometryFactoryCompareByIndex);
    2141             : 
    2142             :     // STEP 4: Add holes as rings of their enclosing polygon.
    2143        2772 :     for (auto &sPolyEx : asPolyEx)
    2144             :     {
    2145        2062 :         if (sPolyEx.bIsTopLevel == false)
    2146             :         {
    2147         677 :             sPolyEx.poEnclosingPolygon->addRingDirectly(
    2148         677 :                 sPolyEx.poPolygon->stealExteriorRingCurve());
    2149         677 :             delete sPolyEx.poPolygon;
    2150             :         }
    2151        1385 :         else if (nCountTopLevel == 1)
    2152             :         {
    2153          68 :             geom = sPolyEx.poPolygon;
    2154             :         }
    2155             :     }
    2156             : 
    2157             :     // STEP 5: Add toplevel polygons.
    2158         710 :     if (nCountTopLevel > 1)
    2159             :     {
    2160             :         OGRGeometryCollection *poGC =
    2161         642 :             bHasCurves ? new OGRMultiSurface() : new OGRMultiPolygon();
    2162        2541 :         for (auto &sPolyEx : asPolyEx)
    2163             :         {
    2164        1899 :             if (sPolyEx.bIsTopLevel)
    2165             :             {
    2166        1317 :                 poGC->addGeometryDirectly(sPolyEx.poPolygon);
    2167             :             }
    2168             :         }
    2169         642 :         geom = poGC;
    2170             :     }
    2171             : 
    2172         710 :     return geom;
    2173             : }
    2174             : 
    2175             : /************************************************************************/
    2176             : /*                           createFromGML()                            */
    2177             : /************************************************************************/
    2178             : 
    2179             : /**
    2180             :  * \brief Create geometry from GML.
    2181             :  *
    2182             :  * This method translates a fragment of GML containing only the geometry
    2183             :  * portion into a corresponding OGRGeometry.  There are many limitations
    2184             :  * on the forms of GML geometries supported by this parser, but they are
    2185             :  * too numerous to list here.
    2186             :  *
    2187             :  * The following GML2 elements are parsed : Point, LineString, Polygon,
    2188             :  * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry.
    2189             :  *
    2190             :  * The following GML3 elements are parsed : Surface,
    2191             :  * MultiSurface, PolygonPatch, Triangle, Rectangle, Curve, MultiCurve,
    2192             :  * LineStringSegment, Arc, Circle, CompositeSurface, OrientableSurface, Solid,
    2193             :  * Tin, TriangulatedSurface.
    2194             :  *
    2195             :  * Arc and Circle elements are returned as curves by default. Stroking to
    2196             :  * linestrings can be done with
    2197             :  * OGR_G_ForceTo(hGeom, OGR_GT_GetLinear(OGR_G_GetGeometryType(hGeom)), NULL).
    2198             :  * A 4 degrees step is used by default, unless the user
    2199             :  * has overridden the value with the OGR_ARC_STEPSIZE configuration variable.
    2200             :  *
    2201             :  * The C function OGR_G_CreateFromGML() is the same as this method.
    2202             :  *
    2203             :  * @param pszData The GML fragment for the geometry.
    2204             :  *
    2205             :  * @return a geometry on success, or NULL on error.
    2206             :  *
    2207             :  * @see OGR_G_ForceTo()
    2208             :  * @see OGR_GT_GetLinear()
    2209             :  * @see OGR_G_GetGeometryType()
    2210             :  */
    2211             : 
    2212           0 : OGRGeometry *OGRGeometryFactory::createFromGML(const char *pszData)
    2213             : 
    2214             : {
    2215             :     OGRGeometryH hGeom;
    2216             : 
    2217           0 :     hGeom = OGR_G_CreateFromGML(pszData);
    2218             : 
    2219           0 :     return OGRGeometry::FromHandle(hGeom);
    2220             : }
    2221             : 
    2222             : /************************************************************************/
    2223             : /*                           createFromGEOS()                           */
    2224             : /************************************************************************/
    2225             : 
    2226             : /** Builds a OGRGeometry* from a GEOSGeom.
    2227             :  * @param hGEOSCtxt GEOS context
    2228             :  * @param geosGeom GEOS geometry
    2229             :  * @return a OGRGeometry*
    2230             :  */
    2231        1311 : OGRGeometry *OGRGeometryFactory::createFromGEOS(
    2232             :     UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt,
    2233             :     UNUSED_IF_NO_GEOS GEOSGeom geosGeom)
    2234             : 
    2235             : {
    2236             : #ifndef HAVE_GEOS
    2237             : 
    2238             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2239             :     return nullptr;
    2240             : 
    2241             : #else
    2242             : 
    2243        1311 :     size_t nSize = 0;
    2244        1311 :     unsigned char *pabyBuf = nullptr;
    2245        1311 :     OGRGeometry *poGeometry = nullptr;
    2246             : 
    2247             :     // Special case as POINT EMPTY cannot be translated to WKB.
    2248        1366 :     if (GEOSGeomTypeId_r(hGEOSCtxt, geosGeom) == GEOS_POINT &&
    2249          55 :         GEOSisEmpty_r(hGEOSCtxt, geosGeom))
    2250           4 :         return new OGRPoint();
    2251             : 
    2252             :     const int nCoordDim =
    2253        1306 :         GEOSGeom_getCoordinateDimension_r(hGEOSCtxt, geosGeom);
    2254        1307 :     GEOSWKBWriter *wkbwriter = GEOSWKBWriter_create_r(hGEOSCtxt);
    2255        1306 :     GEOSWKBWriter_setOutputDimension_r(hGEOSCtxt, wkbwriter, nCoordDim);
    2256        1306 :     pabyBuf = GEOSWKBWriter_write_r(hGEOSCtxt, wkbwriter, geosGeom, &nSize);
    2257        1306 :     GEOSWKBWriter_destroy_r(hGEOSCtxt, wkbwriter);
    2258             : 
    2259        1307 :     if (pabyBuf == nullptr || nSize == 0)
    2260             :     {
    2261           0 :         return nullptr;
    2262             :     }
    2263             : 
    2264        1307 :     if (OGRGeometryFactory::createFromWkb(pabyBuf, nullptr, &poGeometry,
    2265        1307 :                                           static_cast<int>(nSize)) !=
    2266             :         OGRERR_NONE)
    2267             :     {
    2268           0 :         poGeometry = nullptr;
    2269             :     }
    2270             : 
    2271        1307 :     GEOSFree_r(hGEOSCtxt, pabyBuf);
    2272             : 
    2273        1305 :     return poGeometry;
    2274             : 
    2275             : #endif  // HAVE_GEOS
    2276             : }
    2277             : 
    2278             : /************************************************************************/
    2279             : /*                              haveGEOS()                              */
    2280             : /************************************************************************/
    2281             : 
    2282             : /**
    2283             :  * \brief Test if GEOS enabled.
    2284             :  *
    2285             :  * This static method returns TRUE if GEOS support is built into OGR,
    2286             :  * otherwise it returns FALSE.
    2287             :  *
    2288             :  * @return TRUE if available, otherwise FALSE.
    2289             :  */
    2290             : 
    2291       39988 : bool OGRGeometryFactory::haveGEOS()
    2292             : 
    2293             : {
    2294             : #ifndef HAVE_GEOS
    2295             :     return false;
    2296             : #else
    2297       39988 :     return true;
    2298             : #endif
    2299             : }
    2300             : 
    2301             : /************************************************************************/
    2302             : /*                           createFromFgf()                            */
    2303             : /************************************************************************/
    2304             : 
    2305             : /**
    2306             :  * \brief Create a geometry object of the appropriate type from its FGF (FDO
    2307             :  * Geometry Format) binary representation.
    2308             :  *
    2309             :  * Also note that this is a static method, and that there
    2310             :  * is no need to instantiate an OGRGeometryFactory object.
    2311             :  *
    2312             :  * The C function OGR_G_CreateFromFgf() is the same as this method.
    2313             :  *
    2314             :  * @param pabyData pointer to the input BLOB data.
    2315             :  * @param poSR pointer to the spatial reference to be assigned to the
    2316             :  *             created geometry object.  This may be NULL.
    2317             :  * @param ppoReturn the newly created geometry object will be assigned to the
    2318             :  *                  indicated pointer on return.  This will be NULL in case
    2319             :  *                  of failure, but NULL might be a valid return for a NULL
    2320             :  * shape.
    2321             :  * @param nBytes the number of bytes available in pabyData.
    2322             :  * @param pnBytesConsumed if not NULL, it will be set to the number of bytes
    2323             :  * consumed (at most nBytes).
    2324             :  *
    2325             :  * @return OGRERR_NONE if all goes well, otherwise any of
    2326             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    2327             :  * OGRERR_CORRUPT_DATA may be returned.
    2328             :  */
    2329             : 
    2330         352 : OGRErr OGRGeometryFactory::createFromFgf(const void *pabyData,
    2331             :                                          OGRSpatialReference *poSR,
    2332             :                                          OGRGeometry **ppoReturn, int nBytes,
    2333             :                                          int *pnBytesConsumed)
    2334             : 
    2335             : {
    2336         352 :     return createFromFgfInternal(static_cast<const GByte *>(pabyData), poSR,
    2337         352 :                                  ppoReturn, nBytes, pnBytesConsumed, 0);
    2338             : }
    2339             : 
    2340             : /************************************************************************/
    2341             : /*                       createFromFgfInternal()                        */
    2342             : /************************************************************************/
    2343             : 
    2344         355 : OGRErr OGRGeometryFactory::createFromFgfInternal(
    2345             :     const unsigned char *pabyData, OGRSpatialReference *poSR,
    2346             :     OGRGeometry **ppoReturn, int nBytes, int *pnBytesConsumed, int nRecLevel)
    2347             : {
    2348             :     // Arbitrary value, but certainly large enough for reasonable usages.
    2349         355 :     if (nRecLevel == 32)
    2350             :     {
    2351           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2352             :                  "Too many recursion levels (%d) while parsing FGF geometry.",
    2353             :                  nRecLevel);
    2354           0 :         return OGRERR_CORRUPT_DATA;
    2355             :     }
    2356             : 
    2357         355 :     *ppoReturn = nullptr;
    2358             : 
    2359         355 :     if (nBytes < 4)
    2360         109 :         return OGRERR_NOT_ENOUGH_DATA;
    2361             : 
    2362             :     /* -------------------------------------------------------------------- */
    2363             :     /*      Decode the geometry type.                                       */
    2364             :     /* -------------------------------------------------------------------- */
    2365         246 :     GInt32 nGType = 0;
    2366         246 :     memcpy(&nGType, pabyData + 0, 4);
    2367         246 :     CPL_LSBPTR32(&nGType);
    2368             : 
    2369         246 :     if (nGType < 0 || nGType > 13)
    2370         232 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    2371             : 
    2372             :     /* -------------------------------------------------------------------- */
    2373             :     /*      Decode the dimensionality if appropriate.                       */
    2374             :     /* -------------------------------------------------------------------- */
    2375          14 :     int nTupleSize = 0;
    2376          14 :     GInt32 nGDim = 0;
    2377             : 
    2378             :     // TODO: Why is this a switch?
    2379          14 :     switch (nGType)
    2380             :     {
    2381           9 :         case 1:  // Point
    2382             :         case 2:  // LineString
    2383             :         case 3:  // Polygon
    2384           9 :             if (nBytes < 8)
    2385           0 :                 return OGRERR_NOT_ENOUGH_DATA;
    2386             : 
    2387           9 :             memcpy(&nGDim, pabyData + 4, 4);
    2388           9 :             CPL_LSBPTR32(&nGDim);
    2389             : 
    2390           9 :             if (nGDim < 0 || nGDim > 3)
    2391           0 :                 return OGRERR_CORRUPT_DATA;
    2392             : 
    2393           9 :             nTupleSize = 2;
    2394           9 :             if (nGDim & 0x01)  // Z
    2395           1 :                 nTupleSize++;
    2396           9 :             if (nGDim & 0x02)  // M
    2397           0 :                 nTupleSize++;
    2398             : 
    2399           9 :             break;
    2400             : 
    2401           5 :         default:
    2402           5 :             break;
    2403             :     }
    2404             : 
    2405          14 :     OGRGeometry *poGeom = nullptr;
    2406             : 
    2407             :     /* -------------------------------------------------------------------- */
    2408             :     /*      None                                                            */
    2409             :     /* -------------------------------------------------------------------- */
    2410          14 :     if (nGType == 0)
    2411             :     {
    2412           0 :         if (pnBytesConsumed)
    2413           0 :             *pnBytesConsumed = 4;
    2414             :     }
    2415             : 
    2416             :     /* -------------------------------------------------------------------- */
    2417             :     /*      Point                                                           */
    2418             :     /* -------------------------------------------------------------------- */
    2419          14 :     else if (nGType == 1)
    2420             :     {
    2421           3 :         if (nBytes < nTupleSize * 8 + 8)
    2422           0 :             return OGRERR_NOT_ENOUGH_DATA;
    2423             : 
    2424           3 :         double adfTuple[4] = {0.0, 0.0, 0.0, 0.0};
    2425           3 :         memcpy(adfTuple, pabyData + 8, nTupleSize * 8);
    2426             : #ifdef CPL_MSB
    2427             :         for (int iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++)
    2428             :             CPL_SWAP64PTR(adfTuple + iOrdinal);
    2429             : #endif
    2430           3 :         if (nTupleSize > 2)
    2431           1 :             poGeom = new OGRPoint(adfTuple[0], adfTuple[1], adfTuple[2]);
    2432             :         else
    2433           2 :             poGeom = new OGRPoint(adfTuple[0], adfTuple[1]);
    2434             : 
    2435           3 :         if (pnBytesConsumed)
    2436           1 :             *pnBytesConsumed = 8 + nTupleSize * 8;
    2437             :     }
    2438             : 
    2439             :     /* -------------------------------------------------------------------- */
    2440             :     /*      LineString                                                      */
    2441             :     /* -------------------------------------------------------------------- */
    2442          11 :     else if (nGType == 2)
    2443             :     {
    2444           2 :         if (nBytes < 12)
    2445           0 :             return OGRERR_NOT_ENOUGH_DATA;
    2446             : 
    2447           2 :         GInt32 nPointCount = 0;
    2448           2 :         memcpy(&nPointCount, pabyData + 8, 4);
    2449           2 :         CPL_LSBPTR32(&nPointCount);
    2450             : 
    2451           2 :         if (nPointCount < 0 || nPointCount > INT_MAX / (nTupleSize * 8))
    2452           0 :             return OGRERR_CORRUPT_DATA;
    2453             : 
    2454           2 :         if (nBytes - 12 < nTupleSize * 8 * nPointCount)
    2455           0 :             return OGRERR_NOT_ENOUGH_DATA;
    2456             : 
    2457           2 :         OGRLineString *poLS = new OGRLineString();
    2458           2 :         poGeom = poLS;
    2459           2 :         poLS->setNumPoints(nPointCount);
    2460             : 
    2461           4 :         for (int iPoint = 0; iPoint < nPointCount; iPoint++)
    2462             :         {
    2463           2 :             double adfTuple[4] = {0.0, 0.0, 0.0, 0.0};
    2464           2 :             memcpy(adfTuple, pabyData + 12 + 8 * nTupleSize * iPoint,
    2465           2 :                    nTupleSize * 8);
    2466             : #ifdef CPL_MSB
    2467             :             for (int iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++)
    2468             :                 CPL_SWAP64PTR(adfTuple + iOrdinal);
    2469             : #endif
    2470           2 :             if (nTupleSize > 2)
    2471           0 :                 poLS->setPoint(iPoint, adfTuple[0], adfTuple[1], adfTuple[2]);
    2472             :             else
    2473           2 :                 poLS->setPoint(iPoint, adfTuple[0], adfTuple[1]);
    2474             :         }
    2475             : 
    2476           2 :         if (pnBytesConsumed)
    2477           0 :             *pnBytesConsumed = 12 + nTupleSize * 8 * nPointCount;
    2478             :     }
    2479             : 
    2480             :     /* -------------------------------------------------------------------- */
    2481             :     /*      Polygon                                                         */
    2482             :     /* -------------------------------------------------------------------- */
    2483           9 :     else if (nGType == 3)
    2484             :     {
    2485           4 :         if (nBytes < 12)
    2486           0 :             return OGRERR_NOT_ENOUGH_DATA;
    2487             : 
    2488           4 :         GInt32 nRingCount = 0;
    2489           4 :         memcpy(&nRingCount, pabyData + 8, 4);
    2490           4 :         CPL_LSBPTR32(&nRingCount);
    2491             : 
    2492           4 :         if (nRingCount < 0 || nRingCount > INT_MAX / 4)
    2493           0 :             return OGRERR_CORRUPT_DATA;
    2494             : 
    2495             :         // Each ring takes at least 4 bytes.
    2496           4 :         if (nBytes - 12 < nRingCount * 4)
    2497           0 :             return OGRERR_NOT_ENOUGH_DATA;
    2498             : 
    2499           4 :         int nNextByte = 12;
    2500             : 
    2501           4 :         OGRPolygon *poPoly = new OGRPolygon();
    2502           4 :         poGeom = poPoly;
    2503             : 
    2504          10 :         for (int iRing = 0; iRing < nRingCount; iRing++)
    2505             :         {
    2506           6 :             if (nBytes - nNextByte < 4)
    2507             :             {
    2508           0 :                 delete poGeom;
    2509           0 :                 return OGRERR_NOT_ENOUGH_DATA;
    2510             :             }
    2511             : 
    2512           6 :             GInt32 nPointCount = 0;
    2513           6 :             memcpy(&nPointCount, pabyData + nNextByte, 4);
    2514           6 :             CPL_LSBPTR32(&nPointCount);
    2515             : 
    2516           6 :             if (nPointCount < 0 || nPointCount > INT_MAX / (nTupleSize * 8))
    2517             :             {
    2518           0 :                 delete poGeom;
    2519           0 :                 return OGRERR_CORRUPT_DATA;
    2520             :             }
    2521             : 
    2522           6 :             nNextByte += 4;
    2523             : 
    2524           6 :             if (nBytes - nNextByte < nTupleSize * 8 * nPointCount)
    2525             :             {
    2526           0 :                 delete poGeom;
    2527           0 :                 return OGRERR_NOT_ENOUGH_DATA;
    2528             :             }
    2529             : 
    2530           6 :             OGRLinearRing *poLR = new OGRLinearRing();
    2531           6 :             poLR->setNumPoints(nPointCount);
    2532             : 
    2533          12 :             for (int iPoint = 0; iPoint < nPointCount; iPoint++)
    2534             :             {
    2535           6 :                 double adfTuple[4] = {0.0, 0.0, 0.0, 0.0};
    2536           6 :                 memcpy(adfTuple, pabyData + nNextByte, nTupleSize * 8);
    2537           6 :                 nNextByte += nTupleSize * 8;
    2538             : 
    2539             : #ifdef CPL_MSB
    2540             :                 for (int iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++)
    2541             :                     CPL_SWAP64PTR(adfTuple + iOrdinal);
    2542             : #endif
    2543           6 :                 if (nTupleSize > 2)
    2544           0 :                     poLR->setPoint(iPoint, adfTuple[0], adfTuple[1],
    2545             :                                    adfTuple[2]);
    2546             :                 else
    2547           6 :                     poLR->setPoint(iPoint, adfTuple[0], adfTuple[1]);
    2548             :             }
    2549             : 
    2550           6 :             poPoly->addRingDirectly(poLR);
    2551             :         }
    2552             : 
    2553           4 :         if (pnBytesConsumed)
    2554           2 :             *pnBytesConsumed = nNextByte;
    2555             :     }
    2556             : 
    2557             :     /* -------------------------------------------------------------------- */
    2558             :     /*      GeometryCollections of various kinds.                           */
    2559             :     /* -------------------------------------------------------------------- */
    2560           5 :     else if (nGType == 4      // MultiPoint
    2561           5 :              || nGType == 5   // MultiLineString
    2562           5 :              || nGType == 6   // MultiPolygon
    2563           3 :              || nGType == 7)  // MultiGeometry
    2564             :     {
    2565           5 :         if (nBytes < 8)
    2566           3 :             return OGRERR_NOT_ENOUGH_DATA;
    2567             : 
    2568           5 :         GInt32 nGeomCount = 0;
    2569           5 :         memcpy(&nGeomCount, pabyData + 4, 4);
    2570           5 :         CPL_LSBPTR32(&nGeomCount);
    2571             : 
    2572           5 :         if (nGeomCount < 0 || nGeomCount > INT_MAX / 4)
    2573           0 :             return OGRERR_CORRUPT_DATA;
    2574             : 
    2575             :         // Each geometry takes at least 4 bytes.
    2576           5 :         if (nBytes - 8 < 4 * nGeomCount)
    2577           2 :             return OGRERR_NOT_ENOUGH_DATA;
    2578             : 
    2579           3 :         OGRGeometryCollection *poGC = nullptr;
    2580           3 :         if (nGType == 4)
    2581           0 :             poGC = new OGRMultiPoint();
    2582           3 :         else if (nGType == 5)
    2583           0 :             poGC = new OGRMultiLineString();
    2584           3 :         else if (nGType == 6)
    2585           1 :             poGC = new OGRMultiPolygon();
    2586           2 :         else if (nGType == 7)
    2587           2 :             poGC = new OGRGeometryCollection();
    2588             : 
    2589           3 :         int nBytesUsed = 8;
    2590             : 
    2591           5 :         for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
    2592             :         {
    2593           3 :             int nThisGeomSize = 0;
    2594           3 :             OGRGeometry *poThisGeom = nullptr;
    2595             : 
    2596           6 :             const OGRErr eErr = createFromFgfInternal(
    2597           3 :                 pabyData + nBytesUsed, poSR, &poThisGeom, nBytes - nBytesUsed,
    2598             :                 &nThisGeomSize, nRecLevel + 1);
    2599           3 :             if (eErr != OGRERR_NONE)
    2600             :             {
    2601           0 :                 delete poGC;
    2602           1 :                 return eErr;
    2603             :             }
    2604             : 
    2605           3 :             nBytesUsed += nThisGeomSize;
    2606           3 :             if (poThisGeom != nullptr)
    2607             :             {
    2608           3 :                 const OGRErr eErr2 = poGC->addGeometryDirectly(poThisGeom);
    2609           3 :                 if (eErr2 != OGRERR_NONE)
    2610             :                 {
    2611           1 :                     delete poGC;
    2612           1 :                     delete poThisGeom;
    2613           1 :                     return eErr2;
    2614             :                 }
    2615             :             }
    2616             :         }
    2617             : 
    2618           2 :         poGeom = poGC;
    2619           2 :         if (pnBytesConsumed)
    2620           2 :             *pnBytesConsumed = nBytesUsed;
    2621             :     }
    2622             : 
    2623             :     /* -------------------------------------------------------------------- */
    2624             :     /*      Currently unsupported geometry.                                 */
    2625             :     /*                                                                      */
    2626             :     /*      We need to add 10/11/12/13 curve types in some fashion.         */
    2627             :     /* -------------------------------------------------------------------- */
    2628             :     else
    2629             :     {
    2630           0 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    2631             :     }
    2632             : 
    2633             :     /* -------------------------------------------------------------------- */
    2634             :     /*      Assign spatial reference system.                                */
    2635             :     /* -------------------------------------------------------------------- */
    2636          11 :     if (poGeom != nullptr && poSR)
    2637           0 :         poGeom->assignSpatialReference(poSR);
    2638          11 :     *ppoReturn = poGeom;
    2639             : 
    2640          11 :     return OGRERR_NONE;
    2641             : }
    2642             : 
    2643             : /************************************************************************/
    2644             : /*                        OGR_G_CreateFromFgf()                         */
    2645             : /************************************************************************/
    2646             : 
    2647             : /**
    2648             :  * \brief Create a geometry object of the appropriate type from its FGF
    2649             :  * (FDO Geometry Format) binary representation.
    2650             :  *
    2651             :  * See OGRGeometryFactory::createFromFgf() */
    2652           0 : OGRErr CPL_DLL OGR_G_CreateFromFgf(const void *pabyData,
    2653             :                                    OGRSpatialReferenceH hSRS,
    2654             :                                    OGRGeometryH *phGeometry, int nBytes,
    2655             :                                    int *pnBytesConsumed)
    2656             : 
    2657             : {
    2658           0 :     return OGRGeometryFactory::createFromFgf(
    2659             :         pabyData, OGRSpatialReference::FromHandle(hSRS),
    2660           0 :         reinterpret_cast<OGRGeometry **>(phGeometry), nBytes, pnBytesConsumed);
    2661             : }
    2662             : 
    2663             : /************************************************************************/
    2664             : /*                SplitLineStringAtDateline()                           */
    2665             : /************************************************************************/
    2666             : 
    2667           8 : static void SplitLineStringAtDateline(OGRGeometryCollection *poMulti,
    2668             :                                       const OGRLineString *poLS,
    2669             :                                       double dfDateLineOffset, double dfXOffset)
    2670             : {
    2671           8 :     const double dfLeftBorderX = 180 - dfDateLineOffset;
    2672           8 :     const double dfRightBorderX = -180 + dfDateLineOffset;
    2673           8 :     const double dfDiffSpace = 360 - dfDateLineOffset;
    2674             : 
    2675           8 :     const bool bIs3D = poLS->getCoordinateDimension() == 3;
    2676           8 :     OGRLineString *poNewLS = new OGRLineString();
    2677           8 :     poMulti->addGeometryDirectly(poNewLS);
    2678          35 :     for (int i = 0; i < poLS->getNumPoints(); i++)
    2679             :     {
    2680          27 :         const double dfX = poLS->getX(i) + dfXOffset;
    2681          27 :         if (i > 0 && fabs(dfX - (poLS->getX(i - 1) + dfXOffset)) > dfDiffSpace)
    2682             :         {
    2683           9 :             double dfX1 = poLS->getX(i - 1) + dfXOffset;
    2684           9 :             double dfY1 = poLS->getY(i - 1);
    2685           9 :             double dfZ1 = poLS->getY(i - 1);
    2686           9 :             double dfX2 = poLS->getX(i) + dfXOffset;
    2687           9 :             double dfY2 = poLS->getY(i);
    2688           9 :             double dfZ2 = poLS->getY(i);
    2689             : 
    2690           8 :             if (dfX1 > -180 && dfX1 < dfRightBorderX && dfX2 == 180 &&
    2691           0 :                 i + 1 < poLS->getNumPoints() &&
    2692          17 :                 poLS->getX(i + 1) + dfXOffset > -180 &&
    2693           0 :                 poLS->getX(i + 1) + dfXOffset < dfRightBorderX)
    2694             :             {
    2695           0 :                 if (bIs3D)
    2696           0 :                     poNewLS->addPoint(-180, poLS->getY(i), poLS->getZ(i));
    2697             :                 else
    2698           0 :                     poNewLS->addPoint(-180, poLS->getY(i));
    2699             : 
    2700           0 :                 i++;
    2701             : 
    2702           0 :                 if (bIs3D)
    2703           0 :                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i),
    2704             :                                       poLS->getZ(i));
    2705             :                 else
    2706           0 :                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i));
    2707           0 :                 continue;
    2708             :             }
    2709           4 :             else if (dfX1 > dfLeftBorderX && dfX1 < 180 && dfX2 == -180 &&
    2710           0 :                      i + 1 < poLS->getNumPoints() &&
    2711          13 :                      poLS->getX(i + 1) + dfXOffset > dfLeftBorderX &&
    2712           0 :                      poLS->getX(i + 1) + dfXOffset < 180)
    2713             :             {
    2714           0 :                 if (bIs3D)
    2715           0 :                     poNewLS->addPoint(180, poLS->getY(i), poLS->getZ(i));
    2716             :                 else
    2717           0 :                     poNewLS->addPoint(180, poLS->getY(i));
    2718             : 
    2719           0 :                 i++;
    2720             : 
    2721           0 :                 if (bIs3D)
    2722           0 :                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i),
    2723             :                                       poLS->getZ(i));
    2724             :                 else
    2725           0 :                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i));
    2726           0 :                 continue;
    2727             :             }
    2728             : 
    2729           9 :             if (dfX1 < dfRightBorderX && dfX2 > dfLeftBorderX)
    2730             :             {
    2731           5 :                 std::swap(dfX1, dfX2);
    2732           5 :                 std::swap(dfY1, dfY2);
    2733           5 :                 std::swap(dfZ1, dfZ2);
    2734             :             }
    2735           9 :             if (dfX1 > dfLeftBorderX && dfX2 < dfRightBorderX)
    2736           9 :                 dfX2 += 360;
    2737             : 
    2738           9 :             if (dfX1 <= 180 && dfX2 >= 180 && dfX1 < dfX2)
    2739             :             {
    2740           9 :                 const double dfRatio = (180 - dfX1) / (dfX2 - dfX1);
    2741           9 :                 const double dfY = dfRatio * dfY2 + (1 - dfRatio) * dfY1;
    2742           9 :                 const double dfZ = dfRatio * dfZ2 + (1 - dfRatio) * dfZ1;
    2743             :                 double dfNewX =
    2744           9 :                     poLS->getX(i - 1) + dfXOffset > dfLeftBorderX ? 180 : -180;
    2745          18 :                 if (poNewLS->getNumPoints() == 0 ||
    2746          18 :                     poNewLS->getX(poNewLS->getNumPoints() - 1) != dfNewX ||
    2747           2 :                     poNewLS->getY(poNewLS->getNumPoints() - 1) != dfY)
    2748             :                 {
    2749           7 :                     if (bIs3D)
    2750           0 :                         poNewLS->addPoint(dfNewX, dfY, dfZ);
    2751             :                     else
    2752           7 :                         poNewLS->addPoint(dfNewX, dfY);
    2753             :                 }
    2754           9 :                 poNewLS = new OGRLineString();
    2755           9 :                 if (bIs3D)
    2756           0 :                     poNewLS->addPoint(
    2757           0 :                         poLS->getX(i - 1) + dfXOffset > dfLeftBorderX ? -180
    2758             :                                                                       : 180,
    2759             :                         dfY, dfZ);
    2760             :                 else
    2761           9 :                     poNewLS->addPoint(
    2762           9 :                         poLS->getX(i - 1) + dfXOffset > dfLeftBorderX ? -180
    2763             :                                                                       : 180,
    2764             :                         dfY);
    2765           9 :                 poMulti->addGeometryDirectly(poNewLS);
    2766             :             }
    2767             :             else
    2768             :             {
    2769           0 :                 poNewLS = new OGRLineString();
    2770           0 :                 poMulti->addGeometryDirectly(poNewLS);
    2771             :             }
    2772             :         }
    2773          27 :         if (bIs3D)
    2774           0 :             poNewLS->addPoint(dfX, poLS->getY(i), poLS->getZ(i));
    2775             :         else
    2776          27 :             poNewLS->addPoint(dfX, poLS->getY(i));
    2777             :     }
    2778           8 : }
    2779             : 
    2780             : /************************************************************************/
    2781             : /*               FixPolygonCoordinatesAtDateLine()                      */
    2782             : /************************************************************************/
    2783             : 
    2784             : #ifdef HAVE_GEOS
    2785           5 : static void FixPolygonCoordinatesAtDateLine(OGRPolygon *poPoly,
    2786             :                                             double dfDateLineOffset)
    2787             : {
    2788           5 :     const double dfLeftBorderX = 180 - dfDateLineOffset;
    2789           5 :     const double dfRightBorderX = -180 + dfDateLineOffset;
    2790           5 :     const double dfDiffSpace = 360 - dfDateLineOffset;
    2791             : 
    2792          10 :     for (int iPart = 0; iPart < 1 + poPoly->getNumInteriorRings(); iPart++)
    2793             :     {
    2794           5 :         OGRLineString *poLS = (iPart == 0) ? poPoly->getExteriorRing()
    2795           5 :                                            : poPoly->getInteriorRing(iPart - 1);
    2796           5 :         bool bGoEast = false;
    2797           5 :         const bool bIs3D = poLS->getCoordinateDimension() == 3;
    2798          41 :         for (int i = 1; i < poLS->getNumPoints(); i++)
    2799             :         {
    2800          36 :             double dfX = poLS->getX(i);
    2801          36 :             const double dfPrevX = poLS->getX(i - 1);
    2802          36 :             const double dfDiffLong = fabs(dfX - dfPrevX);
    2803          36 :             if (dfDiffLong > dfDiffSpace)
    2804             :             {
    2805          21 :                 if ((dfPrevX > dfLeftBorderX && dfX < dfRightBorderX) ||
    2806           6 :                     (dfX < 0 && bGoEast))
    2807             :                 {
    2808          18 :                     dfX += 360;
    2809          18 :                     bGoEast = true;
    2810          18 :                     if (bIs3D)
    2811           0 :                         poLS->setPoint(i, dfX, poLS->getY(i), poLS->getZ(i));
    2812             :                     else
    2813          18 :                         poLS->setPoint(i, dfX, poLS->getY(i));
    2814             :                 }
    2815           3 :                 else if (dfPrevX < dfRightBorderX && dfX > dfLeftBorderX)
    2816             :                 {
    2817          10 :                     for (int j = i - 1; j >= 0; j--)
    2818             :                     {
    2819           7 :                         dfX = poLS->getX(j);
    2820           7 :                         if (dfX < 0)
    2821             :                         {
    2822           7 :                             if (bIs3D)
    2823           0 :                                 poLS->setPoint(j, dfX + 360, poLS->getY(j),
    2824             :                                                poLS->getZ(j));
    2825             :                             else
    2826           7 :                                 poLS->setPoint(j, dfX + 360, poLS->getY(j));
    2827             :                         }
    2828             :                     }
    2829           3 :                     bGoEast = false;
    2830             :                 }
    2831             :                 else
    2832             :                 {
    2833           0 :                     bGoEast = false;
    2834             :                 }
    2835             :             }
    2836             :         }
    2837             :     }
    2838           5 : }
    2839             : #endif
    2840             : 
    2841             : /************************************************************************/
    2842             : /*                            AddOffsetToLon()                          */
    2843             : /************************************************************************/
    2844             : 
    2845          17 : static void AddOffsetToLon(OGRGeometry *poGeom, double dfOffset)
    2846             : {
    2847          17 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2848             :     {
    2849           7 :         case wkbPolygon:
    2850             :         {
    2851          14 :             for (auto poSubGeom : *(poGeom->toPolygon()))
    2852             :             {
    2853           7 :                 AddOffsetToLon(poSubGeom, dfOffset);
    2854             :             }
    2855             : 
    2856           7 :             break;
    2857             :         }
    2858             : 
    2859           0 :         case wkbMultiLineString:
    2860             :         case wkbMultiPolygon:
    2861             :         case wkbGeometryCollection:
    2862             :         {
    2863           0 :             for (auto poSubGeom : *(poGeom->toGeometryCollection()))
    2864             :             {
    2865           0 :                 AddOffsetToLon(poSubGeom, dfOffset);
    2866             :             }
    2867             : 
    2868           0 :             break;
    2869             :         }
    2870             : 
    2871          10 :         case wkbLineString:
    2872             :         {
    2873          10 :             OGRLineString *poLineString = poGeom->toLineString();
    2874          10 :             const int nPointCount = poLineString->getNumPoints();
    2875          10 :             const int nCoordDim = poLineString->getCoordinateDimension();
    2876          63 :             for (int iPoint = 0; iPoint < nPointCount; iPoint++)
    2877             :             {
    2878          53 :                 if (nCoordDim == 2)
    2879         106 :                     poLineString->setPoint(
    2880          53 :                         iPoint, poLineString->getX(iPoint) + dfOffset,
    2881             :                         poLineString->getY(iPoint));
    2882             :                 else
    2883           0 :                     poLineString->setPoint(
    2884           0 :                         iPoint, poLineString->getX(iPoint) + dfOffset,
    2885             :                         poLineString->getY(iPoint), poLineString->getZ(iPoint));
    2886             :             }
    2887          10 :             break;
    2888             :         }
    2889             : 
    2890           0 :         default:
    2891           0 :             break;
    2892             :     }
    2893          17 : }
    2894             : 
    2895             : /************************************************************************/
    2896             : /*                        AddSimpleGeomToMulti()                        */
    2897             : /************************************************************************/
    2898             : 
    2899             : #ifdef HAVE_GEOS
    2900          12 : static void AddSimpleGeomToMulti(OGRGeometryCollection *poMulti,
    2901             :                                  const OGRGeometry *poGeom)
    2902             : {
    2903          12 :     switch (wkbFlatten(poGeom->getGeometryType()))
    2904             :     {
    2905          12 :         case wkbPolygon:
    2906             :         case wkbLineString:
    2907          12 :             poMulti->addGeometry(poGeom);
    2908          12 :             break;
    2909             : 
    2910           0 :         case wkbMultiLineString:
    2911             :         case wkbMultiPolygon:
    2912             :         case wkbGeometryCollection:
    2913             :         {
    2914           0 :             for (const auto poSubGeom : *(poGeom->toGeometryCollection()))
    2915             :             {
    2916           0 :                 AddSimpleGeomToMulti(poMulti, poSubGeom);
    2917             :             }
    2918           0 :             break;
    2919             :         }
    2920             : 
    2921           0 :         default:
    2922           0 :             break;
    2923             :     }
    2924          12 : }
    2925             : #endif  // #ifdef HAVE_GEOS
    2926             : 
    2927             : /************************************************************************/
    2928             : /*                       WrapPointDateLine()                            */
    2929             : /************************************************************************/
    2930             : 
    2931          14 : static void WrapPointDateLine(OGRPoint *poPoint)
    2932             : {
    2933          14 :     if (poPoint->getX() > 180)
    2934             :     {
    2935           2 :         poPoint->setX(fmod(poPoint->getX() + 180, 360) - 180);
    2936             :     }
    2937          12 :     else if (poPoint->getX() < -180)
    2938             :     {
    2939           3 :         poPoint->setX(-(fmod(-poPoint->getX() + 180, 360) - 180));
    2940             :     }
    2941          14 : }
    2942             : 
    2943             : /************************************************************************/
    2944             : /*                 CutGeometryOnDateLineAndAddToMulti()                 */
    2945             : /************************************************************************/
    2946             : 
    2947          69 : static void CutGeometryOnDateLineAndAddToMulti(OGRGeometryCollection *poMulti,
    2948             :                                                const OGRGeometry *poGeom,
    2949             :                                                double dfDateLineOffset)
    2950             : {
    2951          69 :     const OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
    2952          69 :     switch (eGeomType)
    2953             :     {
    2954           1 :         case wkbPoint:
    2955             :         {
    2956           1 :             auto poPoint = poGeom->toPoint()->clone();
    2957           1 :             WrapPointDateLine(poPoint);
    2958           1 :             poMulti->addGeometryDirectly(poPoint);
    2959           1 :             break;
    2960             :         }
    2961             : 
    2962          54 :         case wkbPolygon:
    2963             :         case wkbLineString:
    2964             :         {
    2965          54 :             bool bSplitLineStringAtDateline = false;
    2966          54 :             OGREnvelope oEnvelope;
    2967             : 
    2968          54 :             poGeom->getEnvelope(&oEnvelope);
    2969          54 :             const bool bAroundMinus180 = (oEnvelope.MinX < -180.0);
    2970             : 
    2971             :             // Naive heuristics... Place to improve.
    2972             : #ifdef HAVE_GEOS
    2973          54 :             std::unique_ptr<OGRGeometry> poDupGeom;
    2974          54 :             bool bWrapDateline = false;
    2975             : #endif
    2976             : 
    2977          54 :             const double dfLeftBorderX = 180 - dfDateLineOffset;
    2978          54 :             const double dfRightBorderX = -180 + dfDateLineOffset;
    2979          54 :             const double dfDiffSpace = 360 - dfDateLineOffset;
    2980             : 
    2981          54 :             const double dfXOffset = (bAroundMinus180) ? 360.0 : 0.0;
    2982          54 :             if (oEnvelope.MinX < -180 || oEnvelope.MaxX > 180 ||
    2983          52 :                 (oEnvelope.MinX + dfXOffset > dfLeftBorderX &&
    2984          12 :                  oEnvelope.MaxX + dfXOffset > 180))
    2985             :             {
    2986             : #ifndef HAVE_GEOS
    2987             :                 CPLError(CE_Failure, CPLE_NotSupported,
    2988             :                          "GEOS support not enabled.");
    2989             : #else
    2990           2 :                 bWrapDateline = true;
    2991             : #endif
    2992             :             }
    2993             :             else
    2994             :             {
    2995             :                 auto poLS = eGeomType == wkbPolygon
    2996          52 :                                 ? poGeom->toPolygon()->getExteriorRing()
    2997          14 :                                 : poGeom->toLineString();
    2998          52 :                 if (poLS)
    2999             :                 {
    3000          52 :                     double dfMaxSmallDiffLong = 0;
    3001          52 :                     bool bHasBigDiff = false;
    3002             :                     // Detect big gaps in longitude.
    3003         294 :                     for (int i = 1; i < poLS->getNumPoints(); i++)
    3004             :                     {
    3005         242 :                         const double dfPrevX = poLS->getX(i - 1) + dfXOffset;
    3006         242 :                         const double dfX = poLS->getX(i) + dfXOffset;
    3007         242 :                         const double dfDiffLong = fabs(dfX - dfPrevX);
    3008             : 
    3009         242 :                         if (dfDiffLong > dfDiffSpace &&
    3010          11 :                             ((dfX > dfLeftBorderX &&
    3011          10 :                               dfPrevX < dfRightBorderX) ||
    3012          10 :                              (dfPrevX > dfLeftBorderX && dfX < dfRightBorderX)))
    3013          21 :                             bHasBigDiff = true;
    3014         221 :                         else if (dfDiffLong > dfMaxSmallDiffLong)
    3015          55 :                             dfMaxSmallDiffLong = dfDiffLong;
    3016             :                     }
    3017          52 :                     if (bHasBigDiff && dfMaxSmallDiffLong < dfDateLineOffset)
    3018             :                     {
    3019          13 :                         if (eGeomType == wkbLineString)
    3020           8 :                             bSplitLineStringAtDateline = true;
    3021             :                         else
    3022             :                         {
    3023             : #ifndef HAVE_GEOS
    3024             :                             CPLError(CE_Failure, CPLE_NotSupported,
    3025             :                                      "GEOS support not enabled.");
    3026             : #else
    3027           5 :                             poDupGeom.reset(poGeom->clone());
    3028           5 :                             FixPolygonCoordinatesAtDateLine(
    3029             :                                 poDupGeom->toPolygon(), dfDateLineOffset);
    3030             : 
    3031           5 :                             OGREnvelope sEnvelope;
    3032           5 :                             poDupGeom->getEnvelope(&sEnvelope);
    3033           5 :                             bWrapDateline = sEnvelope.MinX != sEnvelope.MaxX;
    3034             : #endif
    3035             :                         }
    3036             :                     }
    3037             :                 }
    3038             :             }
    3039             : 
    3040          54 :             if (bSplitLineStringAtDateline)
    3041             :             {
    3042           8 :                 SplitLineStringAtDateline(poMulti, poGeom->toLineString(),
    3043             :                                           dfDateLineOffset,
    3044             :                                           (bAroundMinus180) ? 360.0 : 0.0);
    3045             :             }
    3046             : #ifdef HAVE_GEOS
    3047          46 :             else if (bWrapDateline)
    3048             :             {
    3049             :                 const OGRGeometry *poWorkGeom =
    3050           6 :                     poDupGeom ? poDupGeom.get() : poGeom;
    3051           6 :                 OGRGeometry *poRectangle1 = nullptr;
    3052           6 :                 OGRGeometry *poRectangle2 = nullptr;
    3053           6 :                 const char *pszWKT1 =
    3054             :                     !bAroundMinus180
    3055           6 :                         ? "POLYGON((-180 90,180 90,180 -90,-180 -90,-180 90))"
    3056             :                         : "POLYGON((180 90,-180 90,-180 -90,180 -90,180 90))";
    3057           6 :                 const char *pszWKT2 =
    3058             :                     !bAroundMinus180
    3059           6 :                         ? "POLYGON((180 90,360 90,360 -90,180 -90,180 90))"
    3060             :                         : "POLYGON((-180 90,-360 90,-360 -90,-180 -90,-180 "
    3061             :                           "90))";
    3062           6 :                 OGRGeometryFactory::createFromWkt(pszWKT1, nullptr,
    3063             :                                                   &poRectangle1);
    3064           6 :                 OGRGeometryFactory::createFromWkt(pszWKT2, nullptr,
    3065             :                                                   &poRectangle2);
    3066             :                 auto poGeom1 = std::unique_ptr<OGRGeometry>(
    3067          12 :                     poWorkGeom->Intersection(poRectangle1));
    3068             :                 auto poGeom2 = std::unique_ptr<OGRGeometry>(
    3069          12 :                     poWorkGeom->Intersection(poRectangle2));
    3070           6 :                 delete poRectangle1;
    3071           6 :                 delete poRectangle2;
    3072             : 
    3073           6 :                 if (poGeom1 != nullptr && poGeom2 != nullptr)
    3074             :                 {
    3075           6 :                     AddSimpleGeomToMulti(poMulti, poGeom1.get());
    3076           6 :                     AddOffsetToLon(poGeom2.get(),
    3077             :                                    !bAroundMinus180 ? -360.0 : 360.0);
    3078           6 :                     AddSimpleGeomToMulti(poMulti, poGeom2.get());
    3079             :                 }
    3080             :                 else
    3081             :                 {
    3082           0 :                     AddSimpleGeomToMulti(poMulti, poGeom);
    3083             :                 }
    3084             :             }
    3085             : #endif
    3086             :             else
    3087             :             {
    3088          40 :                 poMulti->addGeometry(poGeom);
    3089             :             }
    3090          54 :             break;
    3091             :         }
    3092             : 
    3093          14 :         case wkbMultiLineString:
    3094             :         case wkbMultiPolygon:
    3095             :         case wkbGeometryCollection:
    3096             :         {
    3097          45 :             for (const auto poSubGeom : *(poGeom->toGeometryCollection()))
    3098             :             {
    3099          31 :                 CutGeometryOnDateLineAndAddToMulti(poMulti, poSubGeom,
    3100             :                                                    dfDateLineOffset);
    3101             :             }
    3102          14 :             break;
    3103             :         }
    3104             : 
    3105           0 :         default:
    3106           0 :             break;
    3107             :     }
    3108          69 : }
    3109             : 
    3110             : #ifdef HAVE_GEOS
    3111             : 
    3112             : /************************************************************************/
    3113             : /*                             RemovePoint()                            */
    3114             : /************************************************************************/
    3115             : 
    3116           9 : static void RemovePoint(OGRGeometry *poGeom, OGRPoint *poPoint)
    3117             : {
    3118           9 :     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
    3119           9 :     switch (eType)
    3120             :     {
    3121           4 :         case wkbLineString:
    3122             :         {
    3123           4 :             OGRLineString *poLS = poGeom->toLineString();
    3124           4 :             const bool bIs3D = (poLS->getCoordinateDimension() == 3);
    3125           4 :             int j = 0;
    3126          32 :             for (int i = 0; i < poLS->getNumPoints(); i++)
    3127             :             {
    3128          30 :                 if (poLS->getX(i) != poPoint->getX() ||
    3129           2 :                     poLS->getY(i) != poPoint->getY())
    3130             :                 {
    3131          26 :                     if (i > j)
    3132             :                     {
    3133           4 :                         if (bIs3D)
    3134             :                         {
    3135           0 :                             poLS->setPoint(j, poLS->getX(i), poLS->getY(i),
    3136             :                                            poLS->getZ(i));
    3137             :                         }
    3138             :                         else
    3139             :                         {
    3140           4 :                             poLS->setPoint(j, poLS->getX(i), poLS->getY(i));
    3141             :                         }
    3142             :                     }
    3143          26 :                     j++;
    3144             :                 }
    3145             :             }
    3146           4 :             poLS->setNumPoints(j);
    3147           4 :             break;
    3148             :         }
    3149             : 
    3150           4 :         case wkbPolygon:
    3151             :         {
    3152           4 :             OGRPolygon *poPoly = poGeom->toPolygon();
    3153           4 :             if (poPoly->getExteriorRing() != nullptr)
    3154             :             {
    3155           4 :                 RemovePoint(poPoly->getExteriorRing(), poPoint);
    3156           4 :                 for (int i = 0; i < poPoly->getNumInteriorRings(); ++i)
    3157             :                 {
    3158           0 :                     RemovePoint(poPoly->getInteriorRing(i), poPoint);
    3159             :                 }
    3160             :             }
    3161           4 :             break;
    3162             :         }
    3163             : 
    3164           1 :         case wkbMultiLineString:
    3165             :         case wkbMultiPolygon:
    3166             :         case wkbGeometryCollection:
    3167             :         {
    3168           1 :             OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    3169           3 :             for (int i = 0; i < poGC->getNumGeometries(); ++i)
    3170             :             {
    3171           2 :                 RemovePoint(poGC->getGeometryRef(i), poPoint);
    3172             :             }
    3173           1 :             break;
    3174             :         }
    3175             : 
    3176           0 :         default:
    3177           0 :             break;
    3178             :     }
    3179           9 : }
    3180             : 
    3181             : /************************************************************************/
    3182             : /*                              GetDist()                               */
    3183             : /************************************************************************/
    3184             : 
    3185       30172 : static double GetDist(double dfDeltaX, double dfDeltaY)
    3186             : {
    3187       30172 :     return sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
    3188             : }
    3189             : 
    3190             : /************************************************************************/
    3191             : /*                             AlterPole()                              */
    3192             : /*                                                                      */
    3193             : /* Replace and point at the pole by points really close to the pole,    */
    3194             : /* but on the previous and later segments.                              */
    3195             : /************************************************************************/
    3196             : 
    3197           5 : static void AlterPole(OGRGeometry *poGeom, OGRPoint *poPole,
    3198             :                       bool bIsRing = false)
    3199             : {
    3200           5 :     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
    3201           5 :     switch (eType)
    3202             :     {
    3203           2 :         case wkbLineString:
    3204             :         {
    3205           2 :             if (!bIsRing)
    3206           0 :                 return;
    3207           2 :             OGRLineString *poLS = poGeom->toLineString();
    3208           2 :             const int nNumPoints = poLS->getNumPoints();
    3209           2 :             if (nNumPoints >= 4)
    3210             :             {
    3211           2 :                 const bool bIs3D = (poLS->getCoordinateDimension() == 3);
    3212           4 :                 std::vector<OGRRawPoint> aoPoints;
    3213           4 :                 std::vector<double> adfZ;
    3214           2 :                 bool bMustClose = false;
    3215          10 :                 for (int i = 0; i < nNumPoints; i++)
    3216             :                 {
    3217           8 :                     const double dfX = poLS->getX(i);
    3218           8 :                     const double dfY = poLS->getY(i);
    3219           8 :                     if (dfX == poPole->getX() && dfY == poPole->getY())
    3220             :                     {
    3221             :                         // Replace the pole by points really close to it
    3222           2 :                         if (i == 0)
    3223           0 :                             bMustClose = true;
    3224           2 :                         if (i == nNumPoints - 1)
    3225           0 :                             continue;
    3226           2 :                         const int iBefore = i > 0 ? i - 1 : nNumPoints - 2;
    3227           2 :                         double dfXBefore = poLS->getX(iBefore);
    3228           2 :                         double dfYBefore = poLS->getY(iBefore);
    3229             :                         double dfNorm =
    3230           2 :                             GetDist(dfXBefore - dfX, dfYBefore - dfY);
    3231           2 :                         double dfXInterp =
    3232           2 :                             dfX + (dfXBefore - dfX) / dfNorm * 1.0e-7;
    3233           2 :                         double dfYInterp =
    3234           2 :                             dfY + (dfYBefore - dfY) / dfNorm * 1.0e-7;
    3235           2 :                         OGRRawPoint oPoint;
    3236           2 :                         oPoint.x = dfXInterp;
    3237           2 :                         oPoint.y = dfYInterp;
    3238           2 :                         aoPoints.push_back(oPoint);
    3239           2 :                         adfZ.push_back(poLS->getZ(i));
    3240             : 
    3241           2 :                         const int iAfter = i + 1;
    3242           2 :                         double dfXAfter = poLS->getX(iAfter);
    3243           2 :                         double dfYAfter = poLS->getY(iAfter);
    3244           2 :                         dfNorm = GetDist(dfXAfter - dfX, dfYAfter - dfY);
    3245           2 :                         dfXInterp = dfX + (dfXAfter - dfX) / dfNorm * 1e-7;
    3246           2 :                         dfYInterp = dfY + (dfYAfter - dfY) / dfNorm * 1e-7;
    3247           2 :                         oPoint.x = dfXInterp;
    3248           2 :                         oPoint.y = dfYInterp;
    3249           2 :                         aoPoints.push_back(oPoint);
    3250           2 :                         adfZ.push_back(poLS->getZ(i));
    3251             :                     }
    3252             :                     else
    3253             :                     {
    3254           6 :                         OGRRawPoint oPoint;
    3255           6 :                         oPoint.x = dfX;
    3256           6 :                         oPoint.y = dfY;
    3257           6 :                         aoPoints.push_back(oPoint);
    3258           6 :                         adfZ.push_back(poLS->getZ(i));
    3259             :                     }
    3260             :                 }
    3261           2 :                 if (bMustClose)
    3262             :                 {
    3263           0 :                     aoPoints.push_back(aoPoints[0]);
    3264           0 :                     adfZ.push_back(adfZ[0]);
    3265             :                 }
    3266             : 
    3267           4 :                 poLS->setPoints(static_cast<int>(aoPoints.size()),
    3268           2 :                                 &(aoPoints[0]), bIs3D ? &adfZ[0] : nullptr);
    3269             :             }
    3270           2 :             break;
    3271             :         }
    3272             : 
    3273           2 :         case wkbPolygon:
    3274             :         {
    3275           2 :             OGRPolygon *poPoly = poGeom->toPolygon();
    3276           2 :             if (poPoly->getExteriorRing() != nullptr)
    3277             :             {
    3278           2 :                 AlterPole(poPoly->getExteriorRing(), poPole, true);
    3279           2 :                 for (int i = 0; i < poPoly->getNumInteriorRings(); ++i)
    3280             :                 {
    3281           0 :                     AlterPole(poPoly->getInteriorRing(i), poPole, true);
    3282             :                 }
    3283             :             }
    3284           2 :             break;
    3285             :         }
    3286             : 
    3287           1 :         case wkbMultiLineString:
    3288             :         case wkbMultiPolygon:
    3289             :         case wkbGeometryCollection:
    3290             :         {
    3291           1 :             OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    3292           2 :             for (int i = 0; i < poGC->getNumGeometries(); ++i)
    3293             :             {
    3294           1 :                 AlterPole(poGC->getGeometryRef(i), poPole);
    3295             :             }
    3296           1 :             break;
    3297             :         }
    3298             : 
    3299           0 :         default:
    3300           0 :             break;
    3301             :     }
    3302             : }
    3303             : 
    3304             : /************************************************************************/
    3305             : /*                          IsPolarToWGS84()                            */
    3306             : /*                                                                      */
    3307             : /* Returns true if poCT transforms from a projection that includes one  */
    3308             : /* of the pole in a continuous way.                                     */
    3309             : /************************************************************************/
    3310             : 
    3311          30 : static bool IsPolarToWGS84(OGRCoordinateTransformation *poCT,
    3312             :                            OGRCoordinateTransformation *poRevCT,
    3313             :                            bool &bIsNorthPolarOut)
    3314             : {
    3315          30 :     bool bIsNorthPolar = false;
    3316          30 :     bool bIsSouthPolar = false;
    3317          30 :     double x = 0.0;
    3318          30 :     double y = 90.0;
    3319             : 
    3320          30 :     const bool bBackupEmitErrors = poCT->GetEmitErrors();
    3321          30 :     poRevCT->SetEmitErrors(false);
    3322          30 :     poCT->SetEmitErrors(false);
    3323             : 
    3324          30 :     if (poRevCT->Transform(1, &x, &y) &&
    3325             :         // Surprisingly, pole south projects correctly back &
    3326             :         // forth for antarctic polar stereographic.  Therefore, check that
    3327             :         // the projected value is not too big.
    3328          30 :         fabs(x) < 1e10 && fabs(y) < 1e10)
    3329             :     {
    3330          27 :         double x_tab[] = {x, x - 1e5, x + 1e5};
    3331          27 :         double y_tab[] = {y, y - 1e5, y + 1e5};
    3332          27 :         if (poCT->Transform(3, x_tab, y_tab) &&
    3333          27 :             fabs(y_tab[0] - (90.0)) < 1e-10 &&
    3334          80 :             fabs(x_tab[2] - x_tab[1]) > 170 &&
    3335          26 :             fabs(y_tab[2] - y_tab[1]) < 1e-10)
    3336             :         {
    3337          26 :             bIsNorthPolar = true;
    3338             :         }
    3339             :     }
    3340             : 
    3341          30 :     x = 0.0;
    3342          30 :     y = -90.0;
    3343          30 :     if (poRevCT->Transform(1, &x, &y) && fabs(x) < 1e10 && fabs(y) < 1e10)
    3344             :     {
    3345          24 :         double x_tab[] = {x, x - 1e5, x + 1e5};
    3346          24 :         double y_tab[] = {y, y - 1e5, y + 1e5};
    3347          24 :         if (poCT->Transform(3, x_tab, y_tab) &&
    3348          24 :             fabs(y_tab[0] - (-90.0)) < 1e-10 &&
    3349          71 :             fabs(x_tab[2] - x_tab[1]) > 170 &&
    3350          23 :             fabs(y_tab[2] - y_tab[1]) < 1e-10)
    3351             :         {
    3352          23 :             bIsSouthPolar = true;
    3353             :         }
    3354             :     }
    3355             : 
    3356          30 :     poCT->SetEmitErrors(bBackupEmitErrors);
    3357             : 
    3358          30 :     if (bIsNorthPolar && bIsSouthPolar)
    3359             :     {
    3360          22 :         bIsNorthPolar = false;
    3361          22 :         bIsSouthPolar = false;
    3362             :     }
    3363             : 
    3364          30 :     bIsNorthPolarOut = bIsNorthPolar;
    3365          30 :     return bIsNorthPolar || bIsSouthPolar;
    3366             : }
    3367             : 
    3368             : /************************************************************************/
    3369             : /*                     TransformBeforePolarToWGS84()                    */
    3370             : /*                                                                      */
    3371             : /* Transform the geometry (by intersection), so as to cut each geometry */
    3372             : /* that crosses the pole, in 2 parts. Do also tricks for geometries     */
    3373             : /* that just touch the pole.                                            */
    3374             : /************************************************************************/
    3375             : 
    3376             : static OGRGeometry *
    3377           6 : TransformBeforePolarToWGS84(OGRCoordinateTransformation *poRevCT,
    3378             :                             bool bIsNorthPolar, OGRGeometry *poDstGeom,
    3379             :                             bool &bNeedPostCorrectionOut)
    3380             : {
    3381           6 :     const int nSign = (bIsNorthPolar) ? 1 : -1;
    3382             : 
    3383             :     // Does the geometry fully contains the pole ? */
    3384           6 :     double dfXPole = 0.0;
    3385           6 :     double dfYPole = nSign * 90.0;
    3386           6 :     poRevCT->Transform(1, &dfXPole, &dfYPole);
    3387          12 :     OGRPoint oPole(dfXPole, dfYPole);
    3388           6 :     const bool bContainsPole = CPL_TO_BOOL(poDstGeom->Contains(&oPole));
    3389             : 
    3390           6 :     const double EPS = 1e-9;
    3391             : 
    3392             :     // Does the geometry touches the pole and intersects the antimeridian ?
    3393           6 :     double dfNearPoleAntiMeridianX = 180.0;
    3394           6 :     double dfNearPoleAntiMeridianY = nSign * (90.0 - EPS);
    3395           6 :     poRevCT->Transform(1, &dfNearPoleAntiMeridianX, &dfNearPoleAntiMeridianY);
    3396             :     OGRPoint oNearPoleAntimeridian(dfNearPoleAntiMeridianX,
    3397          12 :                                    dfNearPoleAntiMeridianY);
    3398             :     const bool bContainsNearPoleAntimeridian =
    3399           6 :         CPL_TO_BOOL(poDstGeom->Contains(&oNearPoleAntimeridian));
    3400             : 
    3401             :     // Does the geometry touches the pole (but not intersect the antimeridian) ?
    3402          10 :     const bool bRegularTouchesPole = !bContainsPole &&
    3403           9 :                                      !bContainsNearPoleAntimeridian &&
    3404           3 :                                      CPL_TO_BOOL(poDstGeom->Touches(&oPole));
    3405             : 
    3406             :     // Create a polygon of nearly a full hemisphere, but excluding the anti
    3407             :     // meridian and the pole.
    3408           6 :     OGRPolygon oCutter;
    3409           6 :     OGRLinearRing *poRing = new OGRLinearRing();
    3410           6 :     poRing->addPoint(180.0 - EPS, 0);
    3411           6 :     poRing->addPoint(180.0 - EPS, nSign * (90.0 - EPS));
    3412             :     // If the geometry doesn't contain the pole, then we add it to the cutter
    3413             :     // geometry, but will later remove it completely (geometry touching the
    3414             :     // pole but intersecting the antimeridian), or will replace it by 2
    3415             :     // close points (geometry touching the pole without intersecting the
    3416             :     // antimeridian)
    3417           6 :     if (!bContainsPole)
    3418           4 :         poRing->addPoint(180.0, nSign * 90);
    3419           6 :     poRing->addPoint(-180.0 + EPS, nSign * (90.0 - EPS));
    3420           6 :     poRing->addPoint(-180.0 + EPS, 0);
    3421           6 :     poRing->addPoint(180.0 - EPS, 0);
    3422           6 :     oCutter.addRingDirectly(poRing);
    3423             : 
    3424           6 :     if (oCutter.transform(poRevCT) == OGRERR_NONE &&
    3425             :         // Check that longitudes +/- 180 are continuous
    3426             :         // in the polar projection
    3427          10 :         fabs(poRing->getX(0) - poRing->getX(poRing->getNumPoints() - 2)) < 1 &&
    3428           4 :         (bContainsPole || bContainsNearPoleAntimeridian || bRegularTouchesPole))
    3429             :     {
    3430           5 :         if (bContainsPole || bContainsNearPoleAntimeridian)
    3431             :         {
    3432           3 :             OGRGeometry *poNewGeom = poDstGeom->Difference(&oCutter);
    3433           3 :             if (poNewGeom)
    3434             :             {
    3435           3 :                 if (bContainsNearPoleAntimeridian)
    3436           3 :                     RemovePoint(poNewGeom, &oPole);
    3437           3 :                 delete poDstGeom;
    3438           3 :                 poDstGeom = poNewGeom;
    3439             :             }
    3440             :         }
    3441             : 
    3442           5 :         if (bRegularTouchesPole)
    3443             :         {
    3444           2 :             AlterPole(poDstGeom, &oPole);
    3445             :         }
    3446             : 
    3447           5 :         bNeedPostCorrectionOut = true;
    3448             :     }
    3449          12 :     return poDstGeom;
    3450             : }
    3451             : 
    3452             : /************************************************************************/
    3453             : /*                        IsAntimeridianProjToWGS84()                   */
    3454             : /*                                                                      */
    3455             : /* Returns true if poCT transforms from a projection that includes the  */
    3456             : /* antimeridian in a continuous way.                                    */
    3457             : /************************************************************************/
    3458             : 
    3459       10058 : static bool IsAntimeridianProjToWGS84(OGRCoordinateTransformation *poCT,
    3460             :                                       OGRCoordinateTransformation *poRevCT,
    3461             :                                       OGRGeometry *poDstGeometry)
    3462             : {
    3463       10058 :     const bool bBackupEmitErrors = poCT->GetEmitErrors();
    3464       10058 :     poRevCT->SetEmitErrors(false);
    3465       10058 :     poCT->SetEmitErrors(false);
    3466             : 
    3467             :     // Find a reasonable latitude for the geometry
    3468       10058 :     OGREnvelope sEnvelope;
    3469       10058 :     poDstGeometry->getEnvelope(&sEnvelope);
    3470       20116 :     OGRPoint pMean(sEnvelope.MinX, (sEnvelope.MinY + sEnvelope.MaxY) / 2);
    3471       10058 :     if (pMean.transform(poCT) != OGRERR_NONE)
    3472             :     {
    3473           0 :         poCT->SetEmitErrors(bBackupEmitErrors);
    3474           0 :         return false;
    3475             :     }
    3476       10058 :     const double dfMeanLat = pMean.getY();
    3477             : 
    3478             :     // Check that close points on each side of the antimeridian in (long, lat)
    3479             :     // project to close points in the source projection, and check that they
    3480             :     // roundtrip correctly.
    3481       10058 :     const double EPS = 1.0e-8;
    3482       10058 :     double x1 = 180 - EPS;
    3483       10058 :     double y1 = dfMeanLat;
    3484       10058 :     double x2 = -180 + EPS;
    3485       10058 :     double y2 = dfMeanLat;
    3486       30174 :     if (!poRevCT->Transform(1, &x1, &y1) || !poRevCT->Transform(1, &x2, &y2) ||
    3487       20113 :         GetDist(x2 - x1, y2 - y1) > 1 || !poCT->Transform(1, &x1, &y1) ||
    3488       20110 :         !poCT->Transform(1, &x2, &y2) ||
    3489       30171 :         GetDist(x1 - (180 - EPS), y1 - dfMeanLat) > 2 * EPS ||
    3490       10055 :         GetDist(x2 - (-180 + EPS), y2 - dfMeanLat) > 2 * EPS)
    3491             :     {
    3492           3 :         poCT->SetEmitErrors(bBackupEmitErrors);
    3493           3 :         return false;
    3494             :     }
    3495             : 
    3496       10055 :     poCT->SetEmitErrors(bBackupEmitErrors);
    3497             : 
    3498       10055 :     return true;
    3499             : }
    3500             : 
    3501             : /************************************************************************/
    3502             : /*                      CollectPointsOnAntimeridian()                   */
    3503             : /*                                                                      */
    3504             : /* Collect points that are the intersection of the lines of the geometry*/
    3505             : /* with the antimeridian.                                               */
    3506             : /************************************************************************/
    3507             : 
    3508          19 : static void CollectPointsOnAntimeridian(OGRGeometry *poGeom,
    3509             :                                         OGRCoordinateTransformation *poCT,
    3510             :                                         OGRCoordinateTransformation *poRevCT,
    3511             :                                         std::vector<OGRRawPoint> &aoPoints)
    3512             : {
    3513          19 :     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
    3514          19 :     switch (eType)
    3515             :     {
    3516           9 :         case wkbLineString:
    3517             :         {
    3518           9 :             OGRLineString *poLS = poGeom->toLineString();
    3519           9 :             const int nNumPoints = poLS->getNumPoints();
    3520          40 :             for (int i = 0; i < nNumPoints - 1; i++)
    3521             :             {
    3522          31 :                 const double dfX = poLS->getX(i);
    3523          31 :                 const double dfY = poLS->getY(i);
    3524          31 :                 const double dfX2 = poLS->getX(i + 1);
    3525          31 :                 const double dfY2 = poLS->getY(i + 1);
    3526          31 :                 double dfXTrans = dfX;
    3527          31 :                 double dfYTrans = dfY;
    3528          31 :                 double dfX2Trans = dfX2;
    3529          31 :                 double dfY2Trans = dfY2;
    3530          31 :                 poCT->Transform(1, &dfXTrans, &dfYTrans);
    3531          31 :                 poCT->Transform(1, &dfX2Trans, &dfY2Trans);
    3532             :                 // Are we crossing the antimeridian ? (detecting by inversion of
    3533             :                 // sign of X)
    3534          31 :                 if ((dfX2 - dfX) * (dfX2Trans - dfXTrans) < 0 ||
    3535          14 :                     (dfX == dfX2 && dfX2Trans * dfXTrans < 0 &&
    3536           1 :                      fabs(fabs(dfXTrans) - 180) < 10 &&
    3537           1 :                      fabs(fabs(dfX2Trans) - 180) < 10))
    3538             :                 {
    3539          15 :                     double dfXStart = dfX;
    3540          15 :                     double dfYStart = dfY;
    3541          15 :                     double dfXEnd = dfX2;
    3542          15 :                     double dfYEnd = dfY2;
    3543          15 :                     double dfXStartTrans = dfXTrans;
    3544          15 :                     double dfXEndTrans = dfX2Trans;
    3545          15 :                     int iIter = 0;
    3546          15 :                     const double EPS = 1e-8;
    3547             :                     // Find point of the segment intersecting the antimeridian
    3548             :                     // by dichotomy
    3549         397 :                     for (;
    3550         412 :                          iIter < 50 && (fabs(fabs(dfXStartTrans) - 180) > EPS ||
    3551          23 :                                         fabs(fabs(dfXEndTrans) - 180) > EPS);
    3552             :                          ++iIter)
    3553             :                     {
    3554         397 :                         double dfXMid = (dfXStart + dfXEnd) / 2;
    3555         397 :                         double dfYMid = (dfYStart + dfYEnd) / 2;
    3556         397 :                         double dfXMidTrans = dfXMid;
    3557         397 :                         double dfYMidTrans = dfYMid;
    3558         397 :                         poCT->Transform(1, &dfXMidTrans, &dfYMidTrans);
    3559         397 :                         if ((dfXMid - dfXStart) *
    3560         397 :                                     (dfXMidTrans - dfXStartTrans) <
    3561         203 :                                 0 ||
    3562          22 :                             (dfXMid == dfXStart &&
    3563          22 :                              dfXMidTrans * dfXStartTrans < 0))
    3564             :                         {
    3565         202 :                             dfXEnd = dfXMid;
    3566         202 :                             dfYEnd = dfYMid;
    3567         202 :                             dfXEndTrans = dfXMidTrans;
    3568             :                         }
    3569             :                         else
    3570             :                         {
    3571         195 :                             dfXStart = dfXMid;
    3572         195 :                             dfYStart = dfYMid;
    3573         195 :                             dfXStartTrans = dfXMidTrans;
    3574             :                         }
    3575             :                     }
    3576          15 :                     if (iIter < 50)
    3577             :                     {
    3578          15 :                         OGRRawPoint oPoint;
    3579          15 :                         oPoint.x = (dfXStart + dfXEnd) / 2;
    3580          15 :                         oPoint.y = (dfYStart + dfYEnd) / 2;
    3581          15 :                         poCT->Transform(1, &(oPoint.x), &(oPoint.y));
    3582          15 :                         oPoint.x = 180.0;
    3583          15 :                         aoPoints.push_back(oPoint);
    3584             :                     }
    3585             :                 }
    3586             :             }
    3587           9 :             break;
    3588             :         }
    3589             : 
    3590           6 :         case wkbPolygon:
    3591             :         {
    3592           6 :             OGRPolygon *poPoly = poGeom->toPolygon();
    3593           6 :             if (poPoly->getExteriorRing() != nullptr)
    3594             :             {
    3595           6 :                 CollectPointsOnAntimeridian(poPoly->getExteriorRing(), poCT,
    3596             :                                             poRevCT, aoPoints);
    3597           6 :                 for (int i = 0; i < poPoly->getNumInteriorRings(); ++i)
    3598             :                 {
    3599           0 :                     CollectPointsOnAntimeridian(poPoly->getInteriorRing(i),
    3600             :                                                 poCT, poRevCT, aoPoints);
    3601             :                 }
    3602             :             }
    3603           6 :             break;
    3604             :         }
    3605             : 
    3606           4 :         case wkbMultiLineString:
    3607             :         case wkbMultiPolygon:
    3608             :         case wkbGeometryCollection:
    3609             :         {
    3610           4 :             OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    3611           8 :             for (int i = 0; i < poGC->getNumGeometries(); ++i)
    3612             :             {
    3613           4 :                 CollectPointsOnAntimeridian(poGC->getGeometryRef(i), poCT,
    3614             :                                             poRevCT, aoPoints);
    3615             :             }
    3616           4 :             break;
    3617             :         }
    3618             : 
    3619           0 :         default:
    3620           0 :             break;
    3621             :     }
    3622          19 : }
    3623             : 
    3624             : /************************************************************************/
    3625             : /*                         SortPointsByAscendingY()                     */
    3626             : /************************************************************************/
    3627             : 
    3628             : struct SortPointsByAscendingY
    3629             : {
    3630           8 :     bool operator()(const OGRRawPoint &a, const OGRRawPoint &b)
    3631             :     {
    3632           8 :         return a.y < b.y;
    3633             :     }
    3634             : };
    3635             : 
    3636             : /************************************************************************/
    3637             : /*                  TransformBeforeAntimeridianToWGS84()                */
    3638             : /*                                                                      */
    3639             : /* Transform the geometry (by intersection), so as to cut each geometry */
    3640             : /* that crosses the antimeridian, in 2 parts.                           */
    3641             : /************************************************************************/
    3642             : 
    3643       10055 : static OGRGeometry *TransformBeforeAntimeridianToWGS84(
    3644             :     OGRCoordinateTransformation *poCT, OGRCoordinateTransformation *poRevCT,
    3645             :     OGRGeometry *poDstGeom, bool &bNeedPostCorrectionOut)
    3646             : {
    3647       10055 :     OGREnvelope sEnvelope;
    3648       10055 :     poDstGeom->getEnvelope(&sEnvelope);
    3649       20110 :     OGRPoint pMean(sEnvelope.MinX, (sEnvelope.MinY + sEnvelope.MaxY) / 2);
    3650       10055 :     pMean.transform(poCT);
    3651       10055 :     const double dfMeanLat = pMean.getY();
    3652       10055 :     pMean.setX(180.0);
    3653       10055 :     pMean.setY(dfMeanLat);
    3654       10055 :     pMean.transform(poRevCT);
    3655             :     // Check if the antimeridian crosses the bbox of our geometry
    3656       20078 :     if (!(pMean.getX() >= sEnvelope.MinX && pMean.getY() >= sEnvelope.MinY &&
    3657       10023 :           pMean.getX() <= sEnvelope.MaxX && pMean.getY() <= sEnvelope.MaxY))
    3658             :     {
    3659       10046 :         return poDstGeom;
    3660             :     }
    3661             : 
    3662             :     // Collect points that are the intersection of the lines of the geometry
    3663             :     // with the antimeridian
    3664          18 :     std::vector<OGRRawPoint> aoPoints;
    3665           9 :     CollectPointsOnAntimeridian(poDstGeom, poCT, poRevCT, aoPoints);
    3666           9 :     if (aoPoints.empty())
    3667           0 :         return poDstGeom;
    3668             : 
    3669             :     SortPointsByAscendingY sortFunc;
    3670           9 :     std::sort(aoPoints.begin(), aoPoints.end(), sortFunc);
    3671             : 
    3672           9 :     const double EPS = 1e-9;
    3673             : 
    3674             :     // Build a very thin polygon cutting the antimeridian at our points
    3675           9 :     OGRLinearRing *poLR = new OGRLinearRing;
    3676             :     {
    3677           9 :         double x = 180.0 - EPS;
    3678           9 :         double y = aoPoints[0].y - EPS;
    3679           9 :         poRevCT->Transform(1, &x, &y);
    3680           9 :         poLR->addPoint(x, y);
    3681             :     }
    3682          24 :     for (const auto &oPoint : aoPoints)
    3683             :     {
    3684          15 :         double x = 180.0 - EPS;
    3685          15 :         double y = oPoint.y;
    3686          15 :         poRevCT->Transform(1, &x, &y);
    3687          15 :         poLR->addPoint(x, y);
    3688             :     }
    3689             :     {
    3690           9 :         double x = 180.0 - EPS;
    3691           9 :         double y = aoPoints.back().y + EPS;
    3692           9 :         poRevCT->Transform(1, &x, &y);
    3693           9 :         poLR->addPoint(x, y);
    3694             :     }
    3695             :     {
    3696           9 :         double x = 180.0 + EPS;
    3697           9 :         double y = aoPoints.back().y + EPS;
    3698           9 :         poRevCT->Transform(1, &x, &y);
    3699           9 :         poLR->addPoint(x, y);
    3700             :     }
    3701          24 :     for (size_t i = aoPoints.size(); i > 0;)
    3702             :     {
    3703          15 :         --i;
    3704          15 :         const OGRRawPoint &oPoint = aoPoints[i];
    3705          15 :         double x = 180.0 + EPS;
    3706          15 :         double y = oPoint.y;
    3707          15 :         poRevCT->Transform(1, &x, &y);
    3708          15 :         poLR->addPoint(x, y);
    3709             :     }
    3710             :     {
    3711           9 :         double x = 180.0 + EPS;
    3712           9 :         double y = aoPoints[0].y - EPS;
    3713           9 :         poRevCT->Transform(1, &x, &y);
    3714           9 :         poLR->addPoint(x, y);
    3715             :     }
    3716           9 :     poLR->closeRings();
    3717             : 
    3718           9 :     OGRPolygon oPolyToCut;
    3719           9 :     oPolyToCut.addRingDirectly(poLR);
    3720             : 
    3721             : #if DEBUG_VERBOSE
    3722             :     char *pszWKT = NULL;
    3723             :     oPolyToCut.exportToWkt(&pszWKT);
    3724             :     CPLDebug("OGR", "Geometry to cut: %s", pszWKT);
    3725             :     CPLFree(pszWKT);
    3726             : #endif
    3727             : 
    3728             :     // Get the geometry without the antimeridian
    3729           9 :     OGRGeometry *poInter = poDstGeom->Difference(&oPolyToCut);
    3730           9 :     if (poInter != nullptr)
    3731             :     {
    3732           9 :         delete poDstGeom;
    3733           9 :         poDstGeom = poInter;
    3734           9 :         bNeedPostCorrectionOut = true;
    3735             :     }
    3736             : 
    3737           9 :     return poDstGeom;
    3738             : }
    3739             : 
    3740             : /************************************************************************/
    3741             : /*                 SnapCoordsCloseToLatLongBounds()                     */
    3742             : /*                                                                      */
    3743             : /* This function snaps points really close to the antimerdian or poles  */
    3744             : /* to their exact longitudes/latitudes.                                 */
    3745             : /************************************************************************/
    3746             : 
    3747          53 : static void SnapCoordsCloseToLatLongBounds(OGRGeometry *poGeom)
    3748             : {
    3749          53 :     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
    3750          53 :     switch (eType)
    3751             :     {
    3752          24 :         case wkbLineString:
    3753             :         {
    3754          24 :             OGRLineString *poLS = poGeom->toLineString();
    3755          24 :             const double EPS = 1e-8;
    3756         153 :             for (int i = 0; i < poLS->getNumPoints(); i++)
    3757             :             {
    3758         258 :                 OGRPoint p;
    3759         129 :                 poLS->getPoint(i, &p);
    3760         129 :                 if (fabs(p.getX() - 180.0) < EPS)
    3761             :                 {
    3762          34 :                     p.setX(180.0);
    3763          34 :                     poLS->setPoint(i, &p);
    3764             :                 }
    3765          95 :                 else if (fabs(p.getX() - -180.0) < EPS)
    3766             :                 {
    3767          29 :                     p.setX(-180.0);
    3768          29 :                     poLS->setPoint(i, &p);
    3769             :                 }
    3770             : 
    3771         129 :                 if (fabs(p.getY() - 90.0) < EPS)
    3772             :                 {
    3773           8 :                     p.setY(90.0);
    3774           8 :                     poLS->setPoint(i, &p);
    3775             :                 }
    3776         121 :                 else if (fabs(p.getY() - -90.0) < EPS)
    3777             :                 {
    3778           2 :                     p.setY(-90.0);
    3779           2 :                     poLS->setPoint(i, &p);
    3780             :                 }
    3781             :             }
    3782          24 :             break;
    3783             :         }
    3784             : 
    3785          18 :         case wkbPolygon:
    3786             :         {
    3787          18 :             OGRPolygon *poPoly = poGeom->toPolygon();
    3788          18 :             if (poPoly->getExteriorRing() != nullptr)
    3789             :             {
    3790          18 :                 SnapCoordsCloseToLatLongBounds(poPoly->getExteriorRing());
    3791          18 :                 for (int i = 0; i < poPoly->getNumInteriorRings(); ++i)
    3792             :                 {
    3793           0 :                     SnapCoordsCloseToLatLongBounds(poPoly->getInteriorRing(i));
    3794             :                 }
    3795             :             }
    3796          18 :             break;
    3797             :         }
    3798             : 
    3799          11 :         case wkbMultiLineString:
    3800             :         case wkbMultiPolygon:
    3801             :         case wkbGeometryCollection:
    3802             :         {
    3803          11 :             OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    3804          32 :             for (int i = 0; i < poGC->getNumGeometries(); ++i)
    3805             :             {
    3806          21 :                 SnapCoordsCloseToLatLongBounds(poGC->getGeometryRef(i));
    3807             :             }
    3808          11 :             break;
    3809             :         }
    3810             : 
    3811           0 :         default:
    3812           0 :             break;
    3813             :     }
    3814          53 : }
    3815             : 
    3816             : #endif
    3817             : 
    3818             : /************************************************************************/
    3819             : /*                  TransformWithOptionsCache::Private                  */
    3820             : /************************************************************************/
    3821             : 
    3822             : struct OGRGeometryFactory::TransformWithOptionsCache::Private
    3823             : {
    3824             :     std::unique_ptr<OGRCoordinateTransformation> poRevCT{};
    3825             :     bool bIsPolar = false;
    3826             :     bool bIsNorthPolar = false;
    3827             : };
    3828             : 
    3829             : /************************************************************************/
    3830             : /*                     TransformWithOptionsCache()                      */
    3831             : /************************************************************************/
    3832             : 
    3833         916 : OGRGeometryFactory::TransformWithOptionsCache::TransformWithOptionsCache()
    3834         916 :     : d(new Private())
    3835             : {
    3836         916 : }
    3837             : 
    3838             : /************************************************************************/
    3839             : /*                     ~TransformWithOptionsCache()                      */
    3840             : /************************************************************************/
    3841             : 
    3842         916 : OGRGeometryFactory::TransformWithOptionsCache::~TransformWithOptionsCache()
    3843             : {
    3844         916 : }
    3845             : 
    3846             : /************************************************************************/
    3847             : /*                       transformWithOptions()                         */
    3848             : /************************************************************************/
    3849             : 
    3850             : /** Transform a geometry.
    3851             :  * @param poSrcGeom source geometry
    3852             :  * @param poCT coordinate transformation object, or NULL.
    3853             :  * @param papszOptions options. Including WRAPDATELINE=YES and DATELINEOFFSET=.
    3854             :  * @param cache Cache. May increase performance if persisted between invocations
    3855             :  * @return (new) transformed geometry.
    3856             :  */
    3857       10167 : OGRGeometry *OGRGeometryFactory::transformWithOptions(
    3858             :     const OGRGeometry *poSrcGeom, OGRCoordinateTransformation *poCT,
    3859             :     char **papszOptions, CPL_UNUSED const TransformWithOptionsCache &cache)
    3860             : {
    3861       10167 :     OGRGeometry *poDstGeom = poSrcGeom->clone();
    3862       10167 :     if (poCT != nullptr)
    3863             :     {
    3864             : #ifdef HAVE_GEOS
    3865       10132 :         bool bNeedPostCorrection = false;
    3866             : 
    3867       10132 :         auto poSourceCRS = poCT->GetSourceCS();
    3868       10132 :         auto poTargetCRS = poCT->GetTargetCS();
    3869       10126 :         if (poSourceCRS != nullptr && poTargetCRS != nullptr &&
    3870       20258 :             poSourceCRS->IsProjected() && poTargetCRS->IsGeographic())
    3871             :         {
    3872       20132 :             OGRSpatialReference oSRSWGS84;
    3873       10066 :             oSRSWGS84.SetWellKnownGeogCS("WGS84");
    3874       10066 :             oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3875       10066 :             if (poTargetCRS->IsSame(&oSRSWGS84))
    3876             :             {
    3877       20099 :                 if (cache.d->poRevCT == nullptr ||
    3878       10035 :                     !cache.d->poRevCT->GetTargetCS()->IsSame(poSourceCRS))
    3879             :                 {
    3880          30 :                     cache.d->poRevCT.reset(OGRCreateCoordinateTransformation(
    3881             :                         &oSRSWGS84, poSourceCRS));
    3882          30 :                     cache.d->bIsNorthPolar = false;
    3883          30 :                     cache.d->bIsPolar = false;
    3884          90 :                     if (cache.d->poRevCT &&
    3885          30 :                         IsPolarToWGS84(poCT, cache.d->poRevCT.get(),
    3886          60 :                                        cache.d->bIsNorthPolar))
    3887             :                     {
    3888           5 :                         cache.d->bIsPolar = true;
    3889             :                     }
    3890             :                 }
    3891       10064 :                 auto poRevCT = cache.d->poRevCT.get();
    3892       10064 :                 if (poRevCT != nullptr)
    3893             :                 {
    3894       10064 :                     if (cache.d->bIsPolar)
    3895             :                     {
    3896           6 :                         poDstGeom = TransformBeforePolarToWGS84(
    3897           6 :                             poRevCT, cache.d->bIsNorthPolar, poDstGeom,
    3898             :                             bNeedPostCorrection);
    3899             :                     }
    3900       10058 :                     else if (IsAntimeridianProjToWGS84(poCT, poRevCT,
    3901             :                                                        poDstGeom))
    3902             :                     {
    3903       10055 :                         poDstGeom = TransformBeforeAntimeridianToWGS84(
    3904             :                             poCT, poRevCT, poDstGeom, bNeedPostCorrection);
    3905             :                     }
    3906             :                 }
    3907             :             }
    3908             :         }
    3909             : #endif
    3910       10132 :         OGRErr eErr = poDstGeom->transform(poCT);
    3911       10132 :         if (eErr != OGRERR_NONE)
    3912             :         {
    3913           0 :             delete poDstGeom;
    3914           0 :             return nullptr;
    3915             :         }
    3916             : #ifdef HAVE_GEOS
    3917       10132 :         if (bNeedPostCorrection)
    3918             :         {
    3919          14 :             SnapCoordsCloseToLatLongBounds(poDstGeom);
    3920             :         }
    3921             : #endif
    3922             :     }
    3923             : 
    3924       10167 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "NO")))
    3925             :     {
    3926         101 :         if (poDstGeom->getSpatialReference() &&
    3927          49 :             !poDstGeom->getSpatialReference()->IsGeographic())
    3928             :         {
    3929             :             static bool bHasWarned = false;
    3930           0 :             if (!bHasWarned)
    3931             :             {
    3932           0 :                 CPLError(
    3933             :                     CE_Warning, CPLE_AppDefined,
    3934             :                     "WRAPDATELINE is without effect when reprojecting to a "
    3935             :                     "non-geographic CRS");
    3936           0 :                 bHasWarned = true;
    3937             :             }
    3938           0 :             return poDstGeom;
    3939             :         }
    3940             :         // TODO and we should probably also test that the axis order + data axis
    3941             :         // mapping is long-lat...
    3942             :         const OGRwkbGeometryType eType =
    3943          52 :             wkbFlatten(poDstGeom->getGeometryType());
    3944          52 :         if (eType == wkbPoint)
    3945             :         {
    3946           9 :             OGRPoint *poDstPoint = poDstGeom->toPoint();
    3947           9 :             WrapPointDateLine(poDstPoint);
    3948             :         }
    3949          43 :         else if (eType == wkbMultiPoint)
    3950             :         {
    3951           5 :             for (auto *poDstPoint : *(poDstGeom->toMultiPoint()))
    3952             :             {
    3953           4 :                 WrapPointDateLine(poDstPoint);
    3954             :             }
    3955             :         }
    3956             :         else
    3957             :         {
    3958          42 :             OGREnvelope sEnvelope;
    3959          42 :             poDstGeom->getEnvelope(&sEnvelope);
    3960          42 :             if (sEnvelope.MinX >= -360.0 && sEnvelope.MaxX <= -180.0)
    3961           2 :                 AddOffsetToLon(poDstGeom, 360.0);
    3962          40 :             else if (sEnvelope.MinX >= 180.0 && sEnvelope.MaxX <= 360.0)
    3963           2 :                 AddOffsetToLon(poDstGeom, -360.0);
    3964             :             else
    3965             :             {
    3966             :                 OGRwkbGeometryType eNewType;
    3967          38 :                 if (eType == wkbPolygon || eType == wkbMultiPolygon)
    3968          27 :                     eNewType = wkbMultiPolygon;
    3969          11 :                 else if (eType == wkbLineString || eType == wkbMultiLineString)
    3970          10 :                     eNewType = wkbMultiLineString;
    3971             :                 else
    3972           1 :                     eNewType = wkbGeometryCollection;
    3973             : 
    3974          38 :                 OGRGeometry *poMultiGeom = createGeometry(eNewType);
    3975             :                 OGRGeometryCollection *poMulti =
    3976          38 :                     poMultiGeom->toGeometryCollection();
    3977             : 
    3978          38 :                 double dfDateLineOffset = CPLAtofM(
    3979             :                     CSLFetchNameValueDef(papszOptions, "DATELINEOFFSET", "10"));
    3980          38 :                 if (dfDateLineOffset <= 0.0 || dfDateLineOffset >= 360.0)
    3981           0 :                     dfDateLineOffset = 10.0;
    3982             : 
    3983          38 :                 CutGeometryOnDateLineAndAddToMulti(poMulti, poDstGeom,
    3984             :                                                    dfDateLineOffset);
    3985             : 
    3986          38 :                 if (poMulti->getNumGeometries() == 0)
    3987             :                 {
    3988           0 :                     delete poMultiGeom;
    3989             :                 }
    3990          38 :                 else if (poMulti->getNumGeometries() == 1)
    3991             :                 {
    3992          13 :                     delete poDstGeom;
    3993          13 :                     poDstGeom = poMulti->getGeometryRef(0)->clone();
    3994          13 :                     delete poMultiGeom;
    3995             :                 }
    3996             :                 else
    3997             :                 {
    3998          25 :                     delete poDstGeom;
    3999          25 :                     poDstGeom = poMultiGeom;
    4000             :                 }
    4001             :             }
    4002             :         }
    4003             :     }
    4004             : 
    4005       10167 :     return poDstGeom;
    4006             : }
    4007             : 
    4008             : /************************************************************************/
    4009             : /*                         OGRGeomTransformer()                         */
    4010             : /************************************************************************/
    4011             : 
    4012             : struct OGRGeomTransformer
    4013             : {
    4014             :     std::unique_ptr<OGRCoordinateTransformation> poCT{};
    4015             :     OGRGeometryFactory::TransformWithOptionsCache cache{};
    4016             :     CPLStringList aosOptions{};
    4017             : 
    4018           6 :     OGRGeomTransformer() = default;
    4019             :     OGRGeomTransformer(const OGRGeomTransformer &) = delete;
    4020             :     OGRGeomTransformer &operator=(const OGRGeomTransformer &) = delete;
    4021             : };
    4022             : 
    4023             : /************************************************************************/
    4024             : /*                     OGR_GeomTransformer_Create()                     */
    4025             : /************************************************************************/
    4026             : 
    4027             : /** Create a geometry transformer.
    4028             :  *
    4029             :  * This is a enhanced version of OGR_G_Transform().
    4030             :  *
    4031             :  * When reprojecting geometries from a Polar Stereographic projection or a
    4032             :  * projection naturally crossing the antimeridian (like UTM Zone 60) to a
    4033             :  * geographic CRS, it will cut geometries along the antimeridian. So a
    4034             :  * LineString might be returned as a MultiLineString.
    4035             :  *
    4036             :  * The WRAPDATELINE=YES option might be specified for circumstances to correct
    4037             :  * geometries that incorrectly go from a longitude on a side of the antimeridian
    4038             :  * to the other side, like a LINESTRING(-179 0,179 0) will be transformed to
    4039             :  * a MULTILINESTRING ((-179 0,-180 0),(180 0,179 0)). For that use case, hCT
    4040             :  * might be NULL.
    4041             :  *
    4042             :  * @param hCT Coordinate transformation object (will be cloned) or NULL.
    4043             :  * @param papszOptions NULL terminated list of options, or NULL.
    4044             :  *                     Supported options are:
    4045             :  *                     <ul>
    4046             :  *                         <li>WRAPDATELINE=YES</li>
    4047             :  *                         <li>DATELINEOFFSET=longitude_gap_in_degree. Defaults
    4048             :  * to 10.</li>
    4049             :  *                     </ul>
    4050             :  * @return transformer object to free with OGR_GeomTransformer_Destroy()
    4051             :  * @since GDAL 3.1
    4052             :  */
    4053           6 : OGRGeomTransformerH OGR_GeomTransformer_Create(OGRCoordinateTransformationH hCT,
    4054             :                                                CSLConstList papszOptions)
    4055             : {
    4056           6 :     OGRGeomTransformer *transformer = new OGRGeomTransformer;
    4057           6 :     if (hCT)
    4058             :     {
    4059           3 :         transformer->poCT.reset(
    4060           3 :             OGRCoordinateTransformation::FromHandle(hCT)->Clone());
    4061             :     }
    4062           6 :     transformer->aosOptions.Assign(CSLDuplicate(papszOptions));
    4063           6 :     return transformer;
    4064             : }
    4065             : 
    4066             : /************************************************************************/
    4067             : /*                     OGR_GeomTransformer_Transform()                  */
    4068             : /************************************************************************/
    4069             : 
    4070             : /** Transforms a geometry.
    4071             :  *
    4072             :  * @param hTransformer transformer object.
    4073             :  * @param hGeom Source geometry.
    4074             :  * @return a new geometry (or NULL) to destroy with OGR_G_DestroyGeometry()
    4075             :  * @since GDAL 3.1
    4076             :  */
    4077           6 : OGRGeometryH OGR_GeomTransformer_Transform(OGRGeomTransformerH hTransformer,
    4078             :                                            OGRGeometryH hGeom)
    4079             : {
    4080           6 :     VALIDATE_POINTER1(hTransformer, "OGR_GeomTransformer_Transform", nullptr);
    4081           6 :     VALIDATE_POINTER1(hGeom, "OGR_GeomTransformer_Transform", nullptr);
    4082             : 
    4083          12 :     return OGRGeometry::ToHandle(OGRGeometryFactory::transformWithOptions(
    4084           6 :         OGRGeometry::FromHandle(hGeom), hTransformer->poCT.get(),
    4085          12 :         hTransformer->aosOptions.List(), hTransformer->cache));
    4086             : }
    4087             : 
    4088             : /************************************************************************/
    4089             : /*                      OGR_GeomTransformer_Destroy()                   */
    4090             : /************************************************************************/
    4091             : 
    4092             : /** Destroy a geometry transformer allocated with OGR_GeomTransformer_Create()
    4093             :  *
    4094             :  * @param hTransformer transformer object.
    4095             :  * @since GDAL 3.1
    4096             :  */
    4097           6 : void OGR_GeomTransformer_Destroy(OGRGeomTransformerH hTransformer)
    4098             : {
    4099           6 :     delete hTransformer;
    4100           6 : }
    4101             : 
    4102             : /************************************************************************/
    4103             : /*                       OGRGF_GetDefaultStepSize()                     */
    4104             : /************************************************************************/
    4105             : 
    4106        4300 : static double OGRGF_GetDefaultStepSize()
    4107             : {
    4108             :     // coverity[tainted_data]
    4109        4300 :     return CPLAtofM(CPLGetConfigOption("OGR_ARC_STEPSIZE", "4"));
    4110             : }
    4111             : 
    4112             : /************************************************************************/
    4113             : /*                              DISTANCE()                              */
    4114             : /************************************************************************/
    4115             : 
    4116      311641 : static inline double DISTANCE(double x1, double y1, double x2, double y2)
    4117             : {
    4118      311641 :     return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    4119             : }
    4120             : 
    4121             : /************************************************************************/
    4122             : /*                        approximateArcAngles()                        */
    4123             : /************************************************************************/
    4124             : 
    4125             : /**
    4126             :  * Stroke arc to linestring.
    4127             :  *
    4128             :  * Stroke an arc of a circle to a linestring based on a center
    4129             :  * point, radius, start angle and end angle, all angles in degrees.
    4130             :  *
    4131             :  * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be
    4132             :  * used.  This is currently 4 degrees unless the user has overridden the
    4133             :  * value with the OGR_ARC_STEPSIZE configuration variable.
    4134             :  *
    4135             :  * If the OGR_ARC_MAX_GAP configuration variable is set, the straight-line
    4136             :  * distance between adjacent pairs of interpolated points will be limited to
    4137             :  * the specified distance. If the distance between a pair of points exceeds
    4138             :  * this maximum, additional points are interpolated between the two points.
    4139             :  *
    4140             :  * @see CPLSetConfigOption()
    4141             :  *
    4142             :  * @param dfCenterX center X
    4143             :  * @param dfCenterY center Y
    4144             :  * @param dfZ center Z
    4145             :  * @param dfPrimaryRadius X radius of ellipse.
    4146             :  * @param dfSecondaryRadius Y radius of ellipse.
    4147             :  * @param dfRotation rotation of the ellipse clockwise.
    4148             :  * @param dfStartAngle angle to first point on arc (clockwise of X-positive)
    4149             :  * @param dfEndAngle angle to last point on arc (clockwise of X-positive)
    4150             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    4151             :  * arc, zero to use the default setting.
    4152             :  * @param bUseMaxGap Optional: whether to honor OGR_ARC_MAX_GAP.
    4153             :  *
    4154             :  * @return OGRLineString geometry representing an approximation of the arc.
    4155             :  *
    4156             :  * @since OGR 1.8.0
    4157             :  */
    4158             : 
    4159         111 : OGRGeometry *OGRGeometryFactory::approximateArcAngles(
    4160             :     double dfCenterX, double dfCenterY, double dfZ, double dfPrimaryRadius,
    4161             :     double dfSecondaryRadius, double dfRotation, double dfStartAngle,
    4162             :     double dfEndAngle, double dfMaxAngleStepSizeDegrees,
    4163             :     const bool bUseMaxGap /* = false */)
    4164             : 
    4165             : {
    4166         111 :     OGRLineString *poLine = new OGRLineString();
    4167         111 :     const double dfRotationRadians = dfRotation * M_PI / 180.0;
    4168             : 
    4169             :     // Support default arc step setting.
    4170         111 :     if (dfMaxAngleStepSizeDegrees < 1e-6)
    4171             :     {
    4172         110 :         dfMaxAngleStepSizeDegrees = OGRGF_GetDefaultStepSize();
    4173             :     }
    4174             : 
    4175             :     // Determine maximum interpolation gap. This is the largest straight-line
    4176             :     // distance allowed between pairs of interpolated points. Default zero,
    4177             :     // meaning no gap.
    4178             :     // coverity[tainted_data]
    4179             :     const double dfMaxInterpolationGap =
    4180         111 :         bUseMaxGap ? CPLAtofM(CPLGetConfigOption("OGR_ARC_MAX_GAP", "0")) : 0.0;
    4181             : 
    4182             :     // Is this a full circle?
    4183         111 :     const bool bIsFullCircle = fabs(dfEndAngle - dfStartAngle) == 360.0;
    4184             : 
    4185             :     // Switch direction.
    4186         111 :     dfStartAngle *= -1;
    4187         111 :     dfEndAngle *= -1;
    4188             : 
    4189             :     // Figure out the number of slices to make this into.
    4190             :     int nVertexCount =
    4191         222 :         std::max(2, static_cast<int>(ceil(fabs(dfEndAngle - dfStartAngle) /
    4192         111 :                                           dfMaxAngleStepSizeDegrees) +
    4193         111 :                                      1));
    4194         111 :     const double dfSlice = (dfEndAngle - dfStartAngle) / (nVertexCount - 1);
    4195             : 
    4196             :     // If it is a full circle we will work out the last point separately.
    4197         111 :     if (bIsFullCircle)
    4198             :     {
    4199          51 :         nVertexCount--;
    4200             :     }
    4201             : 
    4202             :     /* -------------------------------------------------------------------- */
    4203             :     /*      Compute the interpolated points.                                */
    4204             :     /* -------------------------------------------------------------------- */
    4205         111 :     double dfLastX = 0.0;
    4206         111 :     double dfLastY = 0.0;
    4207         111 :     int nTotalAddPoints = 0;
    4208        6876 :     for (int iPoint = 0; iPoint < nVertexCount; iPoint++)
    4209             :     {
    4210        6765 :         const double dfAngleOnEllipse =
    4211        6765 :             (dfStartAngle + iPoint * dfSlice) * M_PI / 180.0;
    4212             : 
    4213             :         // Compute position on the unrotated ellipse.
    4214        6765 :         const double dfEllipseX = cos(dfAngleOnEllipse) * dfPrimaryRadius;
    4215        6765 :         const double dfEllipseY = sin(dfAngleOnEllipse) * dfSecondaryRadius;
    4216             : 
    4217             :         // Is this point too far from the previous point?
    4218        6765 :         if (iPoint && dfMaxInterpolationGap != 0.0)
    4219             :         {
    4220             :             const double dfDistFromLast =
    4221           1 :                 DISTANCE(dfLastX, dfLastY, dfEllipseX, dfEllipseY);
    4222             : 
    4223           1 :             if (dfDistFromLast > dfMaxInterpolationGap)
    4224             :             {
    4225           1 :                 const int nAddPoints =
    4226           1 :                     static_cast<int>(dfDistFromLast / dfMaxInterpolationGap);
    4227           1 :                 const double dfAddSlice = dfSlice / (nAddPoints + 1);
    4228             : 
    4229             :                 // Interpolate additional points
    4230           3 :                 for (int iAddPoint = 0; iAddPoint < nAddPoints; iAddPoint++)
    4231             :                 {
    4232           2 :                     const double dfAddAngleOnEllipse =
    4233           2 :                         (dfStartAngle + (iPoint - 1) * dfSlice +
    4234           2 :                          (iAddPoint + 1) * dfAddSlice) *
    4235             :                         (M_PI / 180.0);
    4236             : 
    4237           2 :                     poLine->setPoint(
    4238           2 :                         iPoint + nTotalAddPoints + iAddPoint,
    4239           2 :                         cos(dfAddAngleOnEllipse) * dfPrimaryRadius,
    4240           2 :                         sin(dfAddAngleOnEllipse) * dfSecondaryRadius, dfZ);
    4241             :                 }
    4242             : 
    4243           1 :                 nTotalAddPoints += nAddPoints;
    4244             :             }
    4245             :         }
    4246             : 
    4247        6765 :         poLine->setPoint(iPoint + nTotalAddPoints, dfEllipseX, dfEllipseY, dfZ);
    4248        6765 :         dfLastX = dfEllipseX;
    4249        6765 :         dfLastY = dfEllipseY;
    4250             :     }
    4251             : 
    4252             :     /* -------------------------------------------------------------------- */
    4253             :     /*      Rotate and translate the ellipse.                               */
    4254             :     /* -------------------------------------------------------------------- */
    4255         111 :     nVertexCount = poLine->getNumPoints();
    4256        6878 :     for (int iPoint = 0; iPoint < nVertexCount; iPoint++)
    4257             :     {
    4258        6767 :         const double dfEllipseX = poLine->getX(iPoint);
    4259        6767 :         const double dfEllipseY = poLine->getY(iPoint);
    4260             : 
    4261             :         // Rotate this position around the center of the ellipse.
    4262        6767 :         const double dfArcX = dfCenterX + dfEllipseX * cos(dfRotationRadians) +
    4263        6767 :                               dfEllipseY * sin(dfRotationRadians);
    4264        6767 :         const double dfArcY = dfCenterY - dfEllipseX * sin(dfRotationRadians) +
    4265        6767 :                               dfEllipseY * cos(dfRotationRadians);
    4266             : 
    4267        6767 :         poLine->setPoint(iPoint, dfArcX, dfArcY, dfZ);
    4268             :     }
    4269             : 
    4270             :     /* -------------------------------------------------------------------- */
    4271             :     /*      If we're asked to make a full circle, ensure the start and      */
    4272             :     /*      end points coincide exactly, in spite of any rounding error.    */
    4273             :     /* -------------------------------------------------------------------- */
    4274         111 :     if (bIsFullCircle)
    4275             :     {
    4276         102 :         OGRPoint oPoint;
    4277          51 :         poLine->getPoint(0, &oPoint);
    4278          51 :         poLine->setPoint(nVertexCount, &oPoint);
    4279             :     }
    4280             : 
    4281         111 :     return poLine;
    4282             : }
    4283             : 
    4284             : /************************************************************************/
    4285             : /*                     OGR_G_ApproximateArcAngles()                     */
    4286             : /************************************************************************/
    4287             : 
    4288             : /**
    4289             :  * Stroke arc to linestring.
    4290             :  *
    4291             :  * Stroke an arc of a circle to a linestring based on a center
    4292             :  * point, radius, start angle and end angle, all angles in degrees.
    4293             :  *
    4294             :  * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be
    4295             :  * used.  This is currently 4 degrees unless the user has overridden the
    4296             :  * value with the OGR_ARC_STEPSIZE configuration variable.
    4297             :  *
    4298             :  * @see CPLSetConfigOption()
    4299             :  *
    4300             :  * @param dfCenterX center X
    4301             :  * @param dfCenterY center Y
    4302             :  * @param dfZ center Z
    4303             :  * @param dfPrimaryRadius X radius of ellipse.
    4304             :  * @param dfSecondaryRadius Y radius of ellipse.
    4305             :  * @param dfRotation rotation of the ellipse clockwise.
    4306             :  * @param dfStartAngle angle to first point on arc (clockwise of X-positive)
    4307             :  * @param dfEndAngle angle to last point on arc (clockwise of X-positive)
    4308             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    4309             :  * arc, zero to use the default setting.
    4310             :  *
    4311             :  * @return OGRLineString geometry representing an approximation of the arc.
    4312             :  *
    4313             :  * @since OGR 1.8.0
    4314             :  */
    4315             : 
    4316           1 : OGRGeometryH CPL_DLL OGR_G_ApproximateArcAngles(
    4317             :     double dfCenterX, double dfCenterY, double dfZ, double dfPrimaryRadius,
    4318             :     double dfSecondaryRadius, double dfRotation, double dfStartAngle,
    4319             :     double dfEndAngle, double dfMaxAngleStepSizeDegrees)
    4320             : 
    4321             : {
    4322           1 :     return OGRGeometry::ToHandle(OGRGeometryFactory::approximateArcAngles(
    4323             :         dfCenterX, dfCenterY, dfZ, dfPrimaryRadius, dfSecondaryRadius,
    4324           1 :         dfRotation, dfStartAngle, dfEndAngle, dfMaxAngleStepSizeDegrees));
    4325             : }
    4326             : 
    4327             : /************************************************************************/
    4328             : /*                           forceToLineString()                        */
    4329             : /************************************************************************/
    4330             : 
    4331             : /**
    4332             :  * \brief Convert to line string.
    4333             :  *
    4334             :  * Tries to force the provided geometry to be a line string.  This nominally
    4335             :  * effects a change on multilinestrings.
    4336             :  * In GDAL 2.0, for polygons or curvepolygons that have a single exterior ring,
    4337             :  * it will return the ring. For circular strings or compound curves, it will
    4338             :  * return an approximated line string.
    4339             :  *
    4340             :  * The passed in geometry is
    4341             :  * consumed and a new one returned (or potentially the same one).
    4342             :  *
    4343             :  * @param poGeom the input geometry - ownership is passed to the method.
    4344             :  * @param bOnlyInOrder flag that, if set to FALSE, indicate that the order of
    4345             :  *                     points in a linestring might be reversed if it enables
    4346             :  *                     to match the extremity of another linestring. If set
    4347             :  *                     to TRUE, the start of a linestring must match the end
    4348             :  *                     of another linestring.
    4349             :  * @return new geometry.
    4350             :  */
    4351             : 
    4352         171 : OGRGeometry *OGRGeometryFactory::forceToLineString(OGRGeometry *poGeom,
    4353             :                                                    bool bOnlyInOrder)
    4354             : 
    4355             : {
    4356         171 :     if (poGeom == nullptr)
    4357           2 :         return nullptr;
    4358             : 
    4359         169 :     const OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
    4360             : 
    4361             :     /* -------------------------------------------------------------------- */
    4362             :     /*      If this is already a LineString, nothing to do                  */
    4363             :     /* -------------------------------------------------------------------- */
    4364         169 :     if (eGeomType == wkbLineString)
    4365             :     {
    4366             :         // Except if it is a linearring.
    4367          25 :         poGeom = OGRCurve::CastToLineString(poGeom->toCurve());
    4368             : 
    4369          25 :         return poGeom;
    4370             :     }
    4371             : 
    4372             :     /* -------------------------------------------------------------------- */
    4373             :     /*      If it is a polygon with a single ring, return it                 */
    4374             :     /* -------------------------------------------------------------------- */
    4375         144 :     if (eGeomType == wkbPolygon || eGeomType == wkbCurvePolygon)
    4376             :     {
    4377          30 :         OGRCurvePolygon *poCP = poGeom->toCurvePolygon();
    4378          30 :         if (poCP->getNumInteriorRings() == 0)
    4379             :         {
    4380          28 :             OGRCurve *poRing = poCP->stealExteriorRingCurve();
    4381          28 :             delete poCP;
    4382          28 :             return forceToLineString(poRing);
    4383             :         }
    4384           2 :         return poGeom;
    4385             :     }
    4386             : 
    4387             :     /* -------------------------------------------------------------------- */
    4388             :     /*      If it is a curve line, call CurveToLine()                        */
    4389             :     /* -------------------------------------------------------------------- */
    4390         114 :     if (eGeomType == wkbCircularString || eGeomType == wkbCompoundCurve)
    4391             :     {
    4392          66 :         OGRGeometry *poNewGeom = poGeom->toCurve()->CurveToLine();
    4393          66 :         delete poGeom;
    4394          66 :         return poNewGeom;
    4395             :     }
    4396             : 
    4397          48 :     if (eGeomType != wkbGeometryCollection && eGeomType != wkbMultiLineString &&
    4398             :         eGeomType != wkbMultiCurve)
    4399          17 :         return poGeom;
    4400             : 
    4401             :     // Build an aggregated linestring from all the linestrings in the container.
    4402          31 :     OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    4403          31 :     if (poGeom->hasCurveGeometry())
    4404             :     {
    4405             :         OGRGeometryCollection *poNewGC =
    4406           7 :             poGC->getLinearGeometry()->toGeometryCollection();
    4407           7 :         delete poGC;
    4408           7 :         poGC = poNewGC;
    4409             :     }
    4410             : 
    4411          31 :     if (poGC->getNumGeometries() == 0)
    4412             :     {
    4413           3 :         poGeom = new OGRLineString();
    4414           3 :         poGeom->assignSpatialReference(poGC->getSpatialReference());
    4415           3 :         delete poGC;
    4416           3 :         return poGeom;
    4417             :     }
    4418             : 
    4419          28 :     int iGeom0 = 0;
    4420          69 :     while (iGeom0 < poGC->getNumGeometries())
    4421             :     {
    4422          41 :         if (wkbFlatten(poGC->getGeometryRef(iGeom0)->getGeometryType()) !=
    4423             :             wkbLineString)
    4424             :         {
    4425          12 :             iGeom0++;
    4426          26 :             continue;
    4427             :         }
    4428             : 
    4429             :         OGRLineString *poLineString0 =
    4430          29 :             poGC->getGeometryRef(iGeom0)->toLineString();
    4431          29 :         if (poLineString0->getNumPoints() < 2)
    4432             :         {
    4433          14 :             iGeom0++;
    4434          14 :             continue;
    4435             :         }
    4436             : 
    4437          30 :         OGRPoint pointStart0;
    4438          15 :         poLineString0->StartPoint(&pointStart0);
    4439          30 :         OGRPoint pointEnd0;
    4440          15 :         poLineString0->EndPoint(&pointEnd0);
    4441             : 
    4442          15 :         int iGeom1 = iGeom0 + 1;  // Used after for.
    4443          17 :         for (; iGeom1 < poGC->getNumGeometries(); iGeom1++)
    4444             :         {
    4445           6 :             if (wkbFlatten(poGC->getGeometryRef(iGeom1)->getGeometryType()) !=
    4446             :                 wkbLineString)
    4447           1 :                 continue;
    4448             : 
    4449             :             OGRLineString *poLineString1 =
    4450           6 :                 poGC->getGeometryRef(iGeom1)->toLineString();
    4451           6 :             if (poLineString1->getNumPoints() < 2)
    4452           1 :                 continue;
    4453             : 
    4454           5 :             OGRPoint pointStart1;
    4455           5 :             poLineString1->StartPoint(&pointStart1);
    4456           5 :             OGRPoint pointEnd1;
    4457           5 :             poLineString1->EndPoint(&pointEnd1);
    4458             : 
    4459           5 :             if (!bOnlyInOrder && (pointEnd0.Equals(&pointEnd1) ||
    4460           0 :                                   pointStart0.Equals(&pointStart1)))
    4461             :             {
    4462           0 :                 poLineString1->reversePoints();
    4463           0 :                 poLineString1->StartPoint(&pointStart1);
    4464           0 :                 poLineString1->EndPoint(&pointEnd1);
    4465             :             }
    4466             : 
    4467           5 :             if (pointEnd0.Equals(&pointStart1))
    4468             :             {
    4469           4 :                 poLineString0->addSubLineString(poLineString1, 1);
    4470           4 :                 poGC->removeGeometry(iGeom1);
    4471           4 :                 break;
    4472             :             }
    4473             : 
    4474           1 :             if (pointEnd1.Equals(&pointStart0))
    4475             :             {
    4476           0 :                 poLineString1->addSubLineString(poLineString0, 1);
    4477           0 :                 poGC->removeGeometry(iGeom0);
    4478           0 :                 break;
    4479             :             }
    4480             :         }
    4481             : 
    4482          15 :         if (iGeom1 == poGC->getNumGeometries())
    4483             :         {
    4484          14 :             iGeom0++;
    4485             :         }
    4486             :     }
    4487             : 
    4488          28 :     if (poGC->getNumGeometries() == 1)
    4489             :     {
    4490          20 :         OGRGeometry *poSingleGeom = poGC->getGeometryRef(0);
    4491          20 :         poGC->removeGeometry(0, FALSE);
    4492          20 :         delete poGC;
    4493             : 
    4494          20 :         return poSingleGeom;
    4495             :     }
    4496             : 
    4497           8 :     return poGC;
    4498             : }
    4499             : 
    4500             : /************************************************************************/
    4501             : /*                      OGR_G_ForceToLineString()                       */
    4502             : /************************************************************************/
    4503             : 
    4504             : /**
    4505             :  * \brief Convert to line string.
    4506             :  *
    4507             :  * This function is the same as the C++ method
    4508             :  * OGRGeometryFactory::forceToLineString().
    4509             :  *
    4510             :  * @param hGeom handle to the geometry to convert (ownership surrendered).
    4511             :  * @return the converted geometry (ownership to caller).
    4512             :  *
    4513             :  * @since GDAL/OGR 1.10.0
    4514             :  */
    4515             : 
    4516          58 : OGRGeometryH OGR_G_ForceToLineString(OGRGeometryH hGeom)
    4517             : 
    4518             : {
    4519          58 :     return OGRGeometry::ToHandle(
    4520          58 :         OGRGeometryFactory::forceToLineString(OGRGeometry::FromHandle(hGeom)));
    4521             : }
    4522             : 
    4523             : /************************************************************************/
    4524             : /*                           forceTo()                                  */
    4525             : /************************************************************************/
    4526             : 
    4527             : /**
    4528             :  * \brief Convert to another geometry type
    4529             :  *
    4530             :  * Tries to force the provided geometry to the specified geometry type.
    4531             :  *
    4532             :  * It can promote 'single' geometry type to their corresponding collection type
    4533             :  * (see OGR_GT_GetCollection()) or the reverse. non-linear geometry type to
    4534             :  * their corresponding linear geometry type (see OGR_GT_GetLinear()), by
    4535             :  * possibly approximating circular arcs they may contain.  Regarding conversion
    4536             :  * from linear geometry types to curve geometry types, only "wrapping" will be
    4537             :  * done. No attempt to retrieve potential circular arcs by de-approximating
    4538             :  * stroking will be done. For that, OGRGeometry::getCurveGeometry() can be used.
    4539             :  *
    4540             :  * The passed in geometry is consumed and a new one returned (or potentially the
    4541             :  * same one).
    4542             :  *
    4543             :  * Starting with GDAL 3.9, this method honours the dimensionality of eTargetType.
    4544             :  *
    4545             :  * @param poGeom the input geometry - ownership is passed to the method.
    4546             :  * @param eTargetType target output geometry type.
    4547             :  * @param papszOptions options as a null-terminated list of strings or NULL.
    4548             :  * @return new geometry, or nullptr in case of error.
    4549             :  *
    4550             :  * @since GDAL 2.0
    4551             :  */
    4552             : 
    4553        2242 : OGRGeometry *OGRGeometryFactory::forceTo(OGRGeometry *poGeom,
    4554             :                                          OGRwkbGeometryType eTargetType,
    4555             :                                          const char *const *papszOptions)
    4556             : {
    4557        2242 :     if (poGeom == nullptr)
    4558           0 :         return poGeom;
    4559             : 
    4560        2242 :     const OGRwkbGeometryType eTargetTypeFlat = wkbFlatten(eTargetType);
    4561        2242 :     if (eTargetTypeFlat == wkbUnknown)
    4562         252 :         return poGeom;
    4563             : 
    4564        1990 :     if (poGeom->IsEmpty())
    4565             :     {
    4566         277 :         OGRGeometry *poRet = createGeometry(eTargetType);
    4567         277 :         if (poRet)
    4568             :         {
    4569         277 :             poRet->assignSpatialReference(poGeom->getSpatialReference());
    4570         277 :             poRet->set3D(OGR_GT_HasZ(eTargetType));
    4571         277 :             poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4572             :         }
    4573         277 :         delete poGeom;
    4574         277 :         return poRet;
    4575             :     }
    4576             : 
    4577        1713 :     OGRwkbGeometryType eType = poGeom->getGeometryType();
    4578        1713 :     OGRwkbGeometryType eTypeFlat = wkbFlatten(eType);
    4579             : 
    4580        1713 :     if (eTargetTypeFlat != eTargetType && (eType == eTypeFlat))
    4581             :     {
    4582          57 :         auto poGeomNew = forceTo(poGeom, eTargetTypeFlat, papszOptions);
    4583          57 :         if (poGeomNew)
    4584             :         {
    4585          57 :             poGeomNew->set3D(OGR_GT_HasZ(eTargetType));
    4586          57 :             poGeomNew->setMeasured(OGR_GT_HasM(eTargetType));
    4587             :         }
    4588          57 :         return poGeomNew;
    4589             :     }
    4590             : 
    4591        1656 :     if (eTypeFlat == eTargetTypeFlat)
    4592             :     {
    4593         502 :         poGeom->set3D(OGR_GT_HasZ(eTargetType));
    4594         502 :         poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    4595         502 :         return poGeom;
    4596             :     }
    4597             : 
    4598        1154 :     eType = eTypeFlat;
    4599             : 
    4600        1279 :     if (OGR_GT_IsSubClassOf(eType, wkbPolyhedralSurface) &&
    4601         125 :         (eTargetTypeFlat == wkbMultiSurface ||
    4602             :          eTargetTypeFlat == wkbGeometryCollection))
    4603             :     {
    4604          55 :         OGRwkbGeometryType eTempGeomType = wkbMultiPolygon;
    4605          55 :         if (OGR_GT_HasZ(eTargetType))
    4606          51 :             eTempGeomType = OGR_GT_SetZ(eTempGeomType);
    4607          55 :         if (OGR_GT_HasM(eTargetType))
    4608           0 :             eTempGeomType = OGR_GT_SetM(eTempGeomType);
    4609          55 :         return forceTo(forceTo(poGeom, eTempGeomType, papszOptions),
    4610          55 :                        eTargetType, papszOptions);
    4611             :     }
    4612             : 
    4613        1099 :     if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) &&
    4614             :         eTargetTypeFlat == wkbGeometryCollection)
    4615             :     {
    4616         111 :         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    4617         111 :         auto poRet = OGRGeometryCollection::CastToGeometryCollection(poGC);
    4618         111 :         poRet->set3D(OGR_GT_HasZ(eTargetType));
    4619         111 :         poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4620         111 :         return poRet;
    4621             :     }
    4622             : 
    4623         988 :     if (eType == wkbTriangle && eTargetTypeFlat == wkbPolyhedralSurface)
    4624             :     {
    4625           1 :         OGRPolyhedralSurface *poPS = new OGRPolyhedralSurface();
    4626           1 :         poPS->assignSpatialReference(poGeom->getSpatialReference());
    4627           1 :         poPS->addGeometryDirectly(OGRTriangle::CastToPolygon(poGeom));
    4628           1 :         poPS->set3D(OGR_GT_HasZ(eTargetType));
    4629           1 :         poPS->setMeasured(OGR_GT_HasM(eTargetType));
    4630           1 :         return poPS;
    4631             :     }
    4632         987 :     else if (eType == wkbPolygon && eTargetTypeFlat == wkbPolyhedralSurface)
    4633             :     {
    4634           3 :         OGRPolyhedralSurface *poPS = new OGRPolyhedralSurface();
    4635           3 :         poPS->assignSpatialReference(poGeom->getSpatialReference());
    4636           3 :         poPS->addGeometryDirectly(poGeom);
    4637           3 :         poPS->set3D(OGR_GT_HasZ(eTargetType));
    4638           3 :         poPS->setMeasured(OGR_GT_HasM(eTargetType));
    4639           3 :         return poPS;
    4640             :     }
    4641         984 :     else if (eType == wkbMultiPolygon &&
    4642             :              eTargetTypeFlat == wkbPolyhedralSurface)
    4643             :     {
    4644           2 :         OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
    4645           2 :         OGRPolyhedralSurface *poPS = new OGRPolyhedralSurface();
    4646           4 :         for (int i = 0; i < poMP->getNumGeometries(); ++i)
    4647             :         {
    4648           2 :             poPS->addGeometry(poMP->getGeometryRef(i));
    4649             :         }
    4650           2 :         delete poGeom;
    4651           2 :         poPS->set3D(OGR_GT_HasZ(eTargetType));
    4652           2 :         poPS->setMeasured(OGR_GT_HasM(eTargetType));
    4653           2 :         return poPS;
    4654             :     }
    4655         982 :     else if (eType == wkbTIN && eTargetTypeFlat == wkbPolyhedralSurface)
    4656             :     {
    4657           1 :         poGeom = OGRTriangulatedSurface::CastToPolyhedralSurface(
    4658             :             poGeom->toTriangulatedSurface());
    4659             :     }
    4660         981 :     else if (eType == wkbCurvePolygon &&
    4661             :              eTargetTypeFlat == wkbPolyhedralSurface)
    4662             :     {
    4663           1 :         OGRwkbGeometryType eTempGeomType = wkbPolygon;
    4664           1 :         if (OGR_GT_HasZ(eTargetType))
    4665           0 :             eTempGeomType = OGR_GT_SetZ(eTempGeomType);
    4666           1 :         if (OGR_GT_HasM(eTargetType))
    4667           0 :             eTempGeomType = OGR_GT_SetM(eTempGeomType);
    4668           1 :         return forceTo(forceTo(poGeom, eTempGeomType, papszOptions),
    4669           1 :                        eTargetType, papszOptions);
    4670             :     }
    4671         980 :     else if (eType == wkbMultiSurface &&
    4672             :              eTargetTypeFlat == wkbPolyhedralSurface)
    4673             :     {
    4674           1 :         OGRwkbGeometryType eTempGeomType = wkbMultiPolygon;
    4675           1 :         if (OGR_GT_HasZ(eTargetType))
    4676           0 :             eTempGeomType = OGR_GT_SetZ(eTempGeomType);
    4677           1 :         if (OGR_GT_HasM(eTargetType))
    4678           0 :             eTempGeomType = OGR_GT_SetM(eTempGeomType);
    4679           1 :         return forceTo(forceTo(poGeom, eTempGeomType, papszOptions),
    4680           1 :                        eTargetType, papszOptions);
    4681             :     }
    4682             : 
    4683         979 :     else if (eType == wkbTriangle && eTargetTypeFlat == wkbTIN)
    4684             :     {
    4685           1 :         OGRTriangulatedSurface *poTS = new OGRTriangulatedSurface();
    4686           1 :         poTS->assignSpatialReference(poGeom->getSpatialReference());
    4687           1 :         poTS->addGeometryDirectly(poGeom);
    4688           1 :         poTS->set3D(OGR_GT_HasZ(eTargetType));
    4689           1 :         poTS->setMeasured(OGR_GT_HasM(eTargetType));
    4690           1 :         return poTS;
    4691             :     }
    4692         978 :     else if (eType == wkbPolygon && eTargetTypeFlat == wkbTIN)
    4693             :     {
    4694           4 :         OGRPolygon *poPoly = poGeom->toPolygon();
    4695           4 :         OGRLinearRing *poLR = poPoly->getExteriorRing();
    4696           7 :         if (!(poLR != nullptr && poLR->getNumPoints() == 4 &&
    4697           3 :               poPoly->getNumInteriorRings() == 0))
    4698             :         {
    4699           1 :             return poGeom;
    4700             :         }
    4701           3 :         OGRErr eErr = OGRERR_NONE;
    4702           3 :         OGRTriangle *poTriangle = new OGRTriangle(*poPoly, eErr);
    4703           3 :         OGRTriangulatedSurface *poTS = new OGRTriangulatedSurface();
    4704           3 :         poTS->assignSpatialReference(poGeom->getSpatialReference());
    4705           3 :         poTS->addGeometryDirectly(poTriangle);
    4706           3 :         delete poGeom;
    4707           3 :         poTS->set3D(OGR_GT_HasZ(eTargetType));
    4708           3 :         poTS->setMeasured(OGR_GT_HasM(eTargetType));
    4709           3 :         return poTS;
    4710             :     }
    4711         974 :     else if (eType == wkbMultiPolygon && eTargetTypeFlat == wkbTIN)
    4712             :     {
    4713           1 :         OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
    4714           2 :         for (const auto poPoly : *poMP)
    4715             :         {
    4716           1 :             const OGRLinearRing *poLR = poPoly->getExteriorRing();
    4717           2 :             if (!(poLR != nullptr && poLR->getNumPoints() == 4 &&
    4718           1 :                   poPoly->getNumInteriorRings() == 0))
    4719             :             {
    4720           0 :                 return poGeom;
    4721             :             }
    4722             :         }
    4723           1 :         OGRTriangulatedSurface *poTS = new OGRTriangulatedSurface();
    4724           1 :         poTS->assignSpatialReference(poGeom->getSpatialReference());
    4725           2 :         for (const auto poPoly : *poMP)
    4726             :         {
    4727           1 :             OGRErr eErr = OGRERR_NONE;
    4728           1 :             poTS->addGeometryDirectly(new OGRTriangle(*poPoly, eErr));
    4729             :         }
    4730           1 :         delete poGeom;
    4731           1 :         poTS->set3D(OGR_GT_HasZ(eTargetType));
    4732           1 :         poTS->setMeasured(OGR_GT_HasM(eTargetType));
    4733           1 :         return poTS;
    4734             :     }
    4735         973 :     else if (eType == wkbPolyhedralSurface && eTargetTypeFlat == wkbTIN)
    4736             :     {
    4737           2 :         OGRPolyhedralSurface *poPS = poGeom->toPolyhedralSurface();
    4738           3 :         for (const auto poPoly : *poPS)
    4739             :         {
    4740           2 :             const OGRLinearRing *poLR = poPoly->getExteriorRing();
    4741           3 :             if (!(poLR != nullptr && poLR->getNumPoints() == 4 &&
    4742           1 :                   poPoly->getNumInteriorRings() == 0))
    4743             :             {
    4744           1 :                 poGeom->set3D(OGR_GT_HasZ(eTargetType));
    4745           1 :                 poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    4746           1 :                 return poGeom;
    4747             :             }
    4748             :         }
    4749           1 :         OGRTriangulatedSurface *poTS = new OGRTriangulatedSurface();
    4750           1 :         poTS->assignSpatialReference(poGeom->getSpatialReference());
    4751           2 :         for (const auto poPoly : *poPS)
    4752             :         {
    4753           1 :             OGRErr eErr = OGRERR_NONE;
    4754           1 :             poTS->addGeometryDirectly(new OGRTriangle(*poPoly, eErr));
    4755             :         }
    4756           1 :         delete poGeom;
    4757           1 :         poTS->set3D(OGR_GT_HasZ(eTargetType));
    4758           1 :         poTS->setMeasured(OGR_GT_HasM(eTargetType));
    4759           1 :         return poTS;
    4760             :     }
    4761             : 
    4762         971 :     else if (eType == wkbPolygon && eTargetTypeFlat == wkbTriangle)
    4763             :     {
    4764           7 :         OGRPolygon *poPoly = poGeom->toPolygon();
    4765           7 :         OGRLinearRing *poLR = poPoly->getExteriorRing();
    4766          13 :         if (!(poLR != nullptr && poLR->getNumPoints() == 4 &&
    4767           6 :               poPoly->getNumInteriorRings() == 0))
    4768             :         {
    4769           1 :             poGeom->set3D(OGR_GT_HasZ(eTargetType));
    4770           1 :             poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    4771           1 :             return poGeom;
    4772             :         }
    4773           6 :         OGRErr eErr = OGRERR_NONE;
    4774           6 :         OGRTriangle *poTriangle = new OGRTriangle(*poPoly, eErr);
    4775           6 :         delete poGeom;
    4776           6 :         poTriangle->set3D(OGR_GT_HasZ(eTargetType));
    4777           6 :         poTriangle->setMeasured(OGR_GT_HasM(eTargetType));
    4778           6 :         return poTriangle;
    4779             :     }
    4780             : 
    4781         965 :     if (eTargetTypeFlat == wkbTriangle || eTargetTypeFlat == wkbTIN ||
    4782             :         eTargetTypeFlat == wkbPolyhedralSurface)
    4783             :     {
    4784           9 :         OGRwkbGeometryType eTempGeomType = wkbPolygon;
    4785           9 :         if (OGR_GT_HasZ(eTargetType))
    4786           0 :             eTempGeomType = OGR_GT_SetZ(eTempGeomType);
    4787           9 :         if (OGR_GT_HasM(eTargetType))
    4788           1 :             eTempGeomType = OGR_GT_SetM(eTempGeomType);
    4789           9 :         OGRGeometry *poPoly = forceTo(poGeom, eTempGeomType, papszOptions);
    4790           9 :         if (poPoly == poGeom)
    4791           0 :             return poGeom;
    4792           9 :         return forceTo(poPoly, eTargetType, papszOptions);
    4793             :     }
    4794             : 
    4795         956 :     if (eType == wkbTriangle && eTargetTypeFlat == wkbGeometryCollection)
    4796             :     {
    4797           1 :         OGRGeometryCollection *poGC = new OGRGeometryCollection();
    4798           1 :         poGC->assignSpatialReference(poGeom->getSpatialReference());
    4799           1 :         poGC->addGeometryDirectly(poGeom);
    4800           1 :         poGC->set3D(OGR_GT_HasZ(eTargetType));
    4801           1 :         poGC->setMeasured(OGR_GT_HasM(eTargetType));
    4802           1 :         return poGC;
    4803             :     }
    4804             : 
    4805             :     // Promote single to multi.
    4806        1645 :     if (!OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) &&
    4807         690 :         OGR_GT_IsSubClassOf(OGR_GT_GetCollection(eType), eTargetType))
    4808             :     {
    4809         197 :         OGRGeometry *poRet = createGeometry(eTargetType);
    4810         197 :         if (poRet == nullptr)
    4811             :         {
    4812           0 :             delete poGeom;
    4813           0 :             return nullptr;
    4814             :         }
    4815         197 :         poRet->assignSpatialReference(poGeom->getSpatialReference());
    4816         197 :         if (eType == wkbLineString)
    4817          42 :             poGeom = OGRCurve::CastToLineString(poGeom->toCurve());
    4818         197 :         poRet->toGeometryCollection()->addGeometryDirectly(poGeom);
    4819         197 :         poRet->set3D(OGR_GT_HasZ(eTargetType));
    4820         197 :         poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4821         197 :         return poRet;
    4822             :     }
    4823             : 
    4824         758 :     const bool bIsCurve = CPL_TO_BOOL(OGR_GT_IsCurve(eType));
    4825         758 :     if (bIsCurve && eTargetTypeFlat == wkbCompoundCurve)
    4826             :     {
    4827          30 :         auto poRet = OGRCurve::CastToCompoundCurve(poGeom->toCurve());
    4828          30 :         if (poRet)
    4829             :         {
    4830          28 :             poRet->set3D(OGR_GT_HasZ(eTargetType));
    4831          28 :             poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4832             :         }
    4833          30 :         return poRet;
    4834             :     }
    4835         728 :     else if (bIsCurve && eTargetTypeFlat == wkbCurvePolygon)
    4836             :     {
    4837          26 :         OGRCurve *poCurve = poGeom->toCurve();
    4838          26 :         if (poCurve->getNumPoints() >= 3 && poCurve->get_IsClosed())
    4839             :         {
    4840          18 :             OGRCurvePolygon *poCP = new OGRCurvePolygon();
    4841          18 :             if (poCP->addRingDirectly(poCurve) == OGRERR_NONE)
    4842             :             {
    4843          18 :                 poCP->assignSpatialReference(poGeom->getSpatialReference());
    4844          18 :                 poCP->set3D(OGR_GT_HasZ(eTargetType));
    4845          18 :                 poCP->setMeasured(OGR_GT_HasM(eTargetType));
    4846          18 :                 return poCP;
    4847             :             }
    4848             :             else
    4849             :             {
    4850           0 :                 delete poCP;
    4851             :             }
    4852           8 :         }
    4853             :     }
    4854         778 :     else if (eType == wkbLineString &&
    4855          76 :              OGR_GT_IsSubClassOf(eTargetType, wkbMultiSurface))
    4856             :     {
    4857          23 :         OGRGeometry *poTmp = forceTo(poGeom, wkbPolygon, papszOptions);
    4858          23 :         if (wkbFlatten(poTmp->getGeometryType()) != eType)
    4859          15 :             return forceTo(poTmp, eTargetType, papszOptions);
    4860             :     }
    4861         679 :     else if (bIsCurve && eTargetTypeFlat == wkbMultiSurface)
    4862             :     {
    4863          10 :         OGRGeometry *poTmp = forceTo(poGeom, wkbCurvePolygon, papszOptions);
    4864          10 :         if (wkbFlatten(poTmp->getGeometryType()) != eType)
    4865          10 :             return forceTo(poTmp, eTargetType, papszOptions);
    4866             :     }
    4867         669 :     else if (bIsCurve && eTargetTypeFlat == wkbMultiPolygon)
    4868             :     {
    4869          13 :         OGRGeometry *poTmp = forceTo(poGeom, wkbPolygon, papszOptions);
    4870          13 :         if (wkbFlatten(poTmp->getGeometryType()) != eType)
    4871          13 :             return forceTo(poTmp, eTargetType, papszOptions);
    4872             :     }
    4873         656 :     else if (eType == wkbTriangle && eTargetTypeFlat == wkbCurvePolygon)
    4874             :     {
    4875           1 :         auto poRet = OGRSurface::CastToCurvePolygon(
    4876             :             OGRTriangle::CastToPolygon(poGeom)->toSurface());
    4877           1 :         poRet->set3D(OGR_GT_HasZ(eTargetType));
    4878           1 :         poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4879           1 :         return poRet;
    4880             :     }
    4881         655 :     else if (eType == wkbPolygon && eTargetTypeFlat == wkbCurvePolygon)
    4882             :     {
    4883          18 :         auto poRet = OGRSurface::CastToCurvePolygon(poGeom->toPolygon());
    4884          18 :         poRet->set3D(OGR_GT_HasZ(eTargetType));
    4885          18 :         poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4886          18 :         return poRet;
    4887             :     }
    4888         637 :     else if (OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) &&
    4889             :              eTargetTypeFlat == wkbCompoundCurve)
    4890             :     {
    4891          15 :         OGRCurvePolygon *poPoly = poGeom->toCurvePolygon();
    4892          15 :         if (poPoly->getNumInteriorRings() == 0)
    4893             :         {
    4894          14 :             OGRCurve *poRet = poPoly->stealExteriorRingCurve();
    4895          14 :             if (poRet)
    4896          14 :                 poRet->assignSpatialReference(poGeom->getSpatialReference());
    4897          14 :             delete poPoly;
    4898          14 :             return forceTo(poRet, eTargetType, papszOptions);
    4899             :         }
    4900             :     }
    4901         622 :     else if (eType == wkbMultiPolygon && eTargetTypeFlat == wkbMultiSurface)
    4902             :     {
    4903             :         auto poRet =
    4904          10 :             OGRMultiPolygon::CastToMultiSurface(poGeom->toMultiPolygon());
    4905          10 :         poRet->set3D(OGR_GT_HasZ(eTargetType));
    4906          10 :         poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4907          10 :         return poRet;
    4908             :     }
    4909         612 :     else if (eType == wkbMultiLineString && eTargetTypeFlat == wkbMultiCurve)
    4910             :     {
    4911             :         auto poRet =
    4912           8 :             OGRMultiLineString::CastToMultiCurve(poGeom->toMultiLineString());
    4913           8 :         poRet->set3D(OGR_GT_HasZ(eTargetType));
    4914           8 :         poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4915           8 :         return poRet;
    4916             :     }
    4917         604 :     else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    4918             :     {
    4919         247 :         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    4920         247 :         if (poGC->getNumGeometries() == 1)
    4921             :         {
    4922         156 :             OGRGeometry *poSubGeom = poGC->getGeometryRef(0);
    4923         156 :             if (poSubGeom)
    4924             :             {
    4925         156 :                 poSubGeom->assignSpatialReference(
    4926         156 :                     poGeom->getSpatialReference());
    4927         156 :                 poGC->removeGeometry(0, FALSE);
    4928             :                 OGRGeometry *poRet =
    4929         156 :                     forceTo(poSubGeom->clone(), eTargetType, papszOptions);
    4930         156 :                 if (OGR_GT_IsSubClassOf(wkbFlatten(poRet->getGeometryType()),
    4931         156 :                                         eTargetType))
    4932             :                 {
    4933         121 :                     delete poGC;
    4934         121 :                     delete poSubGeom;
    4935         121 :                     return poRet;
    4936             :                 }
    4937          35 :                 poGC->addGeometryDirectly(poSubGeom);
    4938          35 :                 poRet->set3D(OGR_GT_HasZ(eTargetType));
    4939          35 :                 poRet->setMeasured(OGR_GT_HasM(eTargetType));
    4940          35 :                 delete poRet;
    4941             :             }
    4942             :         }
    4943             :     }
    4944         471 :     else if (OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) &&
    4945         114 :              (OGR_GT_IsSubClassOf(eTargetType, wkbMultiSurface) ||
    4946         102 :               OGR_GT_IsSubClassOf(eTargetType, wkbMultiCurve)))
    4947             :     {
    4948          43 :         OGRCurvePolygon *poCP = poGeom->toCurvePolygon();
    4949          43 :         if (poCP->getNumInteriorRings() == 0)
    4950             :         {
    4951          41 :             OGRCurve *poRing = poCP->getExteriorRingCurve();
    4952          41 :             poRing->assignSpatialReference(poGeom->getSpatialReference());
    4953          41 :             OGRwkbGeometryType eRingType = poRing->getGeometryType();
    4954          41 :             OGRGeometry *poRingDup = poRing->clone();
    4955          41 :             OGRGeometry *poRet = forceTo(poRingDup, eTargetType, papszOptions);
    4956          57 :             if (poRet->getGeometryType() != eRingType &&
    4957          16 :                 !(eTypeFlat == wkbPolygon &&
    4958             :                   eTargetTypeFlat == wkbMultiLineString))
    4959             :             {
    4960          29 :                 delete poCP;
    4961          29 :                 return poRet;
    4962             :             }
    4963             :             else
    4964             :             {
    4965          12 :                 delete poRet;
    4966             :             }
    4967             :         }
    4968             :     }
    4969             : 
    4970         475 :     if (eTargetTypeFlat == wkbLineString)
    4971             :     {
    4972          85 :         poGeom = forceToLineString(poGeom);
    4973          85 :         poGeom->set3D(OGR_GT_HasZ(eTargetType));
    4974          85 :         poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    4975             :     }
    4976         390 :     else if (eTargetTypeFlat == wkbPolygon)
    4977             :     {
    4978          99 :         poGeom = forceToPolygon(poGeom);
    4979          99 :         if (poGeom)
    4980             :         {
    4981          99 :             poGeom->set3D(OGR_GT_HasZ(eTargetType));
    4982          99 :             poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    4983             :         }
    4984             :     }
    4985         291 :     else if (eTargetTypeFlat == wkbMultiPolygon)
    4986             :     {
    4987         112 :         poGeom = forceToMultiPolygon(poGeom);
    4988         112 :         poGeom->set3D(OGR_GT_HasZ(eTargetType));
    4989         112 :         poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    4990             :     }
    4991         179 :     else if (eTargetTypeFlat == wkbMultiLineString)
    4992             :     {
    4993          37 :         poGeom = forceToMultiLineString(poGeom);
    4994          37 :         poGeom->set3D(OGR_GT_HasZ(eTargetType));
    4995          37 :         poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    4996             :     }
    4997         142 :     else if (eTargetTypeFlat == wkbMultiPoint)
    4998             :     {
    4999          22 :         poGeom = forceToMultiPoint(poGeom);
    5000          22 :         poGeom->set3D(OGR_GT_HasZ(eTargetType));
    5001          22 :         poGeom->setMeasured(OGR_GT_HasM(eTargetType));
    5002             :     }
    5003             : 
    5004         475 :     return poGeom;
    5005             : }
    5006             : 
    5007             : /************************************************************************/
    5008             : /*                          OGR_G_ForceTo()                             */
    5009             : /************************************************************************/
    5010             : 
    5011             : /**
    5012             :  * \brief Convert to another geometry type
    5013             :  *
    5014             :  * This function is the same as the C++ method OGRGeometryFactory::forceTo().
    5015             :  *
    5016             :  * @param hGeom the input geometry - ownership is passed to the method.
    5017             :  * @param eTargetType target output geometry type.
    5018             :  * @param papszOptions options as a null-terminated list of strings or NULL.
    5019             :  * @return new geometry.
    5020             :  *
    5021             :  * @since GDAL 2.0
    5022             :  */
    5023             : 
    5024         848 : OGRGeometryH OGR_G_ForceTo(OGRGeometryH hGeom, OGRwkbGeometryType eTargetType,
    5025             :                            char **papszOptions)
    5026             : 
    5027             : {
    5028         848 :     return OGRGeometry::ToHandle(OGRGeometryFactory::forceTo(
    5029         848 :         OGRGeometry::FromHandle(hGeom), eTargetType, papszOptions));
    5030             : }
    5031             : 
    5032             : /************************************************************************/
    5033             : /*                         GetCurveParameters()                          */
    5034             : /************************************************************************/
    5035             : 
    5036             : /**
    5037             :  * \brief Returns the parameter of an arc circle.
    5038             :  *
    5039             :  * Angles are return in radians, with trigonometic convention (counter clock
    5040             :  * wise)
    5041             :  *
    5042             :  * @param x0 x of first point
    5043             :  * @param y0 y of first point
    5044             :  * @param x1 x of intermediate point
    5045             :  * @param y1 y of intermediate point
    5046             :  * @param x2 x of final point
    5047             :  * @param y2 y of final point
    5048             :  * @param R radius (output)
    5049             :  * @param cx x of arc center (output)
    5050             :  * @param cy y of arc center (output)
    5051             :  * @param alpha0 angle between center and first point, in radians (output)
    5052             :  * @param alpha1 angle between center and intermediate point, in radians
    5053             :  * (output)
    5054             :  * @param alpha2 angle between center and final point, in radians (output)
    5055             :  * @return TRUE if the points are not aligned and define an arc circle.
    5056             :  *
    5057             :  * @since GDAL 2.0
    5058             :  */
    5059             : 
    5060      187552 : int OGRGeometryFactory::GetCurveParameters(double x0, double y0, double x1,
    5061             :                                            double y1, double x2, double y2,
    5062             :                                            double &R, double &cx, double &cy,
    5063             :                                            double &alpha0, double &alpha1,
    5064             :                                            double &alpha2)
    5065             : {
    5066      187552 :     if (CPLIsNan(x0) || CPLIsNan(y0) || CPLIsNan(x1) || CPLIsNan(y1) ||
    5067      187552 :         CPLIsNan(x2) || CPLIsNan(y2))
    5068             :     {
    5069           0 :         return FALSE;
    5070             :     }
    5071             : 
    5072             :     // Circle.
    5073      187552 :     if (x0 == x2 && y0 == y2)
    5074             :     {
    5075         153 :         if (x0 != x1 || y0 != y1)
    5076             :         {
    5077         152 :             cx = (x0 + x1) / 2;
    5078         152 :             cy = (y0 + y1) / 2;
    5079         152 :             R = DISTANCE(cx, cy, x0, y0);
    5080             :             // Arbitrarily pick counter-clock-wise order (like PostGIS does).
    5081         152 :             alpha0 = atan2(y0 - cy, x0 - cx);
    5082         152 :             alpha1 = alpha0 + M_PI;
    5083         152 :             alpha2 = alpha0 + 2 * M_PI;
    5084         152 :             return TRUE;
    5085             :         }
    5086             :         else
    5087             :         {
    5088           1 :             return FALSE;
    5089             :         }
    5090             :     }
    5091             : 
    5092      187399 :     double dx01 = x1 - x0;
    5093      187399 :     double dy01 = y1 - y0;
    5094      187399 :     double dx12 = x2 - x1;
    5095      187399 :     double dy12 = y2 - y1;
    5096             : 
    5097             :     // Normalize above values so as to make sure we don't end up with
    5098             :     // computing a difference of too big values.
    5099      187399 :     double dfScale = fabs(dx01);
    5100      187399 :     if (fabs(dy01) > dfScale)
    5101       93210 :         dfScale = fabs(dy01);
    5102      187399 :     if (fabs(dx12) > dfScale)
    5103       47004 :         dfScale = fabs(dx12);
    5104      187399 :     if (fabs(dy12) > dfScale)
    5105       46301 :         dfScale = fabs(dy12);
    5106      187399 :     const double dfInvScale = 1.0 / dfScale;
    5107      187399 :     dx01 *= dfInvScale;
    5108      187399 :     dy01 *= dfInvScale;
    5109      187399 :     dx12 *= dfInvScale;
    5110      187399 :     dy12 *= dfInvScale;
    5111             : 
    5112      187399 :     const double det = dx01 * dy12 - dx12 * dy01;
    5113      187399 :     if (fabs(det) < 1.0e-8 || CPLIsNan(det))
    5114             :     {
    5115         143 :         return FALSE;
    5116             :     }
    5117      187256 :     const double x01_mid = (x0 + x1) * dfInvScale;
    5118      187256 :     const double x12_mid = (x1 + x2) * dfInvScale;
    5119      187256 :     const double y01_mid = (y0 + y1) * dfInvScale;
    5120      187256 :     const double y12_mid = (y1 + y2) * dfInvScale;
    5121      187256 :     const double c01 = dx01 * x01_mid + dy01 * y01_mid;
    5122      187256 :     const double c12 = dx12 * x12_mid + dy12 * y12_mid;
    5123      187256 :     cx = 0.5 * dfScale * (c01 * dy12 - c12 * dy01) / det;
    5124      187256 :     cy = 0.5 * dfScale * (-c01 * dx12 + c12 * dx01) / det;
    5125             : 
    5126      187256 :     alpha0 = atan2((y0 - cy) * dfInvScale, (x0 - cx) * dfInvScale);
    5127      187256 :     alpha1 = atan2((y1 - cy) * dfInvScale, (x1 - cx) * dfInvScale);
    5128      187256 :     alpha2 = atan2((y2 - cy) * dfInvScale, (x2 - cx) * dfInvScale);
    5129      187256 :     R = DISTANCE(cx, cy, x0, y0);
    5130             : 
    5131             :     // If det is negative, the orientation if clockwise.
    5132      187256 :     if (det < 0)
    5133             :     {
    5134       95252 :         if (alpha1 > alpha0)
    5135        1331 :             alpha1 -= 2 * M_PI;
    5136       95252 :         if (alpha2 > alpha1)
    5137        3424 :             alpha2 -= 2 * M_PI;
    5138             :     }
    5139             :     else
    5140             :     {
    5141       92004 :         if (alpha1 < alpha0)
    5142        1308 :             alpha1 += 2 * M_PI;
    5143       92004 :         if (alpha2 < alpha1)
    5144        3136 :             alpha2 += 2 * M_PI;
    5145             :     }
    5146             : 
    5147      187256 :     CPLAssert((alpha0 <= alpha1 && alpha1 <= alpha2) ||
    5148             :               (alpha0 >= alpha1 && alpha1 >= alpha2));
    5149             : 
    5150      187256 :     return TRUE;
    5151             : }
    5152             : 
    5153             : /************************************************************************/
    5154             : /*                      OGRGeometryFactoryStrokeArc()                   */
    5155             : /************************************************************************/
    5156             : 
    5157        4307 : static void OGRGeometryFactoryStrokeArc(OGRLineString *poLine, double cx,
    5158             :                                         double cy, double R, double z0,
    5159             :                                         double z1, int bHasZ, double alpha0,
    5160             :                                         double alpha1, double dfStep,
    5161             :                                         int bStealthConstraints)
    5162             : {
    5163        4307 :     const int nSign = dfStep > 0 ? 1 : -1;
    5164             : 
    5165             :     // Constant angle between all points, so as to not depend on winding order.
    5166        4307 :     const double dfNumSteps = fabs((alpha1 - alpha0) / dfStep) + 0.5;
    5167        4307 :     if (dfNumSteps >= std::numeric_limits<int>::max() ||
    5168        4307 :         dfNumSteps <= std::numeric_limits<int>::min() || CPLIsNan(dfNumSteps))
    5169             :     {
    5170           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    5171             :                  "OGRGeometryFactoryStrokeArc: bogus steps: "
    5172             :                  "%lf %lf %lf %lf",
    5173             :                  alpha0, alpha1, dfStep, dfNumSteps);
    5174           0 :         return;
    5175             :     }
    5176             : 
    5177        4307 :     int nSteps = static_cast<int>(dfNumSteps);
    5178        4307 :     if (bStealthConstraints)
    5179             :     {
    5180             :         // We need at least 6 intermediate vertex, and if more additional
    5181             :         // multiples of 2.
    5182        4109 :         if (nSteps < 1 + 6)
    5183         104 :             nSteps = 1 + 6;
    5184             :         else
    5185        4005 :             nSteps = 1 + 6 + 2 * ((nSteps - (1 + 6) + (2 - 1)) / 2);
    5186             :     }
    5187         198 :     else if (nSteps < 4)
    5188             :     {
    5189         194 :         nSteps = 4;
    5190             :     }
    5191        4307 :     dfStep = nSign * fabs((alpha1 - alpha0) / nSteps);
    5192        4307 :     double alpha = alpha0 + dfStep;
    5193             : 
    5194      230429 :     for (; (alpha - alpha1) * nSign < -1e-8; alpha += dfStep)
    5195             :     {
    5196      226122 :         const double dfX = cx + R * cos(alpha);
    5197      226122 :         const double dfY = cy + R * sin(alpha);
    5198      226122 :         if (bHasZ)
    5199             :         {
    5200        9104 :             const double z =
    5201        9104 :                 z0 + (z1 - z0) * (alpha - alpha0) / (alpha1 - alpha0);
    5202        9104 :             poLine->addPoint(dfX, dfY, z);
    5203             :         }
    5204             :         else
    5205             :         {
    5206      217018 :             poLine->addPoint(dfX, dfY);
    5207             :         }
    5208             :     }
    5209             : }
    5210             : 
    5211             : /************************************************************************/
    5212             : /*                         OGRGF_SetHiddenValue()                       */
    5213             : /************************************************************************/
    5214             : 
    5215             : // TODO(schwehr): Cleanup these static constants.
    5216             : constexpr int HIDDEN_ALPHA_WIDTH = 32;
    5217             : constexpr GUInt32 HIDDEN_ALPHA_SCALE =
    5218             :     static_cast<GUInt32>((static_cast<GUIntBig>(1) << HIDDEN_ALPHA_WIDTH) - 2);
    5219             : constexpr int HIDDEN_ALPHA_HALF_WIDTH = (HIDDEN_ALPHA_WIDTH / 2);
    5220             : constexpr int HIDDEN_ALPHA_HALF_MASK = (1 << HIDDEN_ALPHA_HALF_WIDTH) - 1;
    5221             : 
    5222             : // Encode 16-bit nValue in the 8-lsb of dfX and dfY.
    5223             : 
    5224             : #ifdef CPL_LSB
    5225             : constexpr int DOUBLE_LSB_OFFSET = 0;
    5226             : #else
    5227             : constexpr int DOUBLE_LSB_OFFSET = 7;
    5228             : #endif
    5229             : 
    5230      225988 : static void OGRGF_SetHiddenValue(GUInt16 nValue, double &dfX, double &dfY)
    5231             : {
    5232      225988 :     GByte abyData[8] = {};
    5233             : 
    5234      225988 :     memcpy(abyData, &dfX, sizeof(double));
    5235      225988 :     abyData[DOUBLE_LSB_OFFSET] = static_cast<GByte>(nValue & 0xFF);
    5236      225988 :     memcpy(&dfX, abyData, sizeof(double));
    5237             : 
    5238      225988 :     memcpy(abyData, &dfY, sizeof(double));
    5239      225988 :     abyData[DOUBLE_LSB_OFFSET] = static_cast<GByte>(nValue >> 8);
    5240      225988 :     memcpy(&dfY, abyData, sizeof(double));
    5241      225988 : }
    5242             : 
    5243             : /************************************************************************/
    5244             : /*                         OGRGF_GetHiddenValue()                       */
    5245             : /************************************************************************/
    5246             : 
    5247             : // Decode 16-bit nValue from the 8-lsb of dfX and dfY.
    5248      182380 : static GUInt16 OGRGF_GetHiddenValue(double dfX, double dfY)
    5249             : {
    5250      182380 :     GByte abyData[8] = {};
    5251      182380 :     memcpy(abyData, &dfX, sizeof(double));
    5252      182380 :     GUInt16 nValue = abyData[DOUBLE_LSB_OFFSET];
    5253      182380 :     memcpy(abyData, &dfY, sizeof(double));
    5254      182380 :     nValue |= (abyData[DOUBLE_LSB_OFFSET] << 8);
    5255             : 
    5256      182380 :     return nValue;
    5257             : }
    5258             : 
    5259             : /************************************************************************/
    5260             : /*                     OGRGF_NeedSwithArcOrder()                        */
    5261             : /************************************************************************/
    5262             : 
    5263             : // We need to define a full ordering between starting point and ending point
    5264             : // whatever it is.
    5265        9432 : static bool OGRGF_NeedSwithArcOrder(double x0, double y0, double x2, double y2)
    5266             : {
    5267        9432 :     return x0 < x2 || (x0 == x2 && y0 < y2);
    5268             : }
    5269             : 
    5270             : /************************************************************************/
    5271             : /*                         curveToLineString()                          */
    5272             : /************************************************************************/
    5273             : 
    5274             : /* clang-format off */
    5275             : /**
    5276             :  * \brief Converts an arc circle into an approximate line string
    5277             :  *
    5278             :  * The arc circle is defined by a first point, an intermediate point and a
    5279             :  * final point.
    5280             :  *
    5281             :  * The provided dfMaxAngleStepSizeDegrees is a hint. The discretization
    5282             :  * algorithm may pick a slightly different value.
    5283             :  *
    5284             :  * So as to avoid gaps when rendering curve polygons that share common arcs,
    5285             :  * this method is guaranteed to return a line with reversed vertex if called
    5286             :  * with inverted first and final point, and identical intermediate point.
    5287             :  *
    5288             :  * @param x0 x of first point
    5289             :  * @param y0 y of first point
    5290             :  * @param z0 z of first point
    5291             :  * @param x1 x of intermediate point
    5292             :  * @param y1 y of intermediate point
    5293             :  * @param z1 z of intermediate point
    5294             :  * @param x2 x of final point
    5295             :  * @param y2 y of final point
    5296             :  * @param z2 z of final point
    5297             :  * @param bHasZ TRUE if z must be taken into account
    5298             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    5299             :  * arc, zero to use the default setting.
    5300             :  * @param papszOptions options as a null-terminated list of strings or NULL.
    5301             :  * Recognized options:
    5302             :  * <ul>
    5303             :  * <li>ADD_INTERMEDIATE_POINT=STEALTH/YES/NO (Default to STEALTH).
    5304             :  *         Determine if and how the intermediate point must be output in the
    5305             :  *         linestring.  If set to STEALTH, no explicit intermediate point is
    5306             :  *         added but its properties are encoded in low significant bits of
    5307             :  *         intermediate points and OGRGeometryFactory::curveFromLineString() can
    5308             :  *         decode them.  This is the best compromise for round-tripping in OGR
    5309             :  *         and better results with PostGIS
    5310             :  *         <a href="http://postgis.org/docs/ST_LineToCurve.html">ST_LineToCurve()</a>.
    5311             :  *         If set to YES, the intermediate point is explicitly added to the
    5312             :  *         linestring. If set to NO, the intermediate point is not explicitly
    5313             :  *         added.
    5314             :  * </li>
    5315             :  * </ul>
    5316             :  *
    5317             :  * @return the converted geometry (ownership to caller).
    5318             :  *
    5319             :  * @since GDAL 2.0
    5320             :  */
    5321             : /* clang-format on */
    5322             : 
    5323        6365 : OGRLineString *OGRGeometryFactory::curveToLineString(
    5324             :     double x0, double y0, double z0, double x1, double y1, double z1, double x2,
    5325             :     double y2, double z2, int bHasZ, double dfMaxAngleStepSizeDegrees,
    5326             :     const char *const *papszOptions)
    5327             : {
    5328             :     // So as to make sure the same curve followed in both direction results
    5329             :     // in perfectly(=binary identical) symmetrical points.
    5330        6365 :     if (OGRGF_NeedSwithArcOrder(x0, y0, x2, y2))
    5331             :     {
    5332             :         OGRLineString *poLS =
    5333        2156 :             curveToLineString(x2, y2, z2, x1, y1, z1, x0, y0, z0, bHasZ,
    5334             :                               dfMaxAngleStepSizeDegrees, papszOptions);
    5335        2156 :         poLS->reversePoints();
    5336        2156 :         return poLS;
    5337             :     }
    5338             : 
    5339        4209 :     double R = 0.0;
    5340        4209 :     double cx = 0.0;
    5341        4209 :     double cy = 0.0;
    5342        4209 :     double alpha0 = 0.0;
    5343        4209 :     double alpha1 = 0.0;
    5344        4209 :     double alpha2 = 0.0;
    5345             : 
    5346        4209 :     OGRLineString *poLine = new OGRLineString();
    5347        4209 :     bool bIsArc = true;
    5348        4209 :     if (!GetCurveParameters(x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1,
    5349             :                             alpha2))
    5350             :     {
    5351          97 :         bIsArc = false;
    5352          97 :         cx = 0.0;
    5353          97 :         cy = 0.0;
    5354          97 :         R = 0.0;
    5355          97 :         alpha0 = 0.0;
    5356          97 :         alpha1 = 0.0;
    5357          97 :         alpha2 = 0.0;
    5358             :     }
    5359             : 
    5360        4209 :     const int nSign = alpha1 >= alpha0 ? 1 : -1;
    5361             : 
    5362             :     // support default arc step setting.
    5363        4209 :     if (dfMaxAngleStepSizeDegrees < 1e-6)
    5364             :     {
    5365        4190 :         dfMaxAngleStepSizeDegrees = OGRGF_GetDefaultStepSize();
    5366             :     }
    5367             : 
    5368        4209 :     double dfStep = dfMaxAngleStepSizeDegrees / 180 * M_PI;
    5369        4209 :     if (dfStep <= 0.01 / 180 * M_PI)
    5370             :     {
    5371           0 :         CPLDebug("OGR", "Too small arc step size: limiting to 0.01 degree.");
    5372           0 :         dfStep = 0.01 / 180 * M_PI;
    5373             :     }
    5374             : 
    5375        4209 :     dfStep *= nSign;
    5376             : 
    5377        4209 :     if (bHasZ)
    5378         252 :         poLine->addPoint(x0, y0, z0);
    5379             :     else
    5380        3957 :         poLine->addPoint(x0, y0);
    5381             : 
    5382        4209 :     bool bAddIntermediatePoint = false;
    5383        4209 :     bool bStealth = true;
    5384        4215 :     for (const char *const *papszIter = papszOptions; papszIter && *papszIter;
    5385             :          papszIter++)
    5386             :     {
    5387           6 :         char *pszKey = nullptr;
    5388           6 :         const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
    5389           6 :         if (pszKey != nullptr && EQUAL(pszKey, "ADD_INTERMEDIATE_POINT"))
    5390             :         {
    5391           4 :             if (EQUAL(pszValue, "YES") || EQUAL(pszValue, "TRUE") ||
    5392           3 :                 EQUAL(pszValue, "ON"))
    5393             :             {
    5394           1 :                 bAddIntermediatePoint = true;
    5395           1 :                 bStealth = false;
    5396             :             }
    5397           3 :             else if (EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") ||
    5398           1 :                      EQUAL(pszValue, "OFF"))
    5399             :             {
    5400           2 :                 bAddIntermediatePoint = false;
    5401           2 :                 bStealth = false;
    5402             :             }
    5403             :             else if (EQUAL(pszValue, "STEALTH"))
    5404             :             {
    5405             :                 // default.
    5406             :             }
    5407             :         }
    5408             :         else
    5409             :         {
    5410           2 :             CPLError(CE_Warning, CPLE_NotSupported, "Unsupported option: %s",
    5411             :                      *papszIter);
    5412             :         }
    5413           6 :         CPLFree(pszKey);
    5414             :     }
    5415             : 
    5416        4209 :     if (!bIsArc || bAddIntermediatePoint)
    5417             :     {
    5418          98 :         OGRGeometryFactoryStrokeArc(poLine, cx, cy, R, z0, z1, bHasZ, alpha0,
    5419             :                                     alpha1, dfStep, FALSE);
    5420             : 
    5421          98 :         if (bHasZ)
    5422          25 :             poLine->addPoint(x1, y1, z1);
    5423             :         else
    5424          73 :             poLine->addPoint(x1, y1);
    5425             : 
    5426          98 :         OGRGeometryFactoryStrokeArc(poLine, cx, cy, R, z1, z2, bHasZ, alpha1,
    5427             :                                     alpha2, dfStep, FALSE);
    5428             :     }
    5429             :     else
    5430             :     {
    5431        4111 :         OGRGeometryFactoryStrokeArc(poLine, cx, cy, R, z0, z2, bHasZ, alpha0,
    5432             :                                     alpha2, dfStep, bStealth);
    5433             : 
    5434        4111 :         if (bStealth && poLine->getNumPoints() > 6)
    5435             :         {
    5436             :             // 'Hide' the angle of the intermediate point in the 8
    5437             :             // low-significant bits of the x, y of the first 2 computed points
    5438             :             // (so 32 bits), then put 0xFF, and on the last couple points put
    5439             :             // again the angle but in reverse order, so that overall the
    5440             :             // low-significant bits of all the points are symmetrical w.r.t the
    5441             :             // mid-point.
    5442        4109 :             const double dfRatio = (alpha1 - alpha0) / (alpha2 - alpha0);
    5443        4109 :             double dfAlphaRatio = 0.5 + HIDDEN_ALPHA_SCALE * dfRatio;
    5444        4109 :             if (dfAlphaRatio < 0.0)
    5445             :             {
    5446           0 :                 CPLError(CE_Warning, CPLE_AppDefined, "AlphaRation < 0: %lf",
    5447             :                          dfAlphaRatio);
    5448           0 :                 dfAlphaRatio *= -1;
    5449             :             }
    5450        8218 :             else if (dfAlphaRatio >= std::numeric_limits<GUInt32>::max() ||
    5451        4109 :                      CPLIsNan(dfAlphaRatio))
    5452             :             {
    5453           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    5454             :                          "AlphaRatio too large: %lf", dfAlphaRatio);
    5455           0 :                 dfAlphaRatio = std::numeric_limits<GUInt32>::max();
    5456             :             }
    5457        4109 :             const GUInt32 nAlphaRatio = static_cast<GUInt32>(dfAlphaRatio);
    5458        4109 :             const GUInt16 nAlphaRatioLow = nAlphaRatio & HIDDEN_ALPHA_HALF_MASK;
    5459        4109 :             const GUInt16 nAlphaRatioHigh =
    5460        4109 :                 nAlphaRatio >> HIDDEN_ALPHA_HALF_WIDTH;
    5461             :             // printf("alpha0=%f, alpha1=%f, alpha2=%f, dfRatio=%f, "/*ok*/
    5462             :             //        "nAlphaRatio = %u\n",
    5463             :             //        alpha0, alpha1, alpha2, dfRatio, nAlphaRatio);
    5464             : 
    5465        4109 :             CPLAssert(((poLine->getNumPoints() - 1 - 6) % 2) == 0);
    5466             : 
    5467      117103 :             for (int i = 1; i + 1 < poLine->getNumPoints(); i += 2)
    5468             :             {
    5469      112994 :                 GUInt16 nVal = 0xFFFF;
    5470             : 
    5471      112994 :                 double dfX = poLine->getX(i);
    5472      112994 :                 double dfY = poLine->getY(i);
    5473      112994 :                 if (i == 1)
    5474        4109 :                     nVal = nAlphaRatioLow;
    5475      108885 :                 else if (i == poLine->getNumPoints() - 2)
    5476        4109 :                     nVal = nAlphaRatioHigh;
    5477      112994 :                 OGRGF_SetHiddenValue(nVal, dfX, dfY);
    5478      112994 :                 poLine->setPoint(i, dfX, dfY);
    5479             : 
    5480      112994 :                 dfX = poLine->getX(i + 1);
    5481      112994 :                 dfY = poLine->getY(i + 1);
    5482      112994 :                 if (i == 1)
    5483        4109 :                     nVal = nAlphaRatioHigh;
    5484      108885 :                 else if (i == poLine->getNumPoints() - 2)
    5485        4109 :                     nVal = nAlphaRatioLow;
    5486      112994 :                 OGRGF_SetHiddenValue(nVal, dfX, dfY);
    5487      112994 :                 poLine->setPoint(i + 1, dfX, dfY);
    5488             :             }
    5489             :         }
    5490             :     }
    5491             : 
    5492        4209 :     if (bHasZ)
    5493         252 :         poLine->addPoint(x2, y2, z2);
    5494             :     else
    5495        3957 :         poLine->addPoint(x2, y2);
    5496             : 
    5497        4209 :     return poLine;
    5498             : }
    5499             : 
    5500             : /************************************************************************/
    5501             : /*                         OGRGF_FixAngle()                             */
    5502             : /************************************************************************/
    5503             : 
    5504             : // Fix dfAngle by offsets of 2 PI so that it lies between dfAngleStart and
    5505             : // dfAngleStop, whatever their respective order.
    5506      181205 : static double OGRGF_FixAngle(double dfAngleStart, double dfAngleStop,
    5507             :                              double dfAngle)
    5508             : {
    5509      181205 :     if (dfAngleStart < dfAngleStop)
    5510             :     {
    5511      124138 :         while (dfAngle <= dfAngleStart + 1e-8)
    5512       35045 :             dfAngle += 2 * M_PI;
    5513             :     }
    5514             :     else
    5515             :     {
    5516      126980 :         while (dfAngle >= dfAngleStart - 1e-8)
    5517       34868 :             dfAngle -= 2 * M_PI;
    5518             :     }
    5519      181205 :     return dfAngle;
    5520             : }
    5521             : 
    5522             : /************************************************************************/
    5523             : /*                         OGRGF_DetectArc()                            */
    5524             : /************************************************************************/
    5525             : 
    5526             : // #define VERBOSE_DEBUG_CURVEFROMLINESTRING
    5527             : 
    5528       12211 : static inline bool IS_ALMOST_INTEGER(double x)
    5529             : {
    5530       12211 :     const double val = fabs(x - floor(x + 0.5));
    5531       12211 :     return val < 1.0e-8;
    5532             : }
    5533             : 
    5534        3468 : static int OGRGF_DetectArc(const OGRLineString *poLS, int i,
    5535             :                            OGRCompoundCurve *&poCC, OGRCircularString *&poCS,
    5536             :                            OGRLineString *&poLSNew)
    5537             : {
    5538        3468 :     if (i + 3 >= poLS->getNumPoints())
    5539         305 :         return -1;
    5540             : 
    5541        6326 :     OGRPoint p0;
    5542        6326 :     OGRPoint p1;
    5543        6326 :     OGRPoint p2;
    5544        3163 :     poLS->getPoint(i, &p0);
    5545        3163 :     poLS->getPoint(i + 1, &p1);
    5546        3163 :     poLS->getPoint(i + 2, &p2);
    5547        3163 :     double R_1 = 0.0;
    5548        3163 :     double cx_1 = 0.0;
    5549        3163 :     double cy_1 = 0.0;
    5550        3163 :     double alpha0_1 = 0.0;
    5551        3163 :     double alpha1_1 = 0.0;
    5552        3163 :     double alpha2_1 = 0.0;
    5553        6319 :     if (!(OGRGeometryFactory::GetCurveParameters(
    5554             :               p0.getX(), p0.getY(), p1.getX(), p1.getY(), p2.getX(), p2.getY(),
    5555             :               R_1, cx_1, cy_1, alpha0_1, alpha1_1, alpha2_1) &&
    5556        3156 :           fabs(alpha2_1 - alpha0_1) < 2.0 * 20.0 / 180.0 * M_PI))
    5557             :     {
    5558          24 :         return -1;
    5559             :     }
    5560             : 
    5561        3139 :     const double dfDeltaAlpha10 = alpha1_1 - alpha0_1;
    5562        3139 :     const double dfDeltaAlpha21 = alpha2_1 - alpha1_1;
    5563             :     const double dfMaxDeltaAlpha =
    5564        3139 :         std::max(fabs(dfDeltaAlpha10), fabs(dfDeltaAlpha21));
    5565             :     GUInt32 nAlphaRatioRef =
    5566        3139 :         OGRGF_GetHiddenValue(p1.getX(), p1.getY()) |
    5567        3139 :         (OGRGF_GetHiddenValue(p2.getX(), p2.getY()) << HIDDEN_ALPHA_HALF_WIDTH);
    5568        3139 :     bool bFoundFFFFFFFFPattern = false;
    5569        3139 :     bool bFoundReversedAlphaRatioRef = false;
    5570        3139 :     bool bValidAlphaRatio = nAlphaRatioRef > 0 && nAlphaRatioRef < 0xFFFFFFFF;
    5571        3139 :     int nCountValidAlphaRatio = 1;
    5572             : 
    5573        3139 :     double dfScale = std::max(1.0, R_1);
    5574        3139 :     dfScale = std::max(dfScale, fabs(cx_1));
    5575        3139 :     dfScale = std::max(dfScale, fabs(cy_1));
    5576        3139 :     dfScale = pow(10.0, ceil(log10(dfScale)));
    5577        3139 :     const double dfInvScale = 1.0 / dfScale;
    5578             : 
    5579        3139 :     const int bInitialConstantStep =
    5580        3139 :         (fabs(dfDeltaAlpha10 - dfDeltaAlpha21) / dfMaxDeltaAlpha) < 1.0e-4;
    5581        3139 :     const double dfDeltaEpsilon =
    5582        3139 :         bInitialConstantStep ? dfMaxDeltaAlpha * 1e-4 : dfMaxDeltaAlpha / 10;
    5583             : 
    5584             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5585             :     printf("----------------------------\n");             /*ok*/
    5586             :     printf("Curve beginning at offset i = %d\n", i);      /*ok*/
    5587             :     printf("Initial alpha ratio = %u\n", nAlphaRatioRef); /*ok*/
    5588             :     /*ok*/ printf("Initial R = %.16g, cx = %.16g, cy = %.16g\n", R_1, cx_1,
    5589             :                   cy_1);
    5590             :     printf("dfScale = %f\n", dfScale);   /*ok*/
    5591             :     printf("bInitialConstantStep = %d, " /*ok*/
    5592             :            "fabs(dfDeltaAlpha10 - dfDeltaAlpha21)=%.8g, "
    5593             :            "dfMaxDeltaAlpha = %.8f, "
    5594             :            "dfDeltaEpsilon = %.8f (%.8f)\n",
    5595             :            bInitialConstantStep, fabs(dfDeltaAlpha10 - dfDeltaAlpha21),
    5596             :            dfMaxDeltaAlpha, dfDeltaEpsilon, 1.0 / 180.0 * M_PI);
    5597             : #endif
    5598        3139 :     int iMidPoint = -1;
    5599        3139 :     double dfLastValidAlpha = alpha2_1;
    5600             : 
    5601        3139 :     double dfLastLogRelDiff = 0;
    5602             : 
    5603        6278 :     OGRPoint p3;
    5604        3139 :     int j = i + 1;  // Used after for.
    5605      182753 :     for (; j + 2 < poLS->getNumPoints(); j++)
    5606             :     {
    5607      179713 :         poLS->getPoint(j, &p1);
    5608      179713 :         poLS->getPoint(j + 1, &p2);
    5609      179713 :         poLS->getPoint(j + 2, &p3);
    5610      179713 :         double R_2 = 0.0;
    5611      179713 :         double cx_2 = 0.0;
    5612      179713 :         double cy_2 = 0.0;
    5613      179713 :         double alpha0_2 = 0.0;
    5614      179713 :         double alpha1_2 = 0.0;
    5615      179713 :         double alpha2_2 = 0.0;
    5616             :         // Check that the new candidate arc shares the same
    5617             :         // radius, center and winding order.
    5618      179713 :         if (!(OGRGeometryFactory::GetCurveParameters(
    5619             :                 p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(),
    5620             :                 p3.getY(), R_2, cx_2, cy_2, alpha0_2, alpha1_2, alpha2_2)))
    5621             :         {
    5622             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5623             :             printf("End of curve at j=%d\n : straight line", j); /*ok*/
    5624             : #endif
    5625          99 :             break;
    5626             :         }
    5627             : 
    5628      179705 :         const double dfRelDiffR = fabs(R_1 - R_2) * dfInvScale;
    5629      179705 :         const double dfRelDiffCx = fabs(cx_1 - cx_2) * dfInvScale;
    5630      179705 :         const double dfRelDiffCy = fabs(cy_1 - cy_2) * dfInvScale;
    5631             : 
    5632             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5633             :         printf("j=%d: R = %.16g, cx = %.16g, cy = %.16g, " /*ok*/
    5634             :                "rel_diff_R=%.8g rel_diff_cx=%.8g rel_diff_cy=%.8g\n",
    5635             :                j, R_2, cx_2, cy_2, dfRelDiffR, dfRelDiffCx, dfRelDiffCy);
    5636             : #endif
    5637             : 
    5638      179705 :         if (dfRelDiffR > 1.0e-7 || dfRelDiffCx > 1.0e-7 ||
    5639      179636 :             dfRelDiffCy > 1.0e-7 ||
    5640      179636 :             dfDeltaAlpha10 * (alpha1_2 - alpha0_2) < 0.0)
    5641             :         {
    5642             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5643             :             printf("End of curve at j=%d\n", j); /*ok*/
    5644             : #endif
    5645             :             break;
    5646             :         }
    5647             : 
    5648      179636 :         if (dfRelDiffR > 0.0 && dfRelDiffCx > 0.0 && dfRelDiffCy > 0.0)
    5649             :         {
    5650             :             const double dfLogRelDiff = std::min(
    5651      359242 :                 std::min(fabs(log10(dfRelDiffR)), fabs(log10(dfRelDiffCx))),
    5652      179621 :                 fabs(log10(dfRelDiffCy)));
    5653             :             // printf("dfLogRelDiff = %f, dfLastLogRelDiff=%f, "/*ok*/
    5654             :             //        "dfLogRelDiff - dfLastLogRelDiff=%f\n",
    5655             :             //         dfLogRelDiff, dfLastLogRelDiff,
    5656             :             //         dfLogRelDiff - dfLastLogRelDiff);
    5657      179621 :             if (dfLogRelDiff > 0.0 && dfLastLogRelDiff >= 8.0 &&
    5658           2 :                 dfLogRelDiff <= 8.0 && dfLogRelDiff < dfLastLogRelDiff - 2.0)
    5659             :             {
    5660             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5661             :                 printf("End of curve at j=%d. Significant different in " /*ok*/
    5662             :                        "relative error w.r.t previous points\n",
    5663             :                        j);
    5664             : #endif
    5665           2 :                 break;
    5666             :             }
    5667      179619 :             dfLastLogRelDiff = dfLogRelDiff;
    5668             :         }
    5669             : 
    5670      179634 :         const double dfStep10 = fabs(alpha1_2 - alpha0_2);
    5671      179634 :         const double dfStep21 = fabs(alpha2_2 - alpha1_2);
    5672             :         // Check that the angle step is consistent with the original step.
    5673      179634 :         if (!(dfStep10 < 2.0 * dfMaxDeltaAlpha &&
    5674      179634 :               dfStep21 < 2.0 * dfMaxDeltaAlpha))
    5675             :         {
    5676             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5677             :             printf("End of curve at j=%d: dfStep10=%f, dfStep21=%f, " /*ok*/
    5678             :                    "2*dfMaxDeltaAlpha=%f\n",
    5679             :                    j, dfStep10, dfStep21, 2 * dfMaxDeltaAlpha);
    5680             : #endif
    5681             :             break;
    5682             :         }
    5683             : 
    5684      179633 :         if (bValidAlphaRatio && j > i + 1 && (i % 2) != (j % 2))
    5685             :         {
    5686             :             const GUInt32 nAlphaRatioReversed =
    5687       88051 :                 (OGRGF_GetHiddenValue(p1.getX(), p1.getY())
    5688      176102 :                  << HIDDEN_ALPHA_HALF_WIDTH) |
    5689       88051 :                 (OGRGF_GetHiddenValue(p2.getX(), p2.getY()));
    5690             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5691             :             printf("j=%d, nAlphaRatioReversed = %u\n", /*ok*/
    5692             :                    j, nAlphaRatioReversed);
    5693             : #endif
    5694       88051 :             if (!bFoundFFFFFFFFPattern && nAlphaRatioReversed == 0xFFFFFFFF)
    5695             :             {
    5696        3067 :                 bFoundFFFFFFFFPattern = true;
    5697        3067 :                 nCountValidAlphaRatio++;
    5698             :             }
    5699       84984 :             else if (bFoundFFFFFFFFPattern && !bFoundReversedAlphaRatioRef &&
    5700             :                      nAlphaRatioReversed == 0xFFFFFFFF)
    5701             :             {
    5702       81890 :                 nCountValidAlphaRatio++;
    5703             :             }
    5704        3094 :             else if (bFoundFFFFFFFFPattern && !bFoundReversedAlphaRatioRef &&
    5705             :                      nAlphaRatioReversed == nAlphaRatioRef)
    5706             :             {
    5707        3067 :                 bFoundReversedAlphaRatioRef = true;
    5708        3067 :                 nCountValidAlphaRatio++;
    5709             :             }
    5710             :             else
    5711             :             {
    5712          27 :                 if (bInitialConstantStep &&
    5713          26 :                     fabs(dfLastValidAlpha - alpha0_1) >= M_PI &&
    5714             :                     nCountValidAlphaRatio > 10)
    5715             :                 {
    5716             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5717             :                     printf("End of curve at j=%d: " /*ok*/
    5718             :                            "fabs(dfLastValidAlpha - alpha0_1)=%f, "
    5719             :                            "nCountValidAlphaRatio=%d\n",
    5720             :                            j, fabs(dfLastValidAlpha - alpha0_1),
    5721             :                            nCountValidAlphaRatio);
    5722             : #endif
    5723          19 :                     if (dfLastValidAlpha - alpha0_1 > 0)
    5724             :                     {
    5725          21 :                         while (dfLastValidAlpha - alpha0_1 - dfMaxDeltaAlpha -
    5726          14 :                                    M_PI >
    5727          14 :                                -dfMaxDeltaAlpha / 10)
    5728             :                         {
    5729           7 :                             dfLastValidAlpha -= dfMaxDeltaAlpha;
    5730           7 :                             j--;
    5731             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5732             :                             printf(/*ok*/
    5733             :                                    "--> corrected as fabs(dfLastValidAlpha - "
    5734             :                                    "alpha0_1)=%f, j=%d\n",
    5735             :                                    fabs(dfLastValidAlpha - alpha0_1), j);
    5736             : #endif
    5737             :                         }
    5738             :                     }
    5739             :                     else
    5740             :                     {
    5741          36 :                         while (dfLastValidAlpha - alpha0_1 + dfMaxDeltaAlpha +
    5742          24 :                                    M_PI <
    5743          24 :                                dfMaxDeltaAlpha / 10)
    5744             :                         {
    5745          12 :                             dfLastValidAlpha += dfMaxDeltaAlpha;
    5746          12 :                             j--;
    5747             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5748             :                             printf(/*ok*/
    5749             :                                    "--> corrected as fabs(dfLastValidAlpha - "
    5750             :                                    "alpha0_1)=%f, j=%d\n",
    5751             :                                    fabs(dfLastValidAlpha - alpha0_1), j);
    5752             : #endif
    5753             :                         }
    5754             :                     }
    5755          19 :                     poLS->getPoint(j + 1, &p2);
    5756          19 :                     break;
    5757             :                 }
    5758             : 
    5759             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5760             :                 printf("j=%d, nAlphaRatioReversed = %u --> inconsistent " /*ok*/
    5761             :                        "values across arc. Don't use it\n",
    5762             :                        j, nAlphaRatioReversed);
    5763             : #endif
    5764           8 :                 bValidAlphaRatio = false;
    5765             :             }
    5766             :         }
    5767             : 
    5768             :         // Correct current end angle, consistently with start angle.
    5769      179614 :         dfLastValidAlpha = OGRGF_FixAngle(alpha0_1, alpha1_1, alpha2_2);
    5770             : 
    5771             :         // Try to detect the precise intermediate point of the
    5772             :         // arc circle by detecting irregular angle step
    5773             :         // This is OK if we don't detect the right point or fail
    5774             :         // to detect it.
    5775             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5776             :         printf("j=%d A(0,1)-maxDelta=%.8f A(1,2)-maxDelta=%.8f " /*ok*/
    5777             :                "x1=%.8f y1=%.8f x2=%.8f y2=%.8f x3=%.8f y3=%.8f\n",
    5778             :                j, fabs(dfStep10 - dfMaxDeltaAlpha),
    5779             :                fabs(dfStep21 - dfMaxDeltaAlpha), p1.getX(), p1.getY(),
    5780             :                p2.getX(), p2.getY(), p3.getX(), p3.getY());
    5781             : #endif
    5782      179614 :         if (j > i + 1 && iMidPoint < 0 && dfDeltaEpsilon < 1.0 / 180.0 * M_PI)
    5783             :         {
    5784      176143 :             if (fabs(dfStep10 - dfMaxDeltaAlpha) > dfDeltaEpsilon)
    5785           8 :                 iMidPoint = j + ((bInitialConstantStep) ? 0 : 1);
    5786      176135 :             else if (fabs(dfStep21 - dfMaxDeltaAlpha) > dfDeltaEpsilon)
    5787           4 :                 iMidPoint = j + ((bInitialConstantStep) ? 1 : 2);
    5788             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5789             :             if (iMidPoint >= 0)
    5790             :             {
    5791             :                 OGRPoint pMid;
    5792             :                 poLS->getPoint(iMidPoint, &pMid);
    5793             :                 printf("Midpoint detected at j = %d, iMidPoint = %d, " /*ok*/
    5794             :                        "x=%.8f y=%.8f\n",
    5795             :                        j, iMidPoint, pMid.getX(), pMid.getY());
    5796             :             }
    5797             : #endif
    5798             :         }
    5799             :     }
    5800             : 
    5801             :     // Take a minimum threshold of consecutive points
    5802             :     // on the arc to avoid false positives.
    5803        3139 :     if (j < i + 3)
    5804          61 :         return -1;
    5805             : 
    5806        3078 :     bValidAlphaRatio &= bFoundFFFFFFFFPattern && bFoundReversedAlphaRatioRef;
    5807             : 
    5808             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5809             :     printf("bValidAlphaRatio=%d bFoundFFFFFFFFPattern=%d, " /*ok*/
    5810             :            "bFoundReversedAlphaRatioRef=%d\n",
    5811             :            static_cast<int>(bValidAlphaRatio),
    5812             :            static_cast<int>(bFoundFFFFFFFFPattern),
    5813             :            static_cast<int>(bFoundReversedAlphaRatioRef));
    5814             :     printf("alpha0_1=%f dfLastValidAlpha=%f\n", /*ok*/
    5815             :            alpha0_1, dfLastValidAlpha);
    5816             : #endif
    5817             : 
    5818        3078 :     if (poLSNew != nullptr)
    5819             :     {
    5820          11 :         double dfScale2 = std::max(1.0, fabs(p0.getX()));
    5821          11 :         dfScale2 = std::max(dfScale2, fabs(p0.getY()));
    5822             :         // Not strictly necessary, but helps having 'clean' lines without
    5823             :         // duplicated points.
    5824          11 :         constexpr double dfToleranceEps =
    5825             :             OGRCompoundCurve::DEFAULT_TOLERANCE_EPSILON;
    5826          11 :         if (fabs(poLSNew->getX(poLSNew->getNumPoints() - 1) - p0.getX()) >
    5827          12 :                 dfToleranceEps * dfScale2 ||
    5828           1 :             fabs(poLSNew->getY(poLSNew->getNumPoints() - 1) - p0.getY()) >
    5829           1 :                 dfToleranceEps * dfScale2)
    5830          10 :             poLSNew->addPoint(&p0);
    5831          11 :         if (poLSNew->getNumPoints() >= 2)
    5832             :         {
    5833          10 :             if (poCC == nullptr)
    5834           3 :                 poCC = new OGRCompoundCurve();
    5835          10 :             poCC->addCurveDirectly(poLSNew);
    5836             :         }
    5837             :         else
    5838           1 :             delete poLSNew;
    5839          11 :         poLSNew = nullptr;
    5840             :     }
    5841             : 
    5842        3078 :     if (poCS == nullptr)
    5843             :     {
    5844        3054 :         poCS = new OGRCircularString();
    5845        3054 :         poCS->addPoint(&p0);
    5846             :     }
    5847             : 
    5848        3078 :     OGRPoint *poFinalPoint = (j + 2 >= poLS->getNumPoints()) ? &p3 : &p2;
    5849             : 
    5850        3078 :     double dfXMid = 0.0;
    5851        3078 :     double dfYMid = 0.0;
    5852        3078 :     double dfZMid = 0.0;
    5853        3078 :     if (bValidAlphaRatio)
    5854             :     {
    5855             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5856             :         printf("Using alpha ratio...\n"); /*ok*/
    5857             : #endif
    5858        3067 :         double dfAlphaMid = 0.0;
    5859        3067 :         if (OGRGF_NeedSwithArcOrder(p0.getX(), p0.getY(), poFinalPoint->getX(),
    5860             :                                     poFinalPoint->getY()))
    5861             :         {
    5862             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5863             :             printf("Switching angles\n"); /*ok*/
    5864             : #endif
    5865        1543 :             dfAlphaMid = dfLastValidAlpha + nAlphaRatioRef *
    5866        1543 :                                                 (alpha0_1 - dfLastValidAlpha) /
    5867             :                                                 HIDDEN_ALPHA_SCALE;
    5868        1543 :             dfAlphaMid = OGRGF_FixAngle(alpha0_1, dfLastValidAlpha, dfAlphaMid);
    5869             :         }
    5870             :         else
    5871             :         {
    5872        1524 :             dfAlphaMid = alpha0_1 + nAlphaRatioRef *
    5873        1524 :                                         (dfLastValidAlpha - alpha0_1) /
    5874             :                                         HIDDEN_ALPHA_SCALE;
    5875             :         }
    5876             : 
    5877        3067 :         dfXMid = cx_1 + R_1 * cos(dfAlphaMid);
    5878        3067 :         dfYMid = cy_1 + R_1 * sin(dfAlphaMid);
    5879             : 
    5880        3067 :         if (poLS->getCoordinateDimension() == 3)
    5881             :         {
    5882           2 :             double dfLastAlpha = 0.0;
    5883           2 :             double dfLastZ = 0.0;
    5884           2 :             int k = i;  // Used after for.
    5885          48 :             for (; k < j + 2; k++)
    5886             :             {
    5887          48 :                 OGRPoint p;
    5888          48 :                 poLS->getPoint(k, &p);
    5889          48 :                 double dfAlpha = atan2(p.getY() - cy_1, p.getX() - cx_1);
    5890          48 :                 dfAlpha = OGRGF_FixAngle(alpha0_1, dfLastValidAlpha, dfAlpha);
    5891          48 :                 if (k > i &&
    5892          46 :                     ((dfAlpha < dfLastValidAlpha && dfAlphaMid < dfAlpha) ||
    5893          23 :                      (dfAlpha > dfLastValidAlpha && dfAlphaMid > dfAlpha)))
    5894             :                 {
    5895           2 :                     const double dfRatio =
    5896           2 :                         (dfAlphaMid - dfLastAlpha) / (dfAlpha - dfLastAlpha);
    5897           2 :                     dfZMid = (1 - dfRatio) * dfLastZ + dfRatio * p.getZ();
    5898           2 :                     break;
    5899             :                 }
    5900          46 :                 dfLastAlpha = dfAlpha;
    5901          46 :                 dfLastZ = p.getZ();
    5902             :             }
    5903           2 :             if (k == j + 2)
    5904           0 :                 dfZMid = dfLastZ;
    5905           2 :             if (IS_ALMOST_INTEGER(dfZMid))
    5906           2 :                 dfZMid = static_cast<int>(floor(dfZMid + 0.5));
    5907             :         }
    5908             : 
    5909             :         // A few rounding strategies in case the mid point was at "exact"
    5910             :         // coordinates.
    5911        3067 :         if (R_1 > 1e-5)
    5912             :         {
    5913             :             const bool bStartEndInteger =
    5914        9161 :                 IS_ALMOST_INTEGER(p0.getX()) && IS_ALMOST_INTEGER(p0.getY()) &&
    5915        9161 :                 IS_ALMOST_INTEGER(poFinalPoint->getX()) &&
    5916        3048 :                 IS_ALMOST_INTEGER(poFinalPoint->getY());
    5917        3061 :             if (bStartEndInteger &&
    5918        3048 :                 fabs(dfXMid - floor(dfXMid + 0.5)) / dfScale < 1e-4 &&
    5919        3029 :                 fabs(dfYMid - floor(dfYMid + 0.5)) / dfScale < 1e-4)
    5920             :             {
    5921        3029 :                 dfXMid = static_cast<int>(floor(dfXMid + 0.5));
    5922        3029 :                 dfYMid = static_cast<int>(floor(dfYMid + 0.5));
    5923             :                 // Sometimes rounding to closest is not best approach
    5924             :                 // Try neighbouring integers to look for the one that
    5925             :                 // minimize the error w.r.t to the arc center
    5926             :                 // But only do that if the radius is greater than
    5927             :                 // the magnitude of the delta that we will try!
    5928             :                 double dfBestRError =
    5929        3029 :                     fabs(R_1 - DISTANCE(dfXMid, dfYMid, cx_1, cy_1));
    5930             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5931             :                 printf("initial_error=%f\n", dfBestRError); /*ok*/
    5932             : #endif
    5933        3029 :                 int iBestX = 0;
    5934        3029 :                 int iBestY = 0;
    5935        3029 :                 if (dfBestRError > 0.001 && R_1 > 2)
    5936             :                 {
    5937           3 :                     int nSearchRadius = 1;
    5938             :                     // Extend the search radius if the arc circle radius
    5939             :                     // is much higher than the coordinate values.
    5940             :                     double dfMaxCoords =
    5941           3 :                         std::max(fabs(p0.getX()), fabs(p0.getY()));
    5942           3 :                     dfMaxCoords = std::max(dfMaxCoords, poFinalPoint->getX());
    5943           3 :                     dfMaxCoords = std::max(dfMaxCoords, poFinalPoint->getY());
    5944           3 :                     dfMaxCoords = std::max(dfMaxCoords, dfXMid);
    5945           3 :                     dfMaxCoords = std::max(dfMaxCoords, dfYMid);
    5946           3 :                     if (R_1 > dfMaxCoords * 1000)
    5947           3 :                         nSearchRadius = 100;
    5948           0 :                     else if (R_1 > dfMaxCoords * 10)
    5949           0 :                         nSearchRadius = 10;
    5950         606 :                     for (int iY = -nSearchRadius; iY <= nSearchRadius; iY++)
    5951             :                     {
    5952      121806 :                         for (int iX = -nSearchRadius; iX <= nSearchRadius; iX++)
    5953             :                         {
    5954      121203 :                             const double dfCandidateX = dfXMid + iX;
    5955      121203 :                             const double dfCandidateY = dfYMid + iY;
    5956      121203 :                             if (fabs(dfCandidateX - p0.getX()) < 1e-8 &&
    5957           0 :                                 fabs(dfCandidateY - p0.getY()) < 1e-8)
    5958           0 :                                 continue;
    5959      121203 :                             if (fabs(dfCandidateX - poFinalPoint->getX()) <
    5960      121203 :                                     1e-8 &&
    5961           0 :                                 fabs(dfCandidateY - poFinalPoint->getY()) <
    5962             :                                     1e-8)
    5963           0 :                                 continue;
    5964             :                             const double dfRError =
    5965      121203 :                                 fabs(R_1 - DISTANCE(dfCandidateX, dfCandidateY,
    5966      121203 :                                                     cx_1, cy_1));
    5967             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    5968             :                             printf("x=%d y=%d error=%f besterror=%f\n", /*ok*/
    5969             :                                    static_cast<int>(dfXMid + iX),
    5970             :                                    static_cast<int>(dfYMid + iY), dfRError,
    5971             :                                    dfBestRError);
    5972             : #endif
    5973      121203 :                             if (dfRError < dfBestRError)
    5974             :                             {
    5975          20 :                                 iBestX = iX;
    5976          20 :                                 iBestY = iY;
    5977          20 :                                 dfBestRError = dfRError;
    5978             :                             }
    5979             :                         }
    5980             :                     }
    5981             :                 }
    5982        3029 :                 dfXMid += iBestX;
    5983        3029 :                 dfYMid += iBestY;
    5984             :             }
    5985             :             else
    5986             :             {
    5987             :                 // Limit the number of significant figures in decimal
    5988             :                 // representation.
    5989          32 :                 if (fabs(dfXMid) < 100000000.0)
    5990             :                 {
    5991          32 :                     dfXMid =
    5992          32 :                         static_cast<GIntBig>(floor(dfXMid * 100000000 + 0.5)) /
    5993             :                         100000000.0;
    5994             :                 }
    5995          32 :                 if (fabs(dfYMid) < 100000000.0)
    5996             :                 {
    5997          32 :                     dfYMid =
    5998          32 :                         static_cast<GIntBig>(floor(dfYMid * 100000000 + 0.5)) /
    5999             :                         100000000.0;
    6000             :                 }
    6001             :             }
    6002             :         }
    6003             : 
    6004             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    6005             :         printf("dfAlphaMid=%f, x_mid = %f, y_mid = %f\n", /*ok*/
    6006             :                dfLastValidAlpha, dfXMid, dfYMid);
    6007             : #endif
    6008             :     }
    6009             : 
    6010             :     // If this is a full circle of a non-polygonal zone, we must
    6011             :     // use a 5-point representation to keep the winding order.
    6012        3089 :     if (p0.Equals(poFinalPoint) &&
    6013          11 :         !EQUAL(poLS->getGeometryName(), "LINEARRING"))
    6014             :     {
    6015             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    6016             :         printf("Full circle of a non-polygonal zone\n"); /*ok*/
    6017             : #endif
    6018           1 :         poLS->getPoint((i + j + 2) / 4, &p1);
    6019           1 :         poCS->addPoint(&p1);
    6020           1 :         if (bValidAlphaRatio)
    6021             :         {
    6022           1 :             p1.setX(dfXMid);
    6023           1 :             p1.setY(dfYMid);
    6024           1 :             if (poLS->getCoordinateDimension() == 3)
    6025           0 :                 p1.setZ(dfZMid);
    6026             :         }
    6027             :         else
    6028             :         {
    6029           0 :             poLS->getPoint((i + j + 1) / 2, &p1);
    6030             :         }
    6031           1 :         poCS->addPoint(&p1);
    6032           1 :         poLS->getPoint(3 * (i + j + 2) / 4, &p1);
    6033           1 :         poCS->addPoint(&p1);
    6034             :     }
    6035             : 
    6036        3077 :     else if (bValidAlphaRatio)
    6037             :     {
    6038        3066 :         p1.setX(dfXMid);
    6039        3066 :         p1.setY(dfYMid);
    6040        3066 :         if (poLS->getCoordinateDimension() == 3)
    6041           2 :             p1.setZ(dfZMid);
    6042        3066 :         poCS->addPoint(&p1);
    6043             :     }
    6044             : 
    6045             :     // If we have found a candidate for a precise intermediate
    6046             :     // point, use it.
    6047          11 :     else if (iMidPoint >= 1 && iMidPoint < j)
    6048             :     {
    6049           3 :         poLS->getPoint(iMidPoint, &p1);
    6050           3 :         poCS->addPoint(&p1);
    6051             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    6052             :         printf("Using detected midpoint...\n");                   /*ok*/
    6053             :         printf("x_mid = %f, y_mid = %f\n", p1.getX(), p1.getY()); /*ok*/
    6054             : #endif
    6055             :     }
    6056             :     // Otherwise pick up the mid point between both extremities.
    6057             :     else
    6058             :     {
    6059           8 :         poLS->getPoint((i + j + 1) / 2, &p1);
    6060           8 :         poCS->addPoint(&p1);
    6061             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    6062             :         printf("Pickup 'random' midpoint at index=%d...\n", /*ok*/
    6063             :                (i + j + 1) / 2);
    6064             :         printf("x_mid = %f, y_mid = %f\n", p1.getX(), p1.getY()); /*ok*/
    6065             : #endif
    6066             :     }
    6067        3078 :     poCS->addPoint(poFinalPoint);
    6068             : 
    6069             : #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
    6070             :     printf("----------------------------\n"); /*ok*/
    6071             : #endif
    6072             : 
    6073        3078 :     if (j + 2 >= poLS->getNumPoints())
    6074        3040 :         return -2;
    6075          38 :     return j + 1;
    6076             : }
    6077             : 
    6078             : /************************************************************************/
    6079             : /*                        curveFromLineString()                         */
    6080             : /************************************************************************/
    6081             : 
    6082             : /**
    6083             :  * \brief Try to convert a linestring approximating curves into a curve.
    6084             :  *
    6085             :  * This method can return a COMPOUNDCURVE, a CIRCULARSTRING or a LINESTRING.
    6086             :  *
    6087             :  * This method is the reverse of curveFromLineString().
    6088             :  *
    6089             :  * @param poLS handle to the geometry to convert.
    6090             :  * @param papszOptions options as a null-terminated list of strings.
    6091             :  *                     Unused for now. Must be set to NULL.
    6092             :  *
    6093             :  * @return the converted geometry (ownership to caller).
    6094             :  *
    6095             :  * @since GDAL 2.0
    6096             :  */
    6097             : 
    6098        3190 : OGRCurve *OGRGeometryFactory::curveFromLineString(
    6099             :     const OGRLineString *poLS, CPL_UNUSED const char *const *papszOptions)
    6100             : {
    6101        3190 :     OGRCompoundCurve *poCC = nullptr;
    6102        3190 :     OGRCircularString *poCS = nullptr;
    6103        3190 :     OGRLineString *poLSNew = nullptr;
    6104        3190 :     const int nLSNumPoints = poLS->getNumPoints();
    6105        3190 :     const bool bIsClosed = nLSNumPoints >= 4 && poLS->get_IsClosed();
    6106        3618 :     for (int i = 0; i < nLSNumPoints; /* nothing */)
    6107             :     {
    6108        3468 :         const int iNewI = OGRGF_DetectArc(poLS, i, poCC, poCS, poLSNew);
    6109        3468 :         if (iNewI == -2)
    6110        3040 :             break;
    6111         428 :         if (iNewI >= 0)
    6112             :         {
    6113          38 :             i = iNewI;
    6114          38 :             continue;
    6115             :         }
    6116             : 
    6117         390 :         if (poCS != nullptr)
    6118             :         {
    6119          14 :             if (poCC == nullptr)
    6120           5 :                 poCC = new OGRCompoundCurve();
    6121          14 :             poCC->addCurveDirectly(poCS);
    6122          14 :             poCS = nullptr;
    6123             :         }
    6124             : 
    6125         390 :         OGRPoint p;
    6126         390 :         poLS->getPoint(i, &p);
    6127         390 :         if (poLSNew == nullptr)
    6128             :         {
    6129         160 :             poLSNew = new OGRLineString();
    6130         160 :             poLSNew->addPoint(&p);
    6131             :         }
    6132             :         // Not strictly necessary, but helps having 'clean' lines without
    6133             :         // duplicated points.
    6134             :         else
    6135             :         {
    6136         230 :             double dfScale = std::max(1.0, fabs(p.getX()));
    6137         230 :             dfScale = std::max(dfScale, fabs(p.getY()));
    6138         230 :             if (bIsClosed && i == nLSNumPoints - 1)
    6139           7 :                 dfScale = 0;
    6140         230 :             constexpr double dfToleranceEps =
    6141             :                 OGRCompoundCurve::DEFAULT_TOLERANCE_EPSILON;
    6142         230 :             if (fabs(poLSNew->getX(poLSNew->getNumPoints() - 1) - p.getX()) >
    6143         239 :                     dfToleranceEps * dfScale ||
    6144           9 :                 fabs(poLSNew->getY(poLSNew->getNumPoints() - 1) - p.getY()) >
    6145           9 :                     dfToleranceEps * dfScale)
    6146             :             {
    6147         229 :                 poLSNew->addPoint(&p);
    6148             :             }
    6149             :         }
    6150             : 
    6151         390 :         i++;
    6152             :     }
    6153             : 
    6154        3190 :     OGRCurve *poRet = nullptr;
    6155             : 
    6156        3190 :     if (poLSNew != nullptr && poLSNew->getNumPoints() < 2)
    6157             :     {
    6158           1 :         delete poLSNew;
    6159           1 :         poLSNew = nullptr;
    6160           1 :         if (poCC != nullptr)
    6161             :         {
    6162           1 :             if (poCC->getNumCurves() == 1)
    6163             :             {
    6164           1 :                 poRet = poCC->stealCurve(0);
    6165           1 :                 delete poCC;
    6166           1 :                 poCC = nullptr;
    6167             :             }
    6168             :             else
    6169           0 :                 poRet = poCC;
    6170             :         }
    6171             :         else
    6172           0 :             poRet = poLS->clone();
    6173             :     }
    6174        3189 :     else if (poCC != nullptr)
    6175             :     {
    6176           7 :         if (poLSNew)
    6177           6 :             poCC->addCurveDirectly(poLSNew);
    6178             :         else
    6179           1 :             poCC->addCurveDirectly(poCS);
    6180           7 :         poRet = poCC;
    6181             :     }
    6182        3182 :     else if (poLSNew != nullptr)
    6183         142 :         poRet = poLSNew;
    6184        3040 :     else if (poCS != nullptr)
    6185        3039 :         poRet = poCS;
    6186             :     else
    6187           1 :         poRet = poLS->clone();
    6188             : 
    6189        3190 :     poRet->assignSpatialReference(poLS->getSpatialReference());
    6190             : 
    6191        3190 :     return poRet;
    6192             : }
    6193             : 
    6194             : /************************************************************************/
    6195             : /*                   createFromGeoJson( const char* )                   */
    6196             : /************************************************************************/
    6197             : 
    6198             : /**
    6199             :  * @brief Create geometry from GeoJson fragment.
    6200             :  * @param pszJsonString The GeoJSON fragment for the geometry.
    6201             :  * @param nSize (new in GDAL 3.4) Optional length of the string
    6202             :  *              if it is not null-terminated
    6203             :  * @return a geometry on success, or NULL on error.
    6204             :  * @since GDAL 2.3
    6205             :  */
    6206           0 : OGRGeometry *OGRGeometryFactory::createFromGeoJson(const char *pszJsonString,
    6207             :                                                    int nSize)
    6208             : {
    6209           0 :     CPLJSONDocument oDocument;
    6210           0 :     if (!oDocument.LoadMemory(reinterpret_cast<const GByte *>(pszJsonString),
    6211             :                               nSize))
    6212             :     {
    6213           0 :         return nullptr;
    6214             :     }
    6215             : 
    6216           0 :     return createFromGeoJson(oDocument.GetRoot());
    6217             : }
    6218             : 
    6219             : /************************************************************************/
    6220             : /*              createFromGeoJson( const CPLJSONObject& )               */
    6221             : /************************************************************************/
    6222             : 
    6223             : /**
    6224             :  * @brief Create geometry from GeoJson fragment.
    6225             :  * @param oJsonObject The JSONObject class describes the GeoJSON geometry.
    6226             :  * @return a geometry on success, or NULL on error.
    6227             :  * @since GDAL 2.3
    6228             :  */
    6229             : OGRGeometry *
    6230           0 : OGRGeometryFactory::createFromGeoJson(const CPLJSONObject &oJsonObject)
    6231             : {
    6232           0 :     if (!oJsonObject.IsValid())
    6233             :     {
    6234           0 :         return nullptr;
    6235             :     }
    6236             : 
    6237             :     // TODO: Move from GeoJSON driver functions create geometry here, and
    6238             :     // replace json-c specific json_object to CPLJSONObject
    6239           0 :     return OGRGeoJSONReadGeometry(
    6240           0 :         static_cast<json_object *>(oJsonObject.GetInternalHandle()));
    6241             : }

Generated by: LCOV version 1.14