LCOV - code coverage report
Current view: top level - ogr - ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1930 2226 86.7 %
Date: 2026-07-16 12:28:33 Functions: 223 247 90.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements a few base methods on OGRGeometry.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_geometry.h"
      16             : 
      17             : #include <climits>
      18             : #include <cstdarg>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <limits>
      24             : #include <memory>
      25             : #include <optional>
      26             : #include <stdexcept>
      27             : #include <string>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_error_internal.h"
      32             : #include "cpl_multiproc.h"
      33             : #include "cpl_string.h"
      34             : #include "ogr_api.h"
      35             : #include "ogr_core.h"
      36             : #include "ogr_geos.h"
      37             : #include "ogr_sfcgal.h"
      38             : #include "ogr_libs.h"
      39             : #include "ogr_p.h"
      40             : #include "ogr_spatialref.h"
      41             : #include "ogr_srs_api.h"
      42             : #include "ogr_wkb.h"
      43             : 
      44             : #ifndef SFCGAL_MAKE_VERSION
      45             : #define SFCGAL_MAKE_VERSION(major, minor, patch)                               \
      46             :     ((major) * 10000 + (minor) * 100 + (patch))
      47             : #endif
      48             : #ifndef SFCGAL_VERSION_NUM
      49             : #define SFCGAL_VERSION_NUM                                                     \
      50             :     SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR,            \
      51             :                         SFCGAL_VERSION_PATCH)
      52             : #endif
      53             : 
      54             : //! @cond Doxygen_Suppress
      55             : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
      56             : //! @endcond
      57             : 
      58             : #ifdef HAVE_GEOS
      59          99 : static void OGRGEOSErrorHandler(const char *fmt, ...)
      60             : {
      61             :     va_list args;
      62             : 
      63          99 :     va_start(args, fmt);
      64          99 :     CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
      65          99 :     va_end(args);
      66          99 : }
      67             : 
      68         111 : static void OGRGEOSWarningHandler(const char *fmt, ...)
      69             : {
      70             :     va_list args;
      71             : 
      72         111 :     va_start(args, fmt);
      73         111 :     CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
      74         111 :     va_end(args);
      75         111 : }
      76             : #endif
      77             : 
      78             : /************************************************************************/
      79             : /*                           OGRWktOptions()                            */
      80             : /************************************************************************/
      81             : 
      82       11308 : int OGRWktOptions::getDefaultPrecision()
      83             : {
      84       11308 :     return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
      85             : }
      86             : 
      87       11404 : bool OGRWktOptions::getDefaultRound()
      88             : {
      89       11404 :     return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                            OGRGeometry()                             */
      94             : /************************************************************************/
      95             : 
      96             : OGRGeometry::OGRGeometry() = default;
      97             : 
      98             : /************************************************************************/
      99             : /*                  OGRGeometry( const OGRGeometry& )                   */
     100             : /************************************************************************/
     101             : 
     102             : /**
     103             :  * \brief Copy constructor.
     104             :  */
     105             : 
     106     1803940 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
     107     1803940 :     : poSRS(other.poSRS), flags(other.flags)
     108             : {
     109     1803940 :     if (poSRS != nullptr)
     110       81294 :         const_cast<OGRSpatialReference *>(poSRS)->Reference();
     111     1803940 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                     OGRGeometry( OGRGeometry&& )                     */
     115             : /************************************************************************/
     116             : 
     117             : /**
     118             :  * \brief Move constructor.
     119             :  *
     120             :  * @since GDAL 3.11
     121             :  */
     122             : 
     123      156330 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
     124      156330 :     : poSRS(other.poSRS), flags(other.flags)
     125             : {
     126      156330 :     other.poSRS = nullptr;
     127      156330 : }
     128             : 
     129             : /************************************************************************/
     130             : /*                            ~OGRGeometry()                            */
     131             : /************************************************************************/
     132             : 
     133    25798500 : OGRGeometry::~OGRGeometry()
     134             : 
     135             : {
     136    12899200 :     if (poSRS != nullptr)
     137     3655700 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     138    12899200 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                    operator=( const OGRGeometry&)                    */
     142             : /************************************************************************/
     143             : 
     144             : /**
     145             :  * \brief Assignment operator.
     146             :  */
     147             : 
     148        1211 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
     149             : {
     150        1211 :     if (this != &other)
     151             :     {
     152        1211 :         empty();
     153        1211 :         assignSpatialReference(other.getSpatialReference());
     154        1211 :         flags = other.flags;
     155             :     }
     156        1211 :     return *this;
     157             : }
     158             : 
     159             : /************************************************************************/
     160             : /*                      operator=( OGRGeometry&&)                       */
     161             : /************************************************************************/
     162             : 
     163             : /**
     164             :  * \brief Move assignment operator.
     165             :  *
     166             :  * @since GDAL 3.11
     167             :  */
     168             : 
     169      103938 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
     170             : {
     171      103938 :     if (this != &other)
     172             :     {
     173      103938 :         poSRS = other.poSRS;
     174      103938 :         other.poSRS = nullptr;
     175      103938 :         flags = other.flags;
     176             :     }
     177      103938 :     return *this;
     178             : }
     179             : 
     180             : /************************************************************************/
     181             : /*                            dumpReadable()                            */
     182             : /************************************************************************/
     183             : 
     184             : /**
     185             :  * \brief Dump geometry in well known text format to indicated output file.
     186             :  *
     187             :  * A few options can be defined to change the default dump :
     188             :  * <ul>
     189             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     190             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     191             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     192             :  * </ul>
     193             :  *
     194             :  * This method is the same as the C function OGR_G_DumpReadable().
     195             :  *
     196             :  * @param fp the text file to write the geometry to.
     197             :  * @param pszPrefix the prefix to put on each line of output.
     198             :  * @param papszOptions NULL terminated list of options (may be NULL)
     199             :  */
     200             : 
     201           0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
     202             :                                CSLConstList papszOptions) const
     203             : 
     204             : {
     205           0 :     if (fp == nullptr)
     206           0 :         fp = stdout;
     207             : 
     208           0 :     const auto osStr = dumpReadable(pszPrefix, papszOptions);
     209           0 :     fprintf(fp, "%s", osStr.c_str());
     210           0 : }
     211             : 
     212             : /************************************************************************/
     213             : /*                            dumpReadable()                            */
     214             : /************************************************************************/
     215             : 
     216             : /**
     217             :  * \brief Dump geometry in well known text format to indicated output file.
     218             :  *
     219             :  * A few options can be defined to change the default dump :
     220             :  * <ul>
     221             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     222             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     223             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     224             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
     225             :  * in WKT (added in GDAL 3.9)</li>
     226             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
     227             :  * WKT (added in GDAL 3.9)</li>
     228             :  * </ul>
     229             :  *
     230             :  * @param pszPrefix the prefix to put on each line of output.
     231             :  * @param papszOptions NULL terminated list of options (may be NULL)
     232             :  * @return a string with the geometry representation.
     233             :  * @since GDAL 3.7
     234             :  */
     235             : 
     236         317 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
     237             :                                       CSLConstList papszOptions) const
     238             : 
     239             : {
     240         317 :     if (pszPrefix == nullptr)
     241         306 :         pszPrefix = "";
     242             : 
     243         317 :     std::string osRet;
     244             : 
     245             :     const auto exportToWktWithOpts =
     246        2044 :         [this, pszPrefix, papszOptions, &osRet](bool bIso)
     247             :     {
     248         292 :         OGRErr err(OGRERR_NONE);
     249         292 :         OGRWktOptions opts;
     250         292 :         if (const char *pszXYPrecision =
     251         292 :                 CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
     252             :         {
     253           1 :             opts.format = OGRWktFormat::F;
     254           1 :             opts.xyPrecision = atoi(pszXYPrecision);
     255             :         }
     256         292 :         if (const char *pszZPrecision =
     257         292 :                 CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
     258             :         {
     259           1 :             opts.format = OGRWktFormat::F;
     260           1 :             opts.zPrecision = atoi(pszZPrecision);
     261             :         }
     262         292 :         if (bIso)
     263         292 :             opts.variant = wkbVariantIso;
     264         584 :         std::string wkt = exportToWkt(opts, &err);
     265         292 :         if (err == OGRERR_NONE)
     266             :         {
     267         292 :             osRet = pszPrefix;
     268         292 :             osRet += wkt.data();
     269         292 :             osRet += '\n';
     270             :         }
     271         292 :     };
     272             : 
     273             :     const char *pszDisplayGeometry =
     274         317 :         CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
     275         317 :     if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
     276             :     {
     277          25 :         osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
     278          25 :         switch (getGeometryType())
     279             :         {
     280           1 :             case wkbUnknown:
     281             :             case wkbNone:
     282             :             case wkbPoint:
     283             :             case wkbPoint25D:
     284             :             case wkbPointM:
     285             :             case wkbPointZM:
     286           1 :                 break;
     287           0 :             case wkbPolyhedralSurface:
     288             :             case wkbTIN:
     289             :             case wkbPolyhedralSurfaceZ:
     290             :             case wkbTINZ:
     291             :             case wkbPolyhedralSurfaceM:
     292             :             case wkbTINM:
     293             :             case wkbPolyhedralSurfaceZM:
     294             :             case wkbTINZM:
     295             :             {
     296           0 :                 const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
     297             :                 osRet +=
     298           0 :                     CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
     299           0 :                 for (auto &&poSubGeom : *poPS)
     300             :                 {
     301           0 :                     osRet += pszPrefix;
     302           0 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     303             :                 }
     304           0 :                 break;
     305             :             }
     306           0 :             case wkbLineString:
     307             :             case wkbLineString25D:
     308             :             case wkbLineStringM:
     309             :             case wkbLineStringZM:
     310             :             case wkbCircularString:
     311             :             case wkbCircularStringZ:
     312             :             case wkbCircularStringM:
     313             :             case wkbCircularStringZM:
     314             :             {
     315           0 :                 const OGRSimpleCurve *poSC = toSimpleCurve();
     316           0 :                 osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
     317           0 :                 break;
     318             :             }
     319          11 :             case wkbPolygon:
     320             :             case wkbTriangle:
     321             :             case wkbTriangleZ:
     322             :             case wkbTriangleM:
     323             :             case wkbTriangleZM:
     324             :             case wkbPolygon25D:
     325             :             case wkbPolygonM:
     326             :             case wkbPolygonZM:
     327             :             case wkbCurvePolygon:
     328             :             case wkbCurvePolygonZ:
     329             :             case wkbCurvePolygonM:
     330             :             case wkbCurvePolygonZM:
     331             :             {
     332          11 :                 const OGRCurvePolygon *poPoly = toCurvePolygon();
     333          11 :                 const OGRCurve *poRing = poPoly->getExteriorRingCurve();
     334          11 :                 const int nRings = poPoly->getNumInteriorRings();
     335          11 :                 if (poRing == nullptr)
     336             :                 {
     337           0 :                     osRet += "empty";
     338             :                 }
     339             :                 else
     340             :                 {
     341          11 :                     osRet += CPLOPrintf("%d points", poRing->getNumPoints());
     342          11 :                     if (wkbFlatten(poRing->getGeometryType()) ==
     343             :                         wkbCompoundCurve)
     344             :                     {
     345           0 :                         osRet += " (";
     346           0 :                         osRet += poRing->dumpReadable(nullptr, papszOptions);
     347           0 :                         osRet += ")";
     348             :                     }
     349          11 :                     if (nRings)
     350             :                     {
     351           1 :                         osRet += CPLOPrintf(", %d inner rings (", nRings);
     352           8 :                         for (int ir = 0; ir < nRings; ir++)
     353             :                         {
     354           7 :                             poRing = poPoly->getInteriorRingCurve(ir);
     355           7 :                             if (ir)
     356           6 :                                 osRet += ", ";
     357             :                             osRet +=
     358           7 :                                 CPLOPrintf("%d points", poRing->getNumPoints());
     359           7 :                             if (wkbFlatten(poRing->getGeometryType()) ==
     360             :                                 wkbCompoundCurve)
     361             :                             {
     362           2 :                                 osRet += " (";
     363             :                                 osRet +=
     364           2 :                                     poRing->dumpReadable(nullptr, papszOptions);
     365           2 :                                 osRet += ")";
     366             :                             }
     367             :                         }
     368           1 :                         osRet += ")";
     369             :                     }
     370             :                 }
     371          11 :                 osRet += "\n";
     372          11 :                 break;
     373             :             }
     374           2 :             case wkbCompoundCurve:
     375             :             case wkbCompoundCurveZ:
     376             :             case wkbCompoundCurveM:
     377             :             case wkbCompoundCurveZM:
     378             :             {
     379           2 :                 const OGRCompoundCurve *poCC = toCompoundCurve();
     380           2 :                 if (poCC->getNumCurves() == 0)
     381             :                 {
     382           0 :                     osRet += "empty";
     383             :                 }
     384             :                 else
     385             :                 {
     386           6 :                     for (int i = 0; i < poCC->getNumCurves(); i++)
     387             :                     {
     388           4 :                         if (i)
     389           2 :                             osRet += ", ";
     390             :                         osRet +=
     391           8 :                             CPLOPrintf("%s (%d points)",
     392           4 :                                        poCC->getCurve(i)->getGeometryName(),
     393           8 :                                        poCC->getCurve(i)->getNumPoints());
     394             :                     }
     395             :                 }
     396           2 :                 break;
     397             :             }
     398             : 
     399          11 :             case wkbMultiPoint:
     400             :             case wkbMultiLineString:
     401             :             case wkbMultiPolygon:
     402             :             case wkbMultiCurve:
     403             :             case wkbMultiSurface:
     404             :             case wkbGeometryCollection:
     405             :             case wkbMultiPoint25D:
     406             :             case wkbMultiLineString25D:
     407             :             case wkbMultiPolygon25D:
     408             :             case wkbMultiCurveZ:
     409             :             case wkbMultiSurfaceZ:
     410             :             case wkbGeometryCollection25D:
     411             :             case wkbMultiPointM:
     412             :             case wkbMultiLineStringM:
     413             :             case wkbMultiPolygonM:
     414             :             case wkbMultiCurveM:
     415             :             case wkbMultiSurfaceM:
     416             :             case wkbGeometryCollectionM:
     417             :             case wkbMultiPointZM:
     418             :             case wkbMultiLineStringZM:
     419             :             case wkbMultiPolygonZM:
     420             :             case wkbMultiCurveZM:
     421             :             case wkbMultiSurfaceZM:
     422             :             case wkbGeometryCollectionZM:
     423             :             {
     424          11 :                 const OGRGeometryCollection *poColl = toGeometryCollection();
     425             :                 osRet +=
     426          11 :                     CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
     427          22 :                 for (auto &&poSubGeom : *poColl)
     428             :                 {
     429          11 :                     osRet += pszPrefix;
     430          11 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     431             :                 }
     432          11 :                 break;
     433             :             }
     434           0 :             case wkbLinearRing:
     435             :             case wkbCurve:
     436             :             case wkbSurface:
     437             :             case wkbCurveZ:
     438             :             case wkbSurfaceZ:
     439             :             case wkbCurveM:
     440             :             case wkbSurfaceM:
     441             :             case wkbCurveZM:
     442             :             case wkbSurfaceZM:
     443           0 :                 break;
     444          25 :         }
     445             :     }
     446         292 :     else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
     447             :     {
     448           0 :         exportToWktWithOpts(/* bIso=*/false);
     449             :     }
     450         292 :     else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
     451           0 :              EQUAL(pszDisplayGeometry, "ISO_WKT"))
     452             :     {
     453         292 :         exportToWktWithOpts(/* bIso=*/true);
     454             :     }
     455             : 
     456         634 :     return osRet;
     457             : }
     458             : 
     459             : /************************************************************************/
     460             : /*                         OGR_G_DumpReadable()                         */
     461             : /************************************************************************/
     462             : /**
     463             :  * \brief Dump geometry in well known text format to indicated output file.
     464             :  *
     465             :  * This method is the same as the CPP method OGRGeometry::dumpReadable.
     466             :  *
     467             :  * @param hGeom handle on the geometry to dump.
     468             :  * @param fp the text file to write the geometry to.
     469             :  * @param pszPrefix the prefix to put on each line of output.
     470             :  */
     471             : 
     472           0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
     473             : 
     474             : {
     475           0 :     VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
     476             : 
     477           0 :     OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                       assignSpatialReference()                       */
     482             : /************************************************************************/
     483             : 
     484             : /**
     485             :  * \brief Assign spatial reference to this object.
     486             :  *
     487             :  * Any existing spatial reference
     488             :  * is replaced, but under no circumstances does this result in the object
     489             :  * being reprojected.  It is just changing the interpretation of the existing
     490             :  * geometry.  Note that assigning a spatial reference increments the
     491             :  * reference count on the OGRSpatialReference, but does not copy it.
     492             :  *
     493             :  * This will also assign the spatial reference to
     494             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     495             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     496             :  * derived classes).
     497             :  *
     498             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     499             :  *
     500             :  * This method is the same as the C function OGR_G_AssignSpatialReference().
     501             :  *
     502             :  * @param poSR new spatial reference system to apply.
     503             :  */
     504             : 
     505     5662600 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
     506             : 
     507             : {
     508             :     // Do in that order to properly handle poSR == poSRS
     509     5662600 :     if (poSR != nullptr)
     510     3620580 :         const_cast<OGRSpatialReference *>(poSR)->Reference();
     511     5662600 :     if (poSRS != nullptr)
     512       46168 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     513             : 
     514     5662600 :     poSRS = poSR;
     515     5662600 : }
     516             : 
     517             : /************************************************************************/
     518             : /*                    OGR_G_AssignSpatialReference()                    */
     519             : /************************************************************************/
     520             : /**
     521             :  * \brief Assign spatial reference to this object.
     522             :  *
     523             :  * Any existing spatial reference
     524             :  * is replaced, but under no circumstances does this result in the object
     525             :  * being reprojected.  It is just changing the interpretation of the existing
     526             :  * geometry.  Note that assigning a spatial reference increments the
     527             :  * reference count on the OGRSpatialReference, but does not copy it.
     528             :  *
     529             :  * This will also assign the spatial reference to
     530             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     531             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     532             :  * derived classes).
     533             :  *
     534             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     535             :  *
     536             :  * This function is the same as the CPP method
     537             :  * OGRGeometry::assignSpatialReference.
     538             :  *
     539             :  * @param hGeom handle on the geometry to apply the new spatial reference
     540             :  * system.
     541             :  * @param hSRS handle on the new spatial reference system to apply.
     542             :  */
     543             : 
     544          80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     545             : 
     546             : {
     547          80 :     VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
     548             : 
     549         160 :     OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
     550          80 :         OGRSpatialReference::FromHandle(hSRS));
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                             Intersects()                             */
     555             : /************************************************************************/
     556             : 
     557             : /**
     558             :  * \brief Do these features intersect?
     559             :  *
     560             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     561             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     562             :  * envelopes (bounding boxes) of the two geometries overlap.
     563             :  *
     564             :  * The poOtherGeom argument may be safely NULL, but in this case the method
     565             :  * will always return TRUE.   That is, a NULL geometry is treated as being
     566             :  * everywhere.
     567             :  *
     568             :  * This method is the same as the C function OGR_G_Intersects().
     569             :  *
     570             :  * @param poOtherGeom the other geometry to test against.
     571             :  *
     572             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     573             :  */
     574             : 
     575          44 : bool OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
     576             : 
     577             : {
     578          44 :     if (poOtherGeom == nullptr)
     579           0 :         return TRUE;
     580             : 
     581          44 :     OGREnvelope oEnv1;
     582          44 :     getEnvelope(&oEnv1);
     583             : 
     584          44 :     OGREnvelope oEnv2;
     585          44 :     poOtherGeom->getEnvelope(&oEnv2);
     586             : 
     587          44 :     if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
     588          26 :         oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
     589          18 :         return FALSE;
     590             : 
     591             : #ifndef HAVE_GEOS
     592             :     // Without GEOS we assume that envelope overlap is equivalent to
     593             :     // actual intersection.
     594             :     return TRUE;
     595             : #else
     596             : 
     597          26 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
     598          26 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
     599          26 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
     600             : 
     601          26 :     bool bResult = false;
     602          26 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
     603             :     {
     604          26 :         bResult =
     605          26 :             GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
     606             :     }
     607             : 
     608          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
     609          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
     610          26 :     freeGEOSContext(hGEOSCtxt);
     611             : 
     612          26 :     return bResult;
     613             : #endif  // HAVE_GEOS
     614             : }
     615             : 
     616             : // Old API compatibility function.
     617             : 
     618             : //! @cond Doxygen_Suppress
     619           0 : bool OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
     620             : 
     621             : {
     622           0 :     return Intersects(poOtherGeom);
     623             : }
     624             : 
     625             : //! @endcond
     626             : 
     627             : /************************************************************************/
     628             : /*                          OGR_G_Intersects()                          */
     629             : /************************************************************************/
     630             : /**
     631             :  * \brief Do these features intersect?
     632             :  *
     633             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     634             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     635             :  * envelopes (bounding boxes) of the two geometries overlap.
     636             :  *
     637             :  * This function is the same as the CPP method OGRGeometry::Intersects.
     638             :  *
     639             :  * @param hGeom handle on the first geometry.
     640             :  * @param hOtherGeom handle on the other geometry to test against.
     641             :  *
     642             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     643             :  */
     644             : 
     645          11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     646             : 
     647             : {
     648          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
     649          11 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
     650             : 
     651          22 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     652          22 :         OGRGeometry::FromHandle(hOtherGeom));
     653             : }
     654             : 
     655             : //! @cond Doxygen_Suppress
     656           0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     657             : 
     658             : {
     659           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
     660           0 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
     661             : 
     662           0 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     663           0 :         OGRGeometry::FromHandle(hOtherGeom));
     664             : }
     665             : 
     666             : //! @endcond
     667             : 
     668             : /************************************************************************/
     669             : /*                            transformTo()                             */
     670             : /************************************************************************/
     671             : 
     672             : /**
     673             :  * \brief Transform geometry to new spatial reference system.
     674             :  *
     675             :  * This method will transform the coordinates of a geometry from
     676             :  * their current spatial reference system to a new target spatial
     677             :  * reference system.  Normally this means reprojecting the vectors,
     678             :  * but it could include datum shifts, and changes of units.
     679             :  *
     680             :  * This method will only work if the geometry already has an assigned
     681             :  * spatial reference system, and if it is transformable to the target
     682             :  * coordinate system.
     683             :  *
     684             :  * Because this method requires internal creation and initialization of an
     685             :  * OGRCoordinateTransformation object it is significantly more expensive to
     686             :  * use this method to transform many geometries than it is to create the
     687             :  * OGRCoordinateTransformation in advance, and call transform() with that
     688             :  * transformation.  This method exists primarily for convenience when only
     689             :  * transforming a single geometry.
     690             :  *
     691             :  * This method is the same as the C function OGR_G_TransformTo().
     692             :  *
     693             :  * @param poSR spatial reference system to transform to.
     694             :  *
     695             :  * @return OGRERR_NONE on success, or an error code.
     696             :  */
     697             : 
     698          33 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
     699             : 
     700             : {
     701          33 :     if (getSpatialReference() == nullptr)
     702             :     {
     703           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
     704           1 :         return OGRERR_FAILURE;
     705             :     }
     706             : 
     707          32 :     if (poSR == nullptr)
     708             :     {
     709           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
     710           0 :         return OGRERR_FAILURE;
     711             :     }
     712             : 
     713             :     OGRCoordinateTransformation *poCT =
     714          32 :         OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
     715          32 :     if (poCT == nullptr)
     716           0 :         return OGRERR_FAILURE;
     717             : 
     718          32 :     const OGRErr eErr = transform(poCT);
     719             : 
     720          32 :     OGRCoordinateTransformation::DestroyCT(poCT);
     721             : 
     722          32 :     return eErr;
     723             : }
     724             : 
     725             : /************************************************************************/
     726             : /*                         OGR_G_TransformTo()                          */
     727             : /************************************************************************/
     728             : /**
     729             :  * \brief Transform geometry to new spatial reference system.
     730             :  *
     731             :  * This function will transform the coordinates of a geometry from
     732             :  * their current spatial reference system to a new target spatial
     733             :  * reference system.  Normally this means reprojecting the vectors,
     734             :  * but it could include datum shifts, and changes of units.
     735             :  *
     736             :  * This function will only work if the geometry already has an assigned
     737             :  * spatial reference system, and if it is transformable to the target
     738             :  * coordinate system.
     739             :  *
     740             :  * Because this function requires internal creation and initialization of an
     741             :  * OGRCoordinateTransformation object it is significantly more expensive to
     742             :  * use this function to transform many geometries than it is to create the
     743             :  * OGRCoordinateTransformation in advance, and call transform() with that
     744             :  * transformation.  This function exists primarily for convenience when only
     745             :  * transforming a single geometry.
     746             :  *
     747             :  * This function is the same as the CPP method OGRGeometry::transformTo.
     748             :  *
     749             :  * @param hGeom handle on the geometry to apply the transform to.
     750             :  * @param hSRS handle on the spatial reference system to apply.
     751             :  *
     752             :  * @return OGRERR_NONE on success, or an error code.
     753             :  */
     754             : 
     755           9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     756             : 
     757             : {
     758           9 :     VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
     759             : 
     760          18 :     return OGRGeometry::FromHandle(hGeom)->transformTo(
     761          18 :         OGRSpatialReference::FromHandle(hSRS));
     762             : }
     763             : 
     764             : /**
     765             :  * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
     766             :  *
     767             :  * \brief Apply arbitrary coordinate transformation to geometry.
     768             :  *
     769             :  * This method will transform the coordinates of a geometry from
     770             :  * their current spatial reference system to a new target spatial
     771             :  * reference system.  Normally this means reprojecting the vectors,
     772             :  * but it could include datum shifts, and changes of units.
     773             :  *
     774             :  * Note that this method does not require that the geometry already
     775             :  * have a spatial reference system.  It will be assumed that they can
     776             :  * be treated as having the source spatial reference system of the
     777             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     778             :  * will be ignored.  On successful completion the output OGRSpatialReference
     779             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     780             :  *
     781             :  * This method only does reprojection on a point-by-point basis. It does not
     782             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     783             :  * For that, use the OGRGeometryFactory::transformWithOptions() method.
     784             :  *
     785             :  * This method is the same as the C function OGR_G_Transform().
     786             :  *
     787             :  * @param poCT the transformation to apply.
     788             :  *
     789             :  * @return OGRERR_NONE on success or an error code.
     790             :  */
     791             : 
     792             : /************************************************************************/
     793             : /*                          OGR_G_Transform()                           */
     794             : /************************************************************************/
     795             : /**
     796             :  * \brief Apply arbitrary coordinate transformation to geometry.
     797             :  *
     798             :  * This function will transform the coordinates of a geometry from
     799             :  * their current spatial reference system to a new target spatial
     800             :  * reference system.  Normally this means reprojecting the vectors,
     801             :  * but it could include datum shifts, and changes of units.
     802             :  *
     803             :  * Note that this function does not require that the geometry already
     804             :  * have a spatial reference system.  It will be assumed that they can
     805             :  * be treated as having the source spatial reference system of the
     806             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     807             :  * will be ignored.  On successful completion the output OGRSpatialReference
     808             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     809             :  *
     810             :  * This function only does reprojection on a point-by-point basis. It does not
     811             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     812             :  * For that, use the OGR_GeomTransformer_Create() and
     813             :  * OGR_GeomTransformer_Transform() functions.
     814             :  *
     815             :  * This function is the same as the CPP method OGRGeometry::transform.
     816             :  *
     817             :  * @param hGeom handle on the geometry to apply the transform to.
     818             :  * @param hTransform handle on the transformation to apply.
     819             :  *
     820             :  * @return OGRERR_NONE on success or an error code.
     821             :  */
     822             : 
     823          11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
     824             :                        OGRCoordinateTransformationH hTransform)
     825             : 
     826             : {
     827          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
     828             : 
     829          22 :     return OGRGeometry::FromHandle(hGeom)->transform(
     830          11 :         OGRCoordinateTransformation::FromHandle(hTransform));
     831             : }
     832             : 
     833             : /**
     834             :  * \fn int OGRGeometry::getDimension() const;
     835             :  *
     836             :  * \brief Get the dimension of this object.
     837             :  *
     838             :  * This method corresponds to the SFCOM IGeometry::GetDimension() method.
     839             :  * It indicates the dimension of the object, but does not indicate the
     840             :  * dimension of the underlying space (as indicated by
     841             :  * OGRGeometry::getCoordinateDimension()).
     842             :  *
     843             :  * This method is the same as the C function OGR_G_GetDimension().
     844             :  *
     845             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     846             :  */
     847             : 
     848             : /**
     849             :  * \brief Get the geometry type that conforms with ISO SQL/MM Part3
     850             :  *
     851             :  * @return the geometry type that conforms with ISO SQL/MM Part3
     852             :  */
     853      730587 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
     854             : {
     855      730587 :     OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
     856             : 
     857      730587 :     if (flags & OGR_G_3D)
     858      214265 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
     859      730587 :     if (flags & OGR_G_MEASURED)
     860       26025 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
     861             : 
     862      730587 :     return nGType;
     863             : }
     864             : 
     865             : /************************************************************************/
     866             : /*                      OGRGeometry::segmentize()                       */
     867             : /************************************************************************/
     868             : /**
     869             :  *
     870             :  * \brief Modify the geometry such it has no segment longer then the
     871             :  * given distance.
     872             :  *
     873             :  * This method modifies the geometry to add intermediate vertices if necessary
     874             :  * so that the maximum length between 2 consecutive vertices is lower than
     875             :  * dfMaxLength.
     876             :  *
     877             :  * Interpolated points will have Z and M values (if needed) set to 0.
     878             :  * Distance computation is performed in 2d only
     879             :  *
     880             :  * This function is the same as the C function OGR_G_Segmentize()
     881             :  *
     882             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     883             :  * @return (since 3.10) true in case of success, false in case of error.
     884             :  */
     885             : 
     886           0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
     887             : {
     888             :     // Do nothing.
     889           0 :     return true;
     890             : }
     891             : 
     892             : /************************************************************************/
     893             : /*                          OGR_G_Segmentize()                          */
     894             : /************************************************************************/
     895             : 
     896             : /**
     897             :  *
     898             :  * \brief Modify the geometry such it has no segment longer then the given
     899             :  * distance.
     900             :  *
     901             :  * Interpolated points will have Z and M values (if needed) set to 0.
     902             :  * Distance computation is performed in 2d only.
     903             :  *
     904             :  * This function is the same as the CPP method OGRGeometry::segmentize().
     905             :  *
     906             :  * @param hGeom handle on the geometry to segmentize
     907             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     908             :  */
     909             : 
     910          24 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
     911             : {
     912          24 :     VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
     913             : 
     914          24 :     if (dfMaxLength <= 0)
     915             :     {
     916           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     917             :                  "dfMaxLength must be strictly positive");
     918           0 :         return;
     919             :     }
     920          24 :     OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
     921             : }
     922             : 
     923             : /************************************************************************/
     924             : /*                         OGR_G_GetDimension()                         */
     925             : /************************************************************************/
     926             : /**
     927             :  *
     928             :  * \brief Get the dimension of this geometry.
     929             :  *
     930             :  * This function corresponds to the SFCOM IGeometry::GetDimension() method.
     931             :  * It indicates the dimension of the geometry, but does not indicate the
     932             :  * dimension of the underlying space (as indicated by
     933             :  * OGR_G_GetCoordinateDimension() function).
     934             :  *
     935             :  * This function is the same as the CPP method OGRGeometry::getDimension().
     936             :  *
     937             :  * @param hGeom handle on the geometry to get the dimension from.
     938             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     939             :  */
     940             : 
     941          21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
     942             : 
     943             : {
     944          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
     945             : 
     946          21 :     return OGRGeometry::FromHandle(hGeom)->getDimension();
     947             : }
     948             : 
     949             : /************************************************************************/
     950             : /*                       getCoordinateDimension()                       */
     951             : /************************************************************************/
     952             : /**
     953             :  * \brief Get the dimension of the coordinates in this object.
     954             :  *
     955             :  * This method is the same as the C function OGR_G_GetCoordinateDimension().
     956             :  *
     957             :  * @deprecated use CoordinateDimension().
     958             :  *
     959             :  * @return this will return 2 or 3.
     960             :  */
     961             : 
     962      593649 : int OGRGeometry::getCoordinateDimension() const
     963             : 
     964             : {
     965      593649 :     return (flags & OGR_G_3D) ? 3 : 2;
     966             : }
     967             : 
     968             : /************************************************************************/
     969             : /*                        CoordinateDimension()                         */
     970             : /************************************************************************/
     971             : /**
     972             :  * \brief Get the dimension of the coordinates in this object.
     973             :  *
     974             :  * This method is the same as the C function OGR_G_CoordinateDimension().
     975             :  *
     976             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
     977             :  *
     978             :  */
     979             : 
     980       29843 : int OGRGeometry::CoordinateDimension() const
     981             : 
     982             : {
     983       29843 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     984        7375 :         return 4;
     985       22468 :     else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
     986        6861 :         return 3;
     987             :     else
     988       15607 :         return 2;
     989             : }
     990             : 
     991             : /************************************************************************/
     992             : /*                    OGR_G_GetCoordinateDimension()                    */
     993             : /************************************************************************/
     994             : /**
     995             :  *
     996             :  * \brief Get the dimension of the coordinates in this geometry.
     997             :  *
     998             :  * This function is the same as the CPP method
     999             :  * OGRGeometry::getCoordinateDimension().
    1000             :  *
    1001             :  * @param hGeom handle on the geometry to get the dimension of the
    1002             :  * coordinates from.
    1003             :  *
    1004             :  * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
    1005             :  * OGR_G_IsMeasured().
    1006             :  *
    1007             :  * @return this will return 2 or 3.
    1008             :  */
    1009             : 
    1010         724 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
    1011             : 
    1012             : {
    1013         724 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
    1014             : 
    1015         724 :     return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
    1016             : }
    1017             : 
    1018             : /************************************************************************/
    1019             : /*                     OGR_G_CoordinateDimension()                      */
    1020             : /************************************************************************/
    1021             : /**
    1022             :  *
    1023             :  * \brief Get the dimension of the coordinates in this geometry.
    1024             :  *
    1025             :  * This function is the same as the CPP method
    1026             :  * OGRGeometry::CoordinateDimension().
    1027             :  *
    1028             :  * @param hGeom handle on the geometry to get the dimension of the
    1029             :  * coordinates from.
    1030             :  *
    1031             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
    1032             :  *
    1033             :  */
    1034             : 
    1035           4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
    1036             : 
    1037             : {
    1038           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
    1039             : 
    1040           4 :     return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
    1041             : }
    1042             : 
    1043             : /**
    1044             :  *
    1045             :  * \brief See whether this geometry has Z coordinates.
    1046             :  *
    1047             :  * This function is the same as the CPP method
    1048             :  * OGRGeometry::Is3D().
    1049             :  *
    1050             :  * @param hGeom handle on the geometry to check whether it has Z coordinates.
    1051             :  *
    1052             :  * @return TRUE if the geometry has Z coordinates.
    1053             :  */
    1054             : 
    1055       37749 : int OGR_G_Is3D(OGRGeometryH hGeom)
    1056             : 
    1057             : {
    1058       37749 :     VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
    1059             : 
    1060       37749 :     return OGRGeometry::FromHandle(hGeom)->Is3D();
    1061             : }
    1062             : 
    1063             : /**
    1064             :  *
    1065             :  * \brief See whether this geometry is measured.
    1066             :  *
    1067             :  * This function is the same as the CPP method
    1068             :  * OGRGeometry::IsMeasured().
    1069             :  *
    1070             :  * @param hGeom handle on the geometry to check whether it is measured.
    1071             :  *
    1072             :  * @return TRUE if the geometry has M coordinates.
    1073             :  */
    1074             : 
    1075       40153 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
    1076             : 
    1077             : {
    1078       40153 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
    1079             : 
    1080       40153 :     return OGRGeometry::FromHandle(hGeom)->IsMeasured();
    1081             : }
    1082             : 
    1083             : /************************************************************************/
    1084             : /*                       setCoordinateDimension()                       */
    1085             : /************************************************************************/
    1086             : 
    1087             : /**
    1088             :  * \brief Set the coordinate dimension.
    1089             :  *
    1090             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1091             :  * dimension of a geometry to 2 should zero out any existing Z values.  Setting
    1092             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1093             :  * will affect the children geometries.
    1094             :  * This will also remove the M dimension if present before this call.
    1095             :  *
    1096             :  * @deprecated use set3D() or setMeasured().
    1097             :  *
    1098             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1099             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1100             :  */
    1101             : 
    1102       68188 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
    1103             : 
    1104             : {
    1105       68188 :     if (nNewDimension == 2)
    1106       67686 :         flags &= ~OGR_G_3D;
    1107             :     else
    1108         502 :         flags |= OGR_G_3D;
    1109       68188 :     return setMeasured(FALSE);
    1110             : }
    1111             : 
    1112             : /**
    1113             :  * \brief Add or remove the Z coordinate dimension.
    1114             :  *
    1115             :  * This method adds or removes the explicit Z coordinate dimension.
    1116             :  * Removing the Z coordinate dimension of a geometry will remove any
    1117             :  * existing Z values.  Adding the Z dimension to a geometry
    1118             :  * collection, a compound curve, a polygon, etc.  will affect the
    1119             :  * children geometries.
    1120             :  *
    1121             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1122             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1123             :  */
    1124             : 
    1125     1624260 : bool OGRGeometry::set3D(bool bIs3D)
    1126             : 
    1127             : {
    1128     1624260 :     if (bIs3D)
    1129     1614820 :         flags |= OGR_G_3D;
    1130             :     else
    1131        9440 :         flags &= ~OGR_G_3D;
    1132     1624260 :     return true;
    1133             : }
    1134             : 
    1135             : /**
    1136             :  * \brief Add or remove the M coordinate dimension.
    1137             :  *
    1138             :  * This method adds or removes the explicit M coordinate dimension.
    1139             :  * Removing the M coordinate dimension of a geometry will remove any
    1140             :  * existing M values.  Adding the M dimension to a geometry
    1141             :  * collection, a compound curve, a polygon, etc.  will affect the
    1142             :  * children geometries.
    1143             :  *
    1144             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1145             :  * TRUE or FALSE.
    1146             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1147             :  */
    1148             : 
    1149      419113 : bool OGRGeometry::setMeasured(bool bIsMeasured)
    1150             : 
    1151             : {
    1152      419113 :     if (bIsMeasured)
    1153      137732 :         flags |= OGR_G_MEASURED;
    1154             :     else
    1155      281381 :         flags &= ~OGR_G_MEASURED;
    1156      419113 :     return true;
    1157             : }
    1158             : 
    1159             : /************************************************************************/
    1160             : /*                    OGR_G_SetCoordinateDimension()                    */
    1161             : /************************************************************************/
    1162             : 
    1163             : /**
    1164             :  * \brief Set the coordinate dimension.
    1165             :  *
    1166             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1167             :  * dimension of a geometry to 2 should zero out any existing Z values. Setting
    1168             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1169             :  * will affect the children geometries.
    1170             :  * This will also remove the M dimension if present before this call.
    1171             :  *
    1172             :  * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
    1173             :  *
    1174             :  * @param hGeom handle on the geometry to set the dimension of the
    1175             :  * coordinates.
    1176             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1177             :  */
    1178             : 
    1179          56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
    1180             : 
    1181             : {
    1182          56 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
    1183             : 
    1184          56 :     OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
    1185             : }
    1186             : 
    1187             : /************************************************************************/
    1188             : /*                            OGR_G_Set3D()                             */
    1189             : /************************************************************************/
    1190             : 
    1191             : /**
    1192             :  * \brief Add or remove the Z coordinate dimension.
    1193             :  *
    1194             :  * This method adds or removes the explicit Z coordinate dimension.
    1195             :  * Removing the Z coordinate dimension of a geometry will remove any
    1196             :  * existing Z values.  Adding the Z dimension to a geometry
    1197             :  * collection, a compound curve, a polygon, etc.  will affect the
    1198             :  * children geometries.
    1199             :  *
    1200             :  * @param hGeom handle on the geometry to set or unset the Z dimension.
    1201             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1202             :  */
    1203             : 
    1204         154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
    1205             : 
    1206             : {
    1207         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
    1208             : 
    1209         154 :     OGRGeometry::FromHandle(hGeom)->set3D(CPL_TO_BOOL(bIs3D));
    1210             : }
    1211             : 
    1212             : /************************************************************************/
    1213             : /*                         OGR_G_SetMeasured()                          */
    1214             : /************************************************************************/
    1215             : 
    1216             : /**
    1217             :  * \brief Add or remove the M coordinate dimension.
    1218             :  *
    1219             :  * This method adds or removes the explicit M coordinate dimension.
    1220             :  * Removing the M coordinate dimension of a geometry will remove any
    1221             :  * existing M values.  Adding the M dimension to a geometry
    1222             :  * collection, a compound curve, a polygon, etc.  will affect the
    1223             :  * children geometries.
    1224             :  *
    1225             :  * @param hGeom handle on the geometry to set or unset the M dimension.
    1226             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1227             :  * TRUE or FALSE.
    1228             :  */
    1229             : 
    1230         154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
    1231             : 
    1232             : {
    1233         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
    1234             : 
    1235         154 :     OGRGeometry::FromHandle(hGeom)->setMeasured(CPL_TO_BOOL(bIsMeasured));
    1236             : }
    1237             : 
    1238             : /**
    1239             :  * \fn bool OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
    1240             :  *
    1241             :  * \brief Returns TRUE if two geometries are equivalent.
    1242             :  *
    1243             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1244             :  *
    1245             :  * The comparison is done in a structural way, that is to say that the geometry
    1246             :  * types must be identical, as well as the number and ordering of sub-geometries
    1247             :  * and vertices.
    1248             :  * Or equivalently, two geometries are considered equal by this method if their
    1249             :  * WKT/WKB representation is equal.
    1250             :  * Note: this must be distinguished for equality in a spatial way (which is
    1251             :  * the purpose of the ST_Equals() operation).
    1252             :  *
    1253             :  * This method is the same as the C function OGR_G_Equals().
    1254             :  *
    1255             :  * @return TRUE if equivalent or FALSE otherwise.
    1256             :  */
    1257             : 
    1258             : // Backward compatibility method.
    1259             : 
    1260             : //! @cond Doxygen_Suppress
    1261           0 : bool OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
    1262             : {
    1263           0 :     return Equals(poOtherGeom);
    1264             : }
    1265             : 
    1266             : //! @endcond
    1267             : 
    1268             : /************************************************************************/
    1269             : /*                            OGR_G_Equals()                            */
    1270             : /************************************************************************/
    1271             : 
    1272             : /**
    1273             :  * \brief Returns TRUE if two geometries are equivalent.
    1274             :  *
    1275             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1276             :  *
    1277             :  * The comparison is done in a structural way, that is to say that the geometry
    1278             :  * types must be identical, as well as the number and ordering of sub-geometries
    1279             :  * and vertices.
    1280             :  * Or equivalently, two geometries are considered equal by this method if their
    1281             :  * WKT/WKB representation is equal.
    1282             :  * Note: this must be distinguished for equality in a spatial way (which is
    1283             :  * the purpose of the ST_Equals() operation).
    1284             :  *
    1285             :  * This function is the same as the CPP method OGRGeometry::Equals() method.
    1286             :  *
    1287             :  * @param hGeom handle on the first geometry.
    1288             :  * @param hOther handle on the other geometry to test against.
    1289             :  * @return TRUE if equivalent or FALSE otherwise.
    1290             :  */
    1291             : 
    1292       28120 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
    1293             : 
    1294             : {
    1295       28120 :     VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
    1296             : 
    1297       28120 :     if (hOther == nullptr)
    1298             :     {
    1299           0 :         CPLError(CE_Failure, CPLE_ObjectNull,
    1300             :                  "hOther was NULL in OGR_G_Equals");
    1301           0 :         return 0;
    1302             :     }
    1303             : 
    1304       56240 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1305       56240 :         OGRGeometry::FromHandle(hOther));
    1306             : }
    1307             : 
    1308             : //! @cond Doxygen_Suppress
    1309           0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
    1310             : 
    1311             : {
    1312           0 :     if (hGeom == nullptr)
    1313             :     {
    1314           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
    1315           0 :         return 0;
    1316             :     }
    1317             : 
    1318           0 :     if (hOther == nullptr)
    1319             :     {
    1320           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
    1321           0 :         return 0;
    1322             :     }
    1323             : 
    1324           0 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1325           0 :         OGRGeometry::FromHandle(hOther));
    1326             : }
    1327             : 
    1328             : //! @endcond
    1329             : 
    1330             : /**
    1331             :  * \fn int OGRGeometry::WkbSize() const;
    1332             :  *
    1333             :  * \brief Returns size of related binary representation.
    1334             :  *
    1335             :  * This method returns the exact number of bytes required to hold the
    1336             :  * well known binary representation of this geometry object.  Its computation
    1337             :  * may be slightly expensive for complex geometries.
    1338             :  *
    1339             :  * This method relates to the SFCOM IWks::WkbSize() method.
    1340             :  *
    1341             :  * This method is the same as the C function OGR_G_WkbSize().
    1342             :  *
    1343             :  * @return size of binary representation in bytes.
    1344             :  */
    1345             : 
    1346             : /************************************************************************/
    1347             : /*                           OGR_G_WkbSize()                            */
    1348             : /************************************************************************/
    1349             : /**
    1350             :  * \brief Returns size of related binary representation.
    1351             :  *
    1352             :  * This function returns the exact number of bytes required to hold the
    1353             :  * well known binary representation of this geometry object.  Its computation
    1354             :  * may be slightly expensive for complex geometries.
    1355             :  *
    1356             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1357             :  *
    1358             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1359             :  *
    1360             :  * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
    1361             :  *
    1362             :  * @param hGeom handle on the geometry to get the binary size from.
    1363             :  * @return size of binary representation in bytes.
    1364             :  */
    1365             : 
    1366           1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
    1367             : 
    1368             : {
    1369           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
    1370             : 
    1371           1 :     const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
    1372           1 :     if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
    1373             :     {
    1374           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1375             :                  "OGR_G_WkbSize() would return a value beyond int range. "
    1376             :                  "Use OGR_G_WkbSizeEx() instead");
    1377           0 :         return 0;
    1378             :     }
    1379           1 :     return static_cast<int>(nSize);
    1380             : }
    1381             : 
    1382             : /************************************************************************/
    1383             : /*                          OGR_G_WkbSizeEx()                           */
    1384             : /************************************************************************/
    1385             : /**
    1386             :  * \brief Returns size of related binary representation.
    1387             :  *
    1388             :  * This function returns the exact number of bytes required to hold the
    1389             :  * well known binary representation of this geometry object.  Its computation
    1390             :  * may be slightly expensive for complex geometries.
    1391             :  *
    1392             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1393             :  *
    1394             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1395             :  *
    1396             :  * @param hGeom handle on the geometry to get the binary size from.
    1397             :  * @return size of binary representation in bytes.
    1398             :  * @since GDAL 3.3
    1399             :  */
    1400             : 
    1401       10679 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
    1402             : 
    1403             : {
    1404       10679 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
    1405             : 
    1406       10679 :     return OGRGeometry::FromHandle(hGeom)->WkbSize();
    1407             : }
    1408             : 
    1409             : /**
    1410             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
    1411             :  *
    1412             :  * \brief Computes and returns the bounding envelope for this geometry
    1413             :  * in the passed psEnvelope structure.
    1414             :  *
    1415             :  * This method is the same as the C function OGR_G_GetEnvelope().
    1416             :  *
    1417             :  * @param psEnvelope the structure in which to place the results.
    1418             :  */
    1419             : 
    1420             : /************************************************************************/
    1421             : /*                         OGR_G_GetEnvelope()                          */
    1422             : /************************************************************************/
    1423             : /**
    1424             :  * \brief Computes and returns the bounding envelope for this geometry
    1425             :  * in the passed psEnvelope structure.
    1426             :  *
    1427             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1428             :  *
    1429             :  * @param hGeom handle of the geometry to get envelope from.
    1430             :  * @param psEnvelope the structure in which to place the results.
    1431             :  */
    1432             : 
    1433       13371 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
    1434             : 
    1435             : {
    1436       13371 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
    1437             : 
    1438       13371 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1439             : }
    1440             : 
    1441             : /**
    1442             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
    1443             :  *
    1444             :  * \brief Computes and returns the bounding envelope (3D) for this
    1445             :  * geometry in the passed psEnvelope structure.
    1446             :  *
    1447             :  * This method is the same as the C function OGR_G_GetEnvelope3D().
    1448             :  *
    1449             :  * @param psEnvelope the structure in which to place the results.
    1450             :  *
    1451             :  */
    1452             : 
    1453             : /************************************************************************/
    1454             : /*                        OGR_G_GetEnvelope3D()                         */
    1455             : /************************************************************************/
    1456             : /**
    1457             :  * \brief Computes and returns the bounding envelope (3D) for this
    1458             :  * geometry in the passed psEnvelope structure.
    1459             :  *
    1460             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1461             :  *
    1462             :  * @param hGeom handle of the geometry to get envelope from.
    1463             :  * @param psEnvelope the structure in which to place the results.
    1464             :  *
    1465             :  */
    1466             : 
    1467          10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
    1468             : 
    1469             : {
    1470          10 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
    1471             : 
    1472          10 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1473             : }
    1474             : 
    1475             : /************************************************************************/
    1476             : /*                           importFromWkb()                            */
    1477             : /************************************************************************/
    1478             : 
    1479             : /**
    1480             :  * \brief Assign geometry from well known binary data.
    1481             :  *
    1482             :  * The object must have already been instantiated as the correct derived
    1483             :  * type of geometry object to match the binaries type.  This method is used
    1484             :  * by the OGRGeometryFactory class, but not normally called by application
    1485             :  * code.
    1486             :  *
    1487             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1488             :  *
    1489             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1490             :  *
    1491             :  * @param pabyData the binary input data.
    1492             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1493             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1494             :  * done for curve geometries code
    1495             :  *
    1496             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1497             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1498             :  * OGRERR_CORRUPT_DATA may be returned.
    1499             :  */
    1500             : 
    1501         492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
    1502             :                                   OGRwkbVariant eWkbVariant)
    1503             : {
    1504         492 :     size_t nBytesConsumedOutIgnored = 0;
    1505         492 :     return importFromWkb(pabyData, nSize, eWkbVariant,
    1506         984 :                          nBytesConsumedOutIgnored);
    1507             : }
    1508             : 
    1509             : /**
    1510             :  * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
    1511             :  * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
    1512             :  *
    1513             :  * \brief Assign geometry from well known binary data.
    1514             :  *
    1515             :  * The object must have already been instantiated as the correct derived
    1516             :  * type of geometry object to match the binaries type.  This method is used
    1517             :  * by the OGRGeometryFactory class, but not normally called by application
    1518             :  * code.
    1519             :  *
    1520             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1521             :  *
    1522             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1523             :  *
    1524             :  * @param pabyData the binary input data.
    1525             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1526             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1527             :  * done for curve geometries code
    1528             :  * @param nBytesConsumedOut output parameter. Number of bytes consumed.
    1529             :  *
    1530             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1531             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1532             :  * OGRERR_CORRUPT_DATA may be returned.
    1533             :  *
    1534             :  */
    1535             : 
    1536             : /************************************************************************/
    1537             : /*                        OGR_G_ImportFromWkb()                         */
    1538             : /************************************************************************/
    1539             : /**
    1540             :  * \brief Assign geometry from well known binary data.
    1541             :  *
    1542             :  * The object must have already been instantiated as the correct derived
    1543             :  * type of geometry object to match the binaries type.
    1544             :  *
    1545             :  * This function relates to the SFCOM IWks::ImportFromWKB() method.
    1546             :  *
    1547             :  * This function is the same as the CPP method OGRGeometry::importFromWkb().
    1548             :  *
    1549             :  * @param hGeom handle on the geometry to assign the well know binary data to.
    1550             :  * @param pabyData the binary input data.
    1551             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1552             :  *
    1553             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1554             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1555             :  * OGRERR_CORRUPT_DATA may be returned.
    1556             :  */
    1557             : 
    1558           0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
    1559             : 
    1560             : {
    1561           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
    1562             : 
    1563           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkb(
    1564           0 :         static_cast<const GByte *>(pabyData), nSize);
    1565             : }
    1566             : 
    1567             : /************************************************************************/
    1568             : /*                      OGRGeometry::exportToWkb()                      */
    1569             : /************************************************************************/
    1570             : 
    1571             : /* clang-format off */
    1572             : /**
    1573             :  * \brief Convert a geometry into well known binary format.
    1574             :  *
    1575             :  * This method relates to the SFCOM IWks::ExportToWKB() method.
    1576             :  *
    1577             :  * This method is the same as the C function OGR_G_ExportToWkb() or
    1578             :  * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
    1579             :  *
    1580             :  * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1581             :  *               respectively.
    1582             :  * @param pabyData a buffer into which the binary representation is
    1583             :  *                      written.  This buffer must be at least
    1584             :  *                      OGRGeometry::WkbSize() byte in size.
    1585             :  * @param eWkbVariant What standard to use when exporting geometries
    1586             :  *                      with three dimensions (or more). The default
    1587             :  *                      wkbVariantOldOgc is the historical OGR
    1588             :  *                      variant. wkbVariantIso is the variant defined
    1589             :  *                      in ISO SQL/MM and adopted by OGC for SFSQL
    1590             :  *                      1.2.
    1591             :  *
    1592             :  * @return Currently OGRERR_NONE is always returned.
    1593             :  */
    1594             : /* clang-format on */
    1595             : 
    1596      278818 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
    1597             :                                 unsigned char *pabyData,
    1598             :                                 OGRwkbVariant eWkbVariant) const
    1599             : {
    1600      278818 :     OGRwkbExportOptions sOptions;
    1601      278818 :     sOptions.eByteOrder = eByteOrder;
    1602      278818 :     sOptions.eWkbVariant = eWkbVariant;
    1603      557636 :     return exportToWkb(pabyData, &sOptions);
    1604             : }
    1605             : 
    1606             : /************************************************************************/
    1607             : /*                         OGR_G_ExportToWkb()                          */
    1608             : /************************************************************************/
    1609             : /**
    1610             :  * \brief Convert a geometry well known binary format
    1611             :  *
    1612             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1613             :  *
    1614             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1615             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1616             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1617             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
    1618             :  *
    1619             :  * This function is the same as the CPP method
    1620             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
    1621             :  * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
    1622             :  *
    1623             :  * @param hGeom handle on the geometry to convert to a well know binary
    1624             :  * data from.
    1625             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1626             :  *               respectively.
    1627             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1628             :  *                      written.  This buffer must be at least
    1629             :  *                      OGR_G_WkbSize() byte in size.
    1630             :  *
    1631             :  * @return Currently OGRERR_NONE is always returned.
    1632             :  */
    1633             : 
    1634         109 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1635             :                          unsigned char *pabyDstBuffer)
    1636             : 
    1637             : {
    1638         109 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
    1639             : 
    1640         109 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
    1641             : }
    1642             : 
    1643             : /************************************************************************/
    1644             : /*                        OGR_G_ExportToIsoWkb()                        */
    1645             : /************************************************************************/
    1646             : /**
    1647             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
    1648             :  * binary format
    1649             :  *
    1650             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1651             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
    1652             :  * types.
    1653             :  *
    1654             :  * This function is the same as the CPP method
    1655             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
    1656             :  * with eWkbVariant = wkbVariantIso.
    1657             :  *
    1658             :  * @param hGeom handle on the geometry to convert to a well know binary
    1659             :  * data from.
    1660             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1661             :  *               respectively.
    1662             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1663             :  *                      written.  This buffer must be at least
    1664             :  *                      OGR_G_WkbSize() byte in size.
    1665             :  *
    1666             :  * @return Currently OGRERR_NONE is always returned.
    1667             :  *
    1668             :  */
    1669             : 
    1670       10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1671             :                             unsigned char *pabyDstBuffer)
    1672             : 
    1673             : {
    1674       10571 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
    1675             : 
    1676       10571 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
    1677       10571 :                                                        wkbVariantIso);
    1678             : }
    1679             : 
    1680             : /************************************************************************/
    1681             : /*                        OGR_G_ExportToWkbEx()                         */
    1682             : /************************************************************************/
    1683             : 
    1684             : /* clang-format off */
    1685             : /**
    1686             :  * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
    1687             :  *
    1688             :  * \brief Convert a geometry into well known binary format
    1689             :  *
    1690             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1691             :  *
    1692             :  * This function is the same as the C function OGR_G_ExportToWkbEx().
    1693             :  *
    1694             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1695             :  *                      written.  This buffer must be at least
    1696             :  *                      OGR_G_WkbSize() byte in size.
    1697             :  * @param psOptions WKB export options.
    1698             : 
    1699             :  * @return Currently OGRERR_NONE is always returned.
    1700             :  *
    1701             :  * @since GDAL 3.9
    1702             :  */
    1703             : /* clang-format on */
    1704             : 
    1705             : /**
    1706             :  * \brief Convert a geometry into well known binary format
    1707             :  *
    1708             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1709             :  *
    1710             :  * This function is the same as the CPP method
    1711             :  * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
    1712             :  *
    1713             :  * @param hGeom handle on the geometry to convert to a well know binary
    1714             :  * data from.
    1715             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1716             :  *                      written.  This buffer must be at least
    1717             :  *                      OGR_G_WkbSize() byte in size.
    1718             :  * @param psOptions WKB export options.
    1719             : 
    1720             :  * @return Currently OGRERR_NONE is always returned.
    1721             :  *
    1722             :  * @since GDAL 3.9
    1723             :  */
    1724             : 
    1725           2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
    1726             :                            const OGRwkbExportOptions *psOptions)
    1727             : {
    1728           2 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
    1729             : 
    1730           4 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
    1731           2 :                                                        psOptions);
    1732             : }
    1733             : 
    1734             : /**
    1735             :  * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
    1736             :  *
    1737             :  * \brief Assign geometry from well known text data.
    1738             :  *
    1739             :  * The object must have already been instantiated as the correct derived
    1740             :  * type of geometry object to match the text type.  This method is used
    1741             :  * by the OGRGeometryFactory class, but not normally called by application
    1742             :  * code.
    1743             :  *
    1744             :  * This method relates to the SFCOM IWks::ImportFromWKT() method.
    1745             :  *
    1746             :  * This method is the same as the C function OGR_G_ImportFromWkt().
    1747             :  *
    1748             :  * @param ppszInput pointer to a pointer to the source text.  The pointer is
    1749             :  *                    updated to pointer after the consumed text.
    1750             :  *
    1751             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1752             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1753             :  * OGRERR_CORRUPT_DATA may be returned.
    1754             :  */
    1755             : 
    1756             : /************************************************************************/
    1757             : /*                        OGR_G_ImportFromWkt()                         */
    1758             : /************************************************************************/
    1759             : /**
    1760             :  * \brief Assign geometry from well known text data.
    1761             :  *
    1762             :  * The object must have already been instantiated as the correct derived
    1763             :  * type of geometry object to match the text type.
    1764             :  *
    1765             :  * This function relates to the SFCOM IWks::ImportFromWKT() method.
    1766             :  *
    1767             :  * This function is the same as the CPP method OGRGeometry::importFromWkt().
    1768             :  *
    1769             :  * @param hGeom handle on the geometry to assign well know text data to.
    1770             :  * @param ppszSrcText pointer to a pointer to the source text.  The pointer is
    1771             :  *                    updated to pointer after the consumed text.
    1772             :  *
    1773             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1774             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1775             :  * OGRERR_CORRUPT_DATA may be returned.
    1776             :  */
    1777             : 
    1778           0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
    1779             : 
    1780             : {
    1781           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
    1782             : 
    1783           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkt(
    1784           0 :         const_cast<const char **>(ppszSrcText));
    1785             : }
    1786             : 
    1787             : /************************************************************************/
    1788             : /*                       importPreambleFromWkt()                        */
    1789             : /************************************************************************/
    1790             : 
    1791             : // Returns -1 if processing must continue.
    1792             : //! @cond Doxygen_Suppress
    1793      123910 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
    1794             :                                           int *pbHasM, bool *pbIsEmpty)
    1795             : {
    1796      123910 :     const char *pszInput = *ppszInput;
    1797             : 
    1798             :     /* -------------------------------------------------------------------- */
    1799             :     /*      Clear existing Geoms.                                           */
    1800             :     /* -------------------------------------------------------------------- */
    1801      123910 :     empty();
    1802      123910 :     *pbIsEmpty = false;
    1803             : 
    1804             :     /* -------------------------------------------------------------------- */
    1805             :     /*      Read and verify the type keyword, and ensure it matches the     */
    1806             :     /*      actual type of this container.                                  */
    1807             :     /* -------------------------------------------------------------------- */
    1808      123910 :     bool bHasM = false;
    1809      123910 :     bool bHasZ = false;
    1810      123910 :     bool bAlreadyGotDimension = false;
    1811             : 
    1812      123910 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
    1813      123910 :     pszInput = OGRWktReadToken(pszInput, szToken);
    1814      123910 :     if (szToken[0] != '\0')
    1815             :     {
    1816             :         // Postgis EWKT: POINTM instead of POINT M.
    1817             :         // Current QGIS versions (at least <= 3.38) also export POINTZ.
    1818      123910 :         const size_t nTokenLen = strlen(szToken);
    1819      123910 :         if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
    1820             :         {
    1821          11 :             szToken[nTokenLen - 1] = '\0';
    1822          11 :             bHasM = true;
    1823          11 :             bAlreadyGotDimension = true;
    1824             : 
    1825          11 :             if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
    1826           9 :                                   szToken[nTokenLen - 2] == 'z'))
    1827             :             {
    1828           4 :                 bHasZ = true;
    1829           4 :                 szToken[nTokenLen - 2] = '\0';
    1830             :             }
    1831             :         }
    1832      123899 :         else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
    1833             :         {
    1834           6 :             szToken[nTokenLen - 1] = '\0';
    1835           6 :             bHasZ = true;
    1836           6 :             bAlreadyGotDimension = true;
    1837             :         }
    1838             :     }
    1839             : 
    1840      123910 :     if (!EQUAL(szToken, getGeometryName()))
    1841           0 :         return OGRERR_CORRUPT_DATA;
    1842             : 
    1843             :     /* -------------------------------------------------------------------- */
    1844             :     /*      Check for Z, M or ZM                                            */
    1845             :     /* -------------------------------------------------------------------- */
    1846      123910 :     if (!bAlreadyGotDimension)
    1847             :     {
    1848      123893 :         const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1849      123893 :         if (EQUAL(szToken, "Z"))
    1850             :         {
    1851        1418 :             pszInput = pszNewInput;
    1852        1418 :             bHasZ = true;
    1853             :         }
    1854      122475 :         else if (EQUAL(szToken, "M"))
    1855             :         {
    1856         354 :             pszInput = pszNewInput;
    1857         354 :             bHasM = true;
    1858             :         }
    1859      122121 :         else if (EQUAL(szToken, "ZM"))
    1860             :         {
    1861         494 :             pszInput = pszNewInput;
    1862         494 :             bHasZ = true;
    1863         494 :             bHasM = true;
    1864             :         }
    1865             :     }
    1866      123910 :     *pbHasZ = bHasZ;
    1867      123910 :     *pbHasM = bHasM;
    1868             : 
    1869             :     /* -------------------------------------------------------------------- */
    1870             :     /*      Check for EMPTY ...                                             */
    1871             :     /* -------------------------------------------------------------------- */
    1872      123910 :     const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1873      123910 :     if (EQUAL(szToken, "EMPTY"))
    1874             :     {
    1875        1578 :         *ppszInput = pszNewInput;
    1876        1578 :         *pbIsEmpty = true;
    1877        1578 :         if (bHasZ)
    1878         137 :             set3D(TRUE);
    1879        1578 :         if (bHasM)
    1880          84 :             setMeasured(TRUE);
    1881        1578 :         return OGRERR_NONE;
    1882             :     }
    1883             : 
    1884      122332 :     if (!EQUAL(szToken, "("))
    1885          35 :         return OGRERR_CORRUPT_DATA;
    1886             : 
    1887      122297 :     if (!bHasZ && !bHasM)
    1888             :     {
    1889             :         // Test for old-style XXXXXXXXX(EMPTY).
    1890      120194 :         pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1891      120194 :         if (EQUAL(szToken, "EMPTY"))
    1892             :         {
    1893          69 :             pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1894             : 
    1895          69 :             if (EQUAL(szToken, ","))
    1896             :             {
    1897             :                 // This is OK according to SFSQL SPEC.
    1898             :             }
    1899          44 :             else if (!EQUAL(szToken, ")"))
    1900             :             {
    1901           9 :                 return OGRERR_CORRUPT_DATA;
    1902             :             }
    1903             :             else
    1904             :             {
    1905          35 :                 *ppszInput = pszNewInput;
    1906          35 :                 empty();
    1907          35 :                 *pbIsEmpty = true;
    1908          35 :                 return OGRERR_NONE;
    1909             :             }
    1910             :         }
    1911             :     }
    1912             : 
    1913      122253 :     *ppszInput = pszInput;
    1914             : 
    1915      122253 :     return OGRERR_NONE;
    1916             : }
    1917             : 
    1918             : //! @endcond
    1919             : 
    1920             : /************************************************************************/
    1921             : /*                           wktTypeString()                            */
    1922             : /************************************************************************/
    1923             : 
    1924             : //! @cond Doxygen_Suppress
    1925             : /** Get a type string for WKT, padded with a space at the end.
    1926             :  *
    1927             :  * @param variant  OGR type variant
    1928             :  * @return  "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
    1929             :  */
    1930       14571 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
    1931             : {
    1932       14571 :     std::string s(" ");
    1933             : 
    1934       14571 :     if (variant == wkbVariantIso)
    1935             :     {
    1936        9546 :         if (flags & OGR_G_3D)
    1937        1957 :             s += "Z";
    1938        9546 :         if (flags & OGR_G_MEASURED)
    1939        1203 :             s += "M";
    1940             :     }
    1941       14571 :     if (s.size() > 1)
    1942        2521 :         s += " ";
    1943       14571 :     return s;
    1944             : }
    1945             : 
    1946             : //! @endcond
    1947             : 
    1948             : /**
    1949             :  * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
    1950             :  * OGRwkbVariant variant = wkbVariantOldOgc ) const;
    1951             :  *
    1952             :  * \brief Convert a geometry into well known text format.
    1953             :  *
    1954             :  * This method relates to the SFCOM IWks::ExportToWKT() method.
    1955             :  *
    1956             :  * This method is the same as the C function OGR_G_ExportToWkt().
    1957             :  *
    1958             :  * @param ppszDstText a text buffer is allocated by the program, and assigned
    1959             :  *                    to the passed pointer. After use, *ppszDstText should be
    1960             :  *                    freed with CPLFree().
    1961             :  * @param variant the specification that must be conformed too :
    1962             :  *                    - wkbVariantOgc for old-style 99-402 extended
    1963             :  *                      dimension (Z) WKB types
    1964             :  *                    - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
    1965             :  *
    1966             :  * @return Currently OGRERR_NONE is always returned.
    1967             :  */
    1968        8909 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
    1969             : {
    1970        8909 :     OGRWktOptions opts;
    1971        8909 :     opts.variant = variant;
    1972        8909 :     OGRErr err(OGRERR_NONE);
    1973             : 
    1974        8909 :     std::string wkt = exportToWkt(opts, &err);
    1975        8909 :     *ppszDstText = CPLStrdup(wkt.data());
    1976       17818 :     return err;
    1977             : }
    1978             : 
    1979             : /************************************************************************/
    1980             : /*                         OGR_G_ExportToWkt()                          */
    1981             : /************************************************************************/
    1982             : 
    1983             : /**
    1984             :  * \brief Convert a geometry into well known text format.
    1985             :  *
    1986             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    1987             :  *
    1988             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1989             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1990             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1991             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
    1992             :  *
    1993             :  * This function is the same as the CPP method OGRGeometry::exportToWkt().
    1994             :  *
    1995             :  * @param hGeom handle on the geometry to convert to a text format from.
    1996             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    1997             :  *                    to the passed pointer. After use, *ppszDstText should be
    1998             :  *                    freed with CPLFree().
    1999             :  *
    2000             :  * @return Currently OGRERR_NONE is always returned.
    2001             :  */
    2002             : 
    2003        2599 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2004             : 
    2005             : {
    2006        2599 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
    2007             : 
    2008        2599 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
    2009             : }
    2010             : 
    2011             : /************************************************************************/
    2012             : /*                        OGR_G_ExportToIsoWkt()                        */
    2013             : /************************************************************************/
    2014             : 
    2015             : /**
    2016             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
    2017             :  * known text format.
    2018             :  *
    2019             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    2020             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
    2021             :  * (Z&M) WKB types.
    2022             :  *
    2023             :  * This function is the same as the CPP method
    2024             :  * OGRGeometry::exportToWkt(wkbVariantIso).
    2025             :  *
    2026             :  * @param hGeom handle on the geometry to convert to a text format from.
    2027             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    2028             :  *                    to the passed pointer. After use, *ppszDstText should be
    2029             :  *                    freed with CPLFree().
    2030             :  *
    2031             :  * @return Currently OGRERR_NONE is always returned.
    2032             :  *
    2033             :  */
    2034             : 
    2035        5499 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2036             : 
    2037             : {
    2038        5499 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
    2039             : 
    2040        5499 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
    2041        5499 :                                                        wkbVariantIso);
    2042             : }
    2043             : 
    2044             : /**
    2045             :  * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
    2046             :  *
    2047             :  * \brief Fetch geometry type.
    2048             :  *
    2049             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2050             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2051             :  * to the return result.
    2052             :  *
    2053             :  * This method is the same as the C function OGR_G_GetGeometryType().
    2054             :  *
    2055             :  * @return the geometry type code.
    2056             :  */
    2057             : 
    2058             : /************************************************************************/
    2059             : /*                       OGR_G_GetGeometryType()                        */
    2060             : /************************************************************************/
    2061             : /**
    2062             :  * \brief Fetch geometry type.
    2063             :  *
    2064             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2065             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2066             :  * to the return result.
    2067             :  *
    2068             :  * This function is the same as the CPP method OGRGeometry::getGeometryType().
    2069             :  *
    2070             :  * @param hGeom handle on the geometry to get type from.
    2071             :  * @return the geometry type code.
    2072             :  */
    2073             : 
    2074        5818 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
    2075             : 
    2076             : {
    2077        5818 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
    2078             : 
    2079        5818 :     return OGRGeometry::FromHandle(hGeom)->getGeometryType();
    2080             : }
    2081             : 
    2082             : /**
    2083             :  * \fn const char * OGRGeometry::getGeometryName() const;
    2084             :  *
    2085             :  * \brief Fetch WKT name for geometry type.
    2086             :  *
    2087             :  * There is no SFCOM analog to this method.
    2088             :  *
    2089             :  * This method is the same as the C function OGR_G_GetGeometryName().
    2090             :  *
    2091             :  * @return name used for this geometry type in well known text format.  The
    2092             :  * returned pointer is to a static internal string and should not be modified
    2093             :  * or freed.
    2094             :  */
    2095             : 
    2096             : /************************************************************************/
    2097             : /*                       OGR_G_GetGeometryName()                        */
    2098             : /************************************************************************/
    2099             : /**
    2100             :  * \brief Fetch WKT name for geometry type.
    2101             :  *
    2102             :  * There is no SFCOM analog to this function.
    2103             :  *
    2104             :  * This function is the same as the CPP method OGRGeometry::getGeometryName().
    2105             :  *
    2106             :  * @param hGeom handle on the geometry to get name from.
    2107             :  * @return name used for this geometry type in well known text format.
    2108             :  */
    2109             : 
    2110       19014 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
    2111             : 
    2112             : {
    2113       19014 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
    2114             : 
    2115       19014 :     return OGRGeometry::FromHandle(hGeom)->getGeometryName();
    2116             : }
    2117             : 
    2118             : /**
    2119             :  * \fn OGRGeometry *OGRGeometry::clone() const;
    2120             :  *
    2121             :  * \brief Make a copy of this object.
    2122             :  *
    2123             :  * This method relates to the SFCOM IGeometry::clone() method.
    2124             :  *
    2125             :  * This method is the same as the C function OGR_G_Clone().
    2126             :  *
    2127             :  * @return a new object instance with the same geometry, and spatial
    2128             :  * reference system as the original.
    2129             :  */
    2130             : 
    2131             : /************************************************************************/
    2132             : /*                            OGR_G_Clone()                             */
    2133             : /************************************************************************/
    2134             : /**
    2135             :  * \brief Make a copy of this object.
    2136             :  *
    2137             :  * This function relates to the SFCOM IGeometry::clone() method.
    2138             :  *
    2139             :  * This function is the same as the CPP method OGRGeometry::clone().
    2140             :  *
    2141             :  * @param hGeom handle on the geometry to clone from.
    2142             :  * @return a handle on the copy of the geometry with the spatial
    2143             :  * reference system as the original.
    2144             :  */
    2145             : 
    2146       13575 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
    2147             : 
    2148             : {
    2149       13575 :     VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
    2150             : 
    2151       13575 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
    2152             : }
    2153             : 
    2154             : /**
    2155             :  * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
    2156             :  *
    2157             :  * \brief Returns spatial reference system for object.
    2158             :  *
    2159             :  * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
    2160             :  *
    2161             :  * This method is the same as the C function OGR_G_GetSpatialReference().
    2162             :  *
    2163             :  * @return a reference to the spatial reference object.  The object may be
    2164             :  * shared with many geometry objects, and should not be modified.
    2165             :  */
    2166             : 
    2167             : /************************************************************************/
    2168             : /*                     OGR_G_GetSpatialReference()                      */
    2169             : /************************************************************************/
    2170             : /**
    2171             :  * \brief Returns spatial reference system for geometry.
    2172             :  *
    2173             :  * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
    2174             :  *
    2175             :  * This function is the same as the CPP method
    2176             :  * OGRGeometry::getSpatialReference().
    2177             :  *
    2178             :  * @param hGeom handle on the geometry to get spatial reference from.
    2179             :  * @return a reference to the spatial reference geometry, which should not be
    2180             :  * modified.
    2181             :  */
    2182             : 
    2183         126 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
    2184             : 
    2185             : {
    2186         126 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
    2187             : 
    2188         126 :     return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
    2189         252 :         OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
    2190             : }
    2191             : 
    2192             : /**
    2193             :  * \fn void OGRGeometry::empty();
    2194             :  *
    2195             :  * \brief Clear geometry information.
    2196             :  * This restores the geometry to its initial
    2197             :  * state after construction, and before assignment of actual geometry.
    2198             :  *
    2199             :  * This method relates to the SFCOM IGeometry::Empty() method.
    2200             :  *
    2201             :  * This method is the same as the C function OGR_G_Empty().
    2202             :  */
    2203             : 
    2204             : /************************************************************************/
    2205             : /*                            OGR_G_Empty()                             */
    2206             : /************************************************************************/
    2207             : /**
    2208             :  * \brief Clear geometry information.
    2209             :  * This restores the geometry to its initial
    2210             :  * state after construction, and before assignment of actual geometry.
    2211             :  *
    2212             :  * This function relates to the SFCOM IGeometry::Empty() method.
    2213             :  *
    2214             :  * This function is the same as the CPP method OGRGeometry::empty().
    2215             :  *
    2216             :  * @param hGeom handle on the geometry to empty.
    2217             :  */
    2218             : 
    2219           4 : void OGR_G_Empty(OGRGeometryH hGeom)
    2220             : 
    2221             : {
    2222           4 :     VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
    2223             : 
    2224           4 :     OGRGeometry::FromHandle(hGeom)->empty();
    2225             : }
    2226             : 
    2227             : /**
    2228             :  * \fn bool OGRGeometry::IsEmpty() const;
    2229             :  *
    2230             :  * \brief Returns TRUE (non-zero) if the object has no points.
    2231             :  *
    2232             :  * Normally this
    2233             :  * returns FALSE except between when an object is instantiated and points
    2234             :  * have been assigned.
    2235             :  *
    2236             :  * This method relates to the SFCOM IGeometry::IsEmpty() method.
    2237             :  *
    2238             :  * @return TRUE if object is empty, otherwise FALSE.
    2239             :  */
    2240             : 
    2241             : /************************************************************************/
    2242             : /*                           OGR_G_IsEmpty()                            */
    2243             : /************************************************************************/
    2244             : 
    2245             : /**
    2246             :  * \brief Test if the geometry is empty.
    2247             :  *
    2248             :  * This method is the same as the CPP method OGRGeometry::IsEmpty().
    2249             :  *
    2250             :  * @param hGeom The Geometry to test.
    2251             :  *
    2252             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2253             :  */
    2254             : 
    2255        2391 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
    2256             : 
    2257             : {
    2258        2391 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
    2259             : 
    2260        2391 :     return OGRGeometry::FromHandle(hGeom)->IsEmpty();
    2261             : }
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                              IsValid()                               */
    2265             : /************************************************************************/
    2266             : 
    2267             : /**
    2268             :  * \brief Test if the geometry is valid.
    2269             :  *
    2270             :  * This method is the same as the C functions OGR_G_IsValid() and
    2271             :  * OGR_G_GetInvalidityReason().
    2272             :  *
    2273             :  * This method is built on the GEOS library, check it for the definition
    2274             :  * of the geometry operation.
    2275             :  * If OGR is built without the GEOS library, this method will always return
    2276             :  * FALSE.
    2277             :  *
    2278             :  * @param[out] posReason (since 3.13) Pointer to a string to receive the reason
    2279             :  *                       for invalidity, or nullptr. When nullptr, invalidity
    2280             :  *                       reasons are emitted as CPL warnings.
    2281             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2282             :  */
    2283             : 
    2284        2977 : bool OGRGeometry::IsValid(std::string *posReason) const
    2285             : 
    2286             : {
    2287        2977 :     if (posReason)
    2288         851 :         posReason->clear();
    2289             : 
    2290        2977 :     if (IsSFCGALCompatible())
    2291             :     {
    2292             : #ifndef HAVE_SFCGAL
    2293             : 
    2294             : #ifdef HAVE_GEOS
    2295           2 :         if (wkbFlatten(getGeometryType()) == wkbTriangle)
    2296             :         {
    2297             :             // go on
    2298             :         }
    2299             :         else
    2300             : #endif
    2301             :         {
    2302           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2303             :                      "SFCGAL support not enabled.");
    2304           0 :             return FALSE;
    2305             :         }
    2306             : #else
    2307             :         sfcgal_init();
    2308             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    2309             :         if (poThis == nullptr)
    2310             :         {
    2311             :             CPLError(CE_Failure, CPLE_IllegalArg,
    2312             :                      "SFCGAL geometry returned is NULL");
    2313             :             return FALSE;
    2314             :         }
    2315             : 
    2316             :         const int res = sfcgal_geometry_is_valid(poThis);
    2317             :         if (res != 1 && posReason)
    2318             :         {
    2319             :             char *pszReason = nullptr;
    2320             :             sfcgal_geometry_is_valid_detail(poThis, &pszReason, nullptr);
    2321             :             if (pszReason)
    2322             :             {
    2323             :                 *posReason = pszReason;
    2324             :                 free(pszReason);
    2325             :             }
    2326             :             else
    2327             :                 *posReason = "unknown reason";
    2328             :         }
    2329             :         sfcgal_geometry_delete(poThis);
    2330             :         return res == 1;
    2331             : #endif
    2332             :     }
    2333             : 
    2334             :     {
    2335             : #ifndef HAVE_GEOS
    2336             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2337             :         return FALSE;
    2338             : 
    2339             : #else
    2340        2977 :         bool bResult = false;
    2341             : 
    2342             :         // Some invalid geometries, such as lines with one point, or
    2343             :         // rings that do not close, cannot be converted to GEOS.
    2344             :         // For validity checking we initialize the GEOS context with
    2345             :         // the warning handler as the error handler to avoid emitting
    2346             :         // CE_Failure when a geometry cannot be converted to GEOS.
    2347             :         GEOSContextHandle_t hGEOSCtxt =
    2348        2977 :             initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
    2349             : 
    2350             :         GEOSGeom hThisGeosGeom;
    2351        2977 :         if (posReason)
    2352             :         {
    2353        1702 :             CPLErrorAccumulator oAccumulator;
    2354             :             {
    2355         851 :                 auto oContext = oAccumulator.InstallForCurrentScope();
    2356         851 :                 CPL_IGNORE_RET_VAL(oContext);
    2357         851 :                 hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2358             :             }
    2359         851 :             if (!hThisGeosGeom && oAccumulator.GetErrors().size() == 1)
    2360             :             {
    2361           2 :                 std::string msg = oAccumulator.GetErrors()[0].msg;
    2362             : 
    2363             :                 // Trim GEOS exception name
    2364           1 :                 const auto subMsgPos = msg.find(": ");
    2365           1 :                 if (subMsgPos != std::string::npos)
    2366             :                 {
    2367           1 :                     msg = msg.substr(subMsgPos + strlen(": "));
    2368             :                 }
    2369             : 
    2370             :                 // Trim newline from end of GEOS exception message
    2371           1 :                 if (!msg.empty() && msg.back() == '\n')
    2372             :                 {
    2373           1 :                     msg.pop_back();
    2374             :                 }
    2375             : 
    2376           1 :                 *posReason = std::move(msg);
    2377             :             }
    2378             :         }
    2379             :         else
    2380             :         {
    2381        2126 :             hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2382             :         }
    2383             : 
    2384        2977 :         if (hThisGeosGeom != nullptr)
    2385             :         {
    2386        2976 :             if (posReason)
    2387             :             {
    2388        1700 :                 CPLErrorAccumulator oAccumulator;
    2389             :                 {
    2390         850 :                     auto oContext = oAccumulator.InstallForCurrentScope();
    2391         850 :                     CPL_IGNORE_RET_VAL(oContext);
    2392         850 :                     bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2393             :                 }
    2394         850 :                 if (!bResult && oAccumulator.GetErrors().size() == 1)
    2395             :                 {
    2396          27 :                     *posReason = oAccumulator.GetErrors()[0].msg;
    2397             :                 }
    2398             :             }
    2399             :             else
    2400             :             {
    2401        2126 :                 bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2402             :             }
    2403             : #ifdef DEBUG_VERBOSE
    2404             :             if (!bResult && !posReason)
    2405             :             {
    2406             :                 char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
    2407             :                 CPLDebug("OGR", "%s", pszReason);
    2408             :                 GEOSFree_r(hGEOSCtxt, pszReason);
    2409             :             }
    2410             : #endif
    2411        2976 :             GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2412             :         }
    2413        2977 :         freeGEOSContext(hGEOSCtxt);
    2414             : 
    2415        2977 :         return bResult;
    2416             : 
    2417             : #endif  // HAVE_GEOS
    2418             :     }
    2419             : }
    2420             : 
    2421             : /************************************************************************/
    2422             : /*                           OGR_G_IsValid()                            */
    2423             : /************************************************************************/
    2424             : 
    2425             : /**
    2426             :  * \brief Test if the geometry is valid.
    2427             :  *
    2428             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2429             :  *
    2430             :  * This function is built on the GEOS library, check it for the definition
    2431             :  * of the geometry operation.
    2432             :  * If OGR is built without the GEOS library, this function will always return
    2433             :  * FALSE.
    2434             :  *
    2435             :  * If the geometry is invalid, the reason for its invalidity is emitted as a
    2436             :  * CPL warning. To get it in a string instead, use OGR_G_GetInvalidityReason()
    2437             :  *
    2438             :  * @param hGeom The Geometry to test.
    2439             :  *
    2440             :  * @return TRUE if the geometry is valid, otherwise FALSE.
    2441             :  */
    2442             : 
    2443          21 : int OGR_G_IsValid(OGRGeometryH hGeom)
    2444             : 
    2445             : {
    2446          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
    2447             : 
    2448          21 :     return OGRGeometry::FromHandle(hGeom)->IsValid();
    2449             : }
    2450             : 
    2451             : /************************************************************************/
    2452             : /*                     OGR_G_GetInvalidityReason()                      */
    2453             : /************************************************************************/
    2454             : 
    2455             : /**
    2456             :  * \brief Test if the geometry is valid and, if not, return the invalidity reason.
    2457             :  *
    2458             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2459             :  *
    2460             :  * This function is built on the GEOS library, check it for the definition
    2461             :  * of the geometry operation.
    2462             :  * If OGR is built without the GEOS library, this function will always return
    2463             :  * FALSE.
    2464             :  *
    2465             :  * @param hGeom The Geometry to test.
    2466             :  * @return a string with the invalidity reason, to free with CPLFree(),
    2467             :  * if the geometry is invalid, or nullptr if the geometry is valid.
    2468             :  *
    2469             :  * @since 3.13
    2470             :  */
    2471             : 
    2472           3 : char *OGR_G_GetInvalidityReason(OGRGeometryH hGeom)
    2473             : 
    2474             : {
    2475           3 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetInvalidityReason", nullptr);
    2476             : 
    2477           6 :     std::string osReason;
    2478           3 :     const int nRet = OGRGeometry::FromHandle(hGeom)->IsValid(&osReason);
    2479           3 :     if (osReason.empty())
    2480             :     {
    2481           1 :         if (!nRet)
    2482             :         {
    2483             :             // not sure if that can happen
    2484           0 :             return CPLStrdup("unknown reason");
    2485             :         }
    2486             :         else
    2487           1 :             return nullptr;
    2488             :     }
    2489             :     else
    2490             :     {
    2491           2 :         return CPLStrdup(osReason.c_str());
    2492             :     }
    2493             : }
    2494             : 
    2495             : /************************************************************************/
    2496             : /*                              IsSimple()                              */
    2497             : /************************************************************************/
    2498             : 
    2499             : /**
    2500             :  * \brief Test if the geometry is simple.
    2501             :  *
    2502             :  * This method is the same as the C function OGR_G_IsSimple().
    2503             :  *
    2504             :  * This method is built on the GEOS library, check it for the definition
    2505             :  * of the geometry operation.
    2506             :  * If OGR is built without the GEOS library, this method will always return
    2507             :  * FALSE.
    2508             :  *
    2509             :  *
    2510             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2511             :  */
    2512             : 
    2513           5 : bool OGRGeometry::IsSimple() const
    2514             : 
    2515             : {
    2516             : #ifndef HAVE_GEOS
    2517             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2518             :     return FALSE;
    2519             : 
    2520             : #else
    2521             : 
    2522           5 :     bool bResult = false;
    2523             : 
    2524           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2525           5 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2526             : 
    2527           5 :     if (hThisGeosGeom != nullptr)
    2528             :     {
    2529           5 :         bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2530           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2531             :     }
    2532           5 :     freeGEOSContext(hGEOSCtxt);
    2533             : 
    2534           5 :     return bResult;
    2535             : 
    2536             : #endif  // HAVE_GEOS
    2537             : }
    2538             : 
    2539             : /**
    2540             :  * \brief Returns TRUE if the geometry is simple.
    2541             :  *
    2542             :  * Returns TRUE if the geometry has no anomalous geometric points, such
    2543             :  * as self intersection or self tangency. The description of each
    2544             :  * instantiable geometric class will include the specific conditions that
    2545             :  * cause an instance of that class to be classified as not simple.
    2546             :  *
    2547             :  * This function is the same as the C++ method OGRGeometry::IsSimple() method.
    2548             :  *
    2549             :  * If OGR is built without the GEOS library, this function will always return
    2550             :  * FALSE.
    2551             :  *
    2552             :  * @param hGeom The Geometry to test.
    2553             :  *
    2554             :  * @return TRUE if object is simple, otherwise FALSE.
    2555             :  */
    2556             : 
    2557           5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
    2558             : 
    2559             : {
    2560           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
    2561             : 
    2562           5 :     return OGRGeometry::FromHandle(hGeom)->IsSimple();
    2563             : }
    2564             : 
    2565             : /************************************************************************/
    2566             : /*                               IsRing()                               */
    2567             : /************************************************************************/
    2568             : 
    2569             : /**
    2570             :  * \brief Test if the geometry is a ring
    2571             :  *
    2572             :  * This method is the same as the C function OGR_G_IsRing().
    2573             :  *
    2574             :  * This method is built on the GEOS library, check it for the definition
    2575             :  * of the geometry operation.
    2576             :  * If OGR is built without the GEOS library, this method will always return
    2577             :  * FALSE.
    2578             :  *
    2579             :  *
    2580             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2581             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2582             :  */
    2583             : 
    2584           1 : bool OGRGeometry::IsRing() const
    2585             : 
    2586             : {
    2587             : #ifndef HAVE_GEOS
    2588             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2589             :     return FALSE;
    2590             : 
    2591             : #else
    2592             : 
    2593           1 :     bool bResult = false;
    2594             : 
    2595           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2596           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2597             : 
    2598           1 :     if (hThisGeosGeom != nullptr)
    2599             :     {
    2600           1 :         bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2601           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2602             :     }
    2603           1 :     freeGEOSContext(hGEOSCtxt);
    2604             : 
    2605           1 :     return bResult;
    2606             : 
    2607             : #endif  // HAVE_GEOS
    2608             : }
    2609             : 
    2610             : /************************************************************************/
    2611             : /*                            OGR_G_IsRing()                            */
    2612             : /************************************************************************/
    2613             : 
    2614             : /**
    2615             :  * \brief Test if the geometry is a ring
    2616             :  *
    2617             :  * This function is the same as the C++ method OGRGeometry::IsRing().
    2618             :  *
    2619             :  * This function is built on the GEOS library, check it for the definition
    2620             :  * of the geometry operation.
    2621             :  * If OGR is built without the GEOS library, this function will always return
    2622             :  * FALSE.
    2623             :  *
    2624             :  * @param hGeom The Geometry to test.
    2625             :  *
    2626             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2627             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2628             :  */
    2629             : 
    2630           1 : int OGR_G_IsRing(OGRGeometryH hGeom)
    2631             : 
    2632             : {
    2633           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
    2634             : 
    2635           1 :     return OGRGeometry::FromHandle(hGeom)->IsRing();
    2636             : }
    2637             : 
    2638             : /************************************************************************/
    2639             : /*                         OGRFromOGCGeomType()                         */
    2640             : /************************************************************************/
    2641             : 
    2642             : /** Map OGC geometry format type to corresponding OGR constants.
    2643             :  * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
    2644             :  * @return OGR constant.
    2645             :  */
    2646        3272 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
    2647             : {
    2648        3272 :     OGRwkbGeometryType eType = wkbUnknown;
    2649        3272 :     bool bConvertTo3D = false;
    2650        3272 :     bool bIsMeasured = false;
    2651        3272 :     if (*pszGeomType != '\0')
    2652             :     {
    2653        3266 :         char ch = pszGeomType[strlen(pszGeomType) - 1];
    2654        3266 :         if (ch == 'm' || ch == 'M')
    2655             :         {
    2656           2 :             bIsMeasured = true;
    2657           2 :             if (strlen(pszGeomType) > 1)
    2658           2 :                 ch = pszGeomType[strlen(pszGeomType) - 2];
    2659             :         }
    2660        3266 :         if (ch == 'z' || ch == 'Z')
    2661             :         {
    2662          34 :             bConvertTo3D = true;
    2663             :         }
    2664             :     }
    2665             : 
    2666        3272 :     if (STARTS_WITH_CI(pszGeomType, "POINT"))
    2667         971 :         eType = wkbPoint;
    2668        2301 :     else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
    2669         186 :         eType = wkbLineString;
    2670        2115 :     else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
    2671         375 :         eType = wkbPolygon;
    2672        1740 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
    2673          24 :         eType = wkbMultiPoint;
    2674        1716 :     else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
    2675          15 :         eType = wkbMultiLineString;
    2676        1701 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
    2677         651 :         eType = wkbMultiPolygon;
    2678        1050 :     else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
    2679           4 :         eType = wkbGeometryCollection;
    2680        1046 :     else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
    2681         356 :         eType = wkbCircularString;
    2682         690 :     else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
    2683           0 :         eType = wkbCompoundCurve;
    2684         690 :     else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
    2685          16 :         eType = wkbCurvePolygon;
    2686         674 :     else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
    2687           2 :         eType = wkbMultiCurve;
    2688         672 :     else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
    2689           0 :         eType = wkbMultiSurface;
    2690         672 :     else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
    2691           0 :         eType = wkbTriangle;
    2692         672 :     else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
    2693           1 :         eType = wkbPolyhedralSurface;
    2694         671 :     else if (STARTS_WITH_CI(pszGeomType, "TIN"))
    2695           5 :         eType = wkbTIN;
    2696         666 :     else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
    2697           3 :         eType = wkbCurve;
    2698         663 :     else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
    2699           3 :         eType = wkbSurface;
    2700             :     else
    2701         660 :         eType = wkbUnknown;
    2702             : 
    2703        3272 :     if (bConvertTo3D)
    2704          34 :         eType = wkbSetZ(eType);
    2705        3272 :     if (bIsMeasured)
    2706           2 :         eType = wkbSetM(eType);
    2707             : 
    2708        3272 :     return eType;
    2709             : }
    2710             : 
    2711             : /************************************************************************/
    2712             : /*                          OGRToOGCGeomType()                          */
    2713             : /************************************************************************/
    2714             : 
    2715             : /** Map OGR geometry format constants to corresponding OGC geometry type.
    2716             :  * @param eGeomType OGR geometry type
    2717             :  * @param bCamelCase Whether the return should be like "MultiPoint"
    2718             :  *        (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
    2719             :  * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
    2720             :  *               Default is false.
    2721             :  * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
    2722             :  *                       suffix. Default is false.
    2723             :  * @return string with OGC geometry type (without dimensionality)
    2724             :  */
    2725        3172 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
    2726             :                              bool bAddZM, bool bSpaceBeforeZM)
    2727             : {
    2728        3172 :     const char *pszRet = "";
    2729        3172 :     switch (wkbFlatten(eGeomType))
    2730             :     {
    2731        1709 :         case wkbUnknown:
    2732        1709 :             pszRet = "Geometry";
    2733        1709 :             break;
    2734         562 :         case wkbPoint:
    2735         562 :             pszRet = "Point";
    2736         562 :             break;
    2737         140 :         case wkbLineString:
    2738         140 :             pszRet = "LineString";
    2739         140 :             break;
    2740         186 :         case wkbPolygon:
    2741         186 :             pszRet = "Polygon";
    2742         186 :             break;
    2743          49 :         case wkbMultiPoint:
    2744          49 :             pszRet = "MultiPoint";
    2745          49 :             break;
    2746          76 :         case wkbMultiLineString:
    2747          76 :             pszRet = "MultiLineString";
    2748          76 :             break;
    2749         293 :         case wkbMultiPolygon:
    2750         293 :             pszRet = "MultiPolygon";
    2751         293 :             break;
    2752          54 :         case wkbGeometryCollection:
    2753          54 :             pszRet = "GeometryCollection";
    2754          54 :             break;
    2755           9 :         case wkbCircularString:
    2756           9 :             pszRet = "CircularString";
    2757           9 :             break;
    2758           3 :         case wkbCompoundCurve:
    2759           3 :             pszRet = "CompoundCurve";
    2760           3 :             break;
    2761          12 :         case wkbCurvePolygon:
    2762          12 :             pszRet = "CurvePolygon";
    2763          12 :             break;
    2764           2 :         case wkbMultiCurve:
    2765           2 :             pszRet = "MultiCurve";
    2766           2 :             break;
    2767           3 :         case wkbMultiSurface:
    2768           3 :             pszRet = "MultiSurface";
    2769           3 :             break;
    2770           3 :         case wkbTriangle:
    2771           3 :             pszRet = "Triangle";
    2772           3 :             break;
    2773           5 :         case wkbPolyhedralSurface:
    2774           5 :             pszRet = "PolyhedralSurface";
    2775           5 :             break;
    2776           1 :         case wkbTIN:
    2777           1 :             pszRet = "Tin";
    2778           1 :             break;
    2779           3 :         case wkbCurve:
    2780           3 :             pszRet = "Curve";
    2781           3 :             break;
    2782           3 :         case wkbSurface:
    2783           3 :             pszRet = "Surface";
    2784           3 :             break;
    2785          59 :         default:
    2786          59 :             break;
    2787             :     }
    2788        3172 :     if (bAddZM)
    2789             :     {
    2790          71 :         const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    2791          71 :         const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    2792          71 :         if (bHasZ || bHasM)
    2793             :         {
    2794          10 :             if (bSpaceBeforeZM)
    2795           1 :                 pszRet = CPLSPrintf("%s ", pszRet);
    2796          10 :             if (bHasZ)
    2797           9 :                 pszRet = CPLSPrintf("%sZ", pszRet);
    2798          10 :             if (bHasM)
    2799           5 :                 pszRet = CPLSPrintf("%sM", pszRet);
    2800             :         }
    2801             :     }
    2802        3172 :     if (!bCamelCase)
    2803        3100 :         pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
    2804        3172 :     return pszRet;
    2805             : }
    2806             : 
    2807             : /************************************************************************/
    2808             : /*                       OGRGeometryTypeToName()                        */
    2809             : /************************************************************************/
    2810             : 
    2811             : /**
    2812             :  * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
    2813             :  * value.  The returned value should not be modified, or freed by the
    2814             :  * application.
    2815             :  *
    2816             :  * This function is C callable.
    2817             :  *
    2818             :  * @param eType the geometry type.
    2819             :  *
    2820             :  * @return internal human readable string, or NULL on failure.
    2821             :  */
    2822             : 
    2823         399 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
    2824             : 
    2825             : {
    2826         399 :     bool b3D = wkbHasZ(eType);
    2827         399 :     bool bMeasured = wkbHasM(eType);
    2828             : 
    2829         399 :     switch (wkbFlatten(eType))
    2830             :     {
    2831          34 :         case wkbUnknown:
    2832          34 :             if (b3D && bMeasured)
    2833           0 :                 return "3D Measured Unknown (any)";
    2834          34 :             else if (b3D)
    2835           1 :                 return "3D Unknown (any)";
    2836          33 :             else if (bMeasured)
    2837           0 :                 return "Measured Unknown (any)";
    2838             :             else
    2839          33 :                 return "Unknown (any)";
    2840             : 
    2841          59 :         case wkbPoint:
    2842          59 :             if (b3D && bMeasured)
    2843           3 :                 return "3D Measured Point";
    2844          56 :             else if (b3D)
    2845          12 :                 return "3D Point";
    2846          44 :             else if (bMeasured)
    2847           5 :                 return "Measured Point";
    2848             :             else
    2849          39 :                 return "Point";
    2850             : 
    2851          21 :         case wkbLineString:
    2852          21 :             if (b3D && bMeasured)
    2853           0 :                 return "3D Measured Line String";
    2854          21 :             else if (b3D)
    2855           9 :                 return "3D Line String";
    2856          12 :             else if (bMeasured)
    2857           0 :                 return "Measured Line String";
    2858             :             else
    2859          12 :                 return "Line String";
    2860             : 
    2861          24 :         case wkbPolygon:
    2862          24 :             if (b3D && bMeasured)
    2863           0 :                 return "3D Measured Polygon";
    2864          24 :             else if (b3D)
    2865           8 :                 return "3D Polygon";
    2866          16 :             else if (bMeasured)
    2867           0 :                 return "Measured Polygon";
    2868             :             else
    2869          16 :                 return "Polygon";
    2870             : 
    2871          21 :         case wkbMultiPoint:
    2872          21 :             if (b3D && bMeasured)
    2873           0 :                 return "3D Measured Multi Point";
    2874          21 :             else if (b3D)
    2875           9 :                 return "3D Multi Point";
    2876          12 :             else if (bMeasured)
    2877           0 :                 return "Measured Multi Point";
    2878             :             else
    2879          12 :                 return "Multi Point";
    2880             : 
    2881          32 :         case wkbMultiLineString:
    2882          32 :             if (b3D && bMeasured)
    2883           0 :                 return "3D Measured Multi Line String";
    2884          32 :             else if (b3D)
    2885           6 :                 return "3D Multi Line String";
    2886          26 :             else if (bMeasured)
    2887           0 :                 return "Measured Multi Line String";
    2888             :             else
    2889          26 :                 return "Multi Line String";
    2890             : 
    2891          59 :         case wkbMultiPolygon:
    2892          59 :             if (b3D && bMeasured)
    2893           0 :                 return "3D Measured Multi Polygon";
    2894          59 :             else if (b3D)
    2895           8 :                 return "3D Multi Polygon";
    2896          51 :             else if (bMeasured)
    2897           0 :                 return "Measured Multi Polygon";
    2898             :             else
    2899          51 :                 return "Multi Polygon";
    2900             : 
    2901          25 :         case wkbGeometryCollection:
    2902          25 :             if (b3D && bMeasured)
    2903           0 :                 return "3D Measured Geometry Collection";
    2904          25 :             else if (b3D)
    2905          10 :                 return "3D Geometry Collection";
    2906          15 :             else if (bMeasured)
    2907           0 :                 return "Measured Geometry Collection";
    2908             :             else
    2909          15 :                 return "Geometry Collection";
    2910             : 
    2911           0 :         case wkbCircularString:
    2912           0 :             if (b3D && bMeasured)
    2913           0 :                 return "3D Measured Circular String";
    2914           0 :             else if (b3D)
    2915           0 :                 return "3D Circular String";
    2916           0 :             else if (bMeasured)
    2917           0 :                 return "Measured Circular String";
    2918             :             else
    2919           0 :                 return "Circular String";
    2920             : 
    2921           1 :         case wkbCompoundCurve:
    2922           1 :             if (b3D && bMeasured)
    2923           0 :                 return "3D Measured Compound Curve";
    2924           1 :             else if (b3D)
    2925           0 :                 return "3D Compound Curve";
    2926           1 :             else if (bMeasured)
    2927           0 :                 return "Measured Compound Curve";
    2928             :             else
    2929           1 :                 return "Compound Curve";
    2930             : 
    2931           0 :         case wkbCurvePolygon:
    2932           0 :             if (b3D && bMeasured)
    2933           0 :                 return "3D Measured Curve Polygon";
    2934           0 :             else if (b3D)
    2935           0 :                 return "3D Curve Polygon";
    2936           0 :             else if (bMeasured)
    2937           0 :                 return "Measured Curve Polygon";
    2938             :             else
    2939           0 :                 return "Curve Polygon";
    2940             : 
    2941           0 :         case wkbMultiCurve:
    2942           0 :             if (b3D && bMeasured)
    2943           0 :                 return "3D Measured Multi Curve";
    2944           0 :             else if (b3D)
    2945           0 :                 return "3D Multi Curve";
    2946           0 :             else if (bMeasured)
    2947           0 :                 return "Measured Multi Curve";
    2948             :             else
    2949           0 :                 return "Multi Curve";
    2950             : 
    2951           0 :         case wkbMultiSurface:
    2952           0 :             if (b3D && bMeasured)
    2953           0 :                 return "3D Measured Multi Surface";
    2954           0 :             else if (b3D)
    2955           0 :                 return "3D Multi Surface";
    2956           0 :             else if (bMeasured)
    2957           0 :                 return "Measured Multi Surface";
    2958             :             else
    2959           0 :                 return "Multi Surface";
    2960             : 
    2961           4 :         case wkbCurve:
    2962           4 :             if (b3D && bMeasured)
    2963           1 :                 return "3D Measured Curve";
    2964           3 :             else if (b3D)
    2965           1 :                 return "3D Curve";
    2966           2 :             else if (bMeasured)
    2967           1 :                 return "Measured Curve";
    2968             :             else
    2969           1 :                 return "Curve";
    2970             : 
    2971           4 :         case wkbSurface:
    2972           4 :             if (b3D && bMeasured)
    2973           1 :                 return "3D Measured Surface";
    2974           3 :             else if (b3D)
    2975           1 :                 return "3D Surface";
    2976           2 :             else if (bMeasured)
    2977           1 :                 return "Measured Surface";
    2978             :             else
    2979           1 :                 return "Surface";
    2980             : 
    2981           0 :         case wkbTriangle:
    2982           0 :             if (b3D && bMeasured)
    2983           0 :                 return "3D Measured Triangle";
    2984           0 :             else if (b3D)
    2985           0 :                 return "3D Triangle";
    2986           0 :             else if (bMeasured)
    2987           0 :                 return "Measured Triangle";
    2988             :             else
    2989           0 :                 return "Triangle";
    2990             : 
    2991           0 :         case wkbPolyhedralSurface:
    2992           0 :             if (b3D && bMeasured)
    2993           0 :                 return "3D Measured PolyhedralSurface";
    2994           0 :             else if (b3D)
    2995           0 :                 return "3D PolyhedralSurface";
    2996           0 :             else if (bMeasured)
    2997           0 :                 return "Measured PolyhedralSurface";
    2998             :             else
    2999           0 :                 return "PolyhedralSurface";
    3000             : 
    3001           2 :         case wkbTIN:
    3002           2 :             if (b3D && bMeasured)
    3003           0 :                 return "3D Measured TIN";
    3004           2 :             else if (b3D)
    3005           0 :                 return "3D TIN";
    3006           2 :             else if (bMeasured)
    3007           0 :                 return "Measured TIN";
    3008             :             else
    3009           2 :                 return "TIN";
    3010             : 
    3011         112 :         case wkbNone:
    3012         112 :             return "None";
    3013             : 
    3014           1 :         default:
    3015             :         {
    3016           1 :             return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
    3017             :         }
    3018             :     }
    3019             : }
    3020             : 
    3021             : /************************************************************************/
    3022             : /*                       OGRMergeGeometryTypes()                        */
    3023             : /************************************************************************/
    3024             : 
    3025             : /**
    3026             :  * \brief Find common geometry type.
    3027             :  *
    3028             :  * Given two geometry types, find the most specific common
    3029             :  * type.  Normally used repeatedly with the geometries in a
    3030             :  * layer to try and establish the most specific geometry type
    3031             :  * that can be reported for the layer.
    3032             :  *
    3033             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    3034             :  * geometry types with nothing in common but the base geometry
    3035             :  * type.  wkbNone should be used to indicate that no geometries
    3036             :  * have been encountered yet, and means the first geometry
    3037             :  * encountered will establish the preliminary type.
    3038             :  *
    3039             :  * @param eMain the first input geometry type.
    3040             :  * @param eExtra the second input geometry type.
    3041             :  *
    3042             :  * @return the merged geometry type.
    3043             :  */
    3044             : 
    3045           0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
    3046             :                                          OGRwkbGeometryType eExtra)
    3047             : 
    3048             : {
    3049           0 :     return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
    3050             : }
    3051             : 
    3052             : /**
    3053             :  * \brief Find common geometry type.
    3054             :  *
    3055             :  * Given two geometry types, find the most specific common
    3056             :  * type.  Normally used repeatedly with the geometries in a
    3057             :  * layer to try and establish the most specific geometry type
    3058             :  * that can be reported for the layer.
    3059             :  *
    3060             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    3061             :  * geometry types with nothing in common but the base geometry
    3062             :  * type.  wkbNone should be used to indicate that no geometries
    3063             :  * have been encountered yet, and means the first geometry
    3064             :  * encountered will establish the preliminary type.
    3065             :  *
    3066             :  * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
    3067             :  * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
    3068             :  * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
    3069             :  * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
    3070             :  *
    3071             :  * @param eMain the first input geometry type.
    3072             :  * @param eExtra the second input geometry type.
    3073             :  * @param bAllowPromotingToCurves determine if promotion to curve type
    3074             :  * must be done.
    3075             :  *
    3076             :  * @return the merged geometry type.
    3077             :  *
    3078             :  */
    3079             : 
    3080         585 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
    3081             :                                            OGRwkbGeometryType eExtra,
    3082             :                                            int bAllowPromotingToCurves)
    3083             : 
    3084             : {
    3085         585 :     OGRwkbGeometryType eFMain = wkbFlatten(eMain);
    3086         585 :     OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
    3087             : 
    3088         585 :     const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
    3089         585 :     const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
    3090             : 
    3091         585 :     if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
    3092          17 :         return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3093             : 
    3094         568 :     if (eFMain == wkbNone)
    3095           2 :         return eExtra;
    3096             : 
    3097         566 :     if (eFExtra == wkbNone)
    3098           0 :         return eMain;
    3099             : 
    3100         566 :     if (eFMain == eFExtra)
    3101             :     {
    3102         544 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3103             :     }
    3104             : 
    3105          22 :     if (bAllowPromotingToCurves)
    3106             :     {
    3107          22 :         if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
    3108           5 :             return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
    3109             :     }
    3110             : 
    3111             :     // One is subclass of the other one
    3112          17 :     if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3113             :     {
    3114           4 :         return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3115             :     }
    3116          13 :     else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3117             :     {
    3118           6 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3119             :     }
    3120             : 
    3121           7 :     if (OGR_GT_GetSingle(eFMain) == eFExtra)
    3122             :     {
    3123           4 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3124             :     }
    3125           3 :     else if (OGR_GT_GetSingle(eFExtra) == eFMain)
    3126             :     {
    3127           1 :         return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3128             :     }
    3129             : 
    3130             :     // Nothing apparently in common.
    3131           2 :     return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3132             : }
    3133             : 
    3134             : /**
    3135             :  * \fn void OGRGeometry::flattenTo2D();
    3136             :  *
    3137             :  * \brief Convert geometry to strictly 2D.
    3138             :  * In a sense this converts all Z coordinates
    3139             :  * to 0.0.
    3140             :  *
    3141             :  * This method is the same as the C function OGR_G_FlattenTo2D().
    3142             :  */
    3143             : 
    3144             : /************************************************************************/
    3145             : /*                         OGR_G_FlattenTo2D()                          */
    3146             : /************************************************************************/
    3147             : /**
    3148             :  * \brief Convert geometry to strictly 2D.
    3149             :  * In a sense this converts all Z coordinates
    3150             :  * to 0.0.
    3151             :  *
    3152             :  * This function is the same as the CPP method OGRGeometry::flattenTo2D().
    3153             :  *
    3154             :  * @param hGeom handle on the geometry to convert.
    3155             :  */
    3156             : 
    3157          31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
    3158             : 
    3159             : {
    3160          31 :     OGRGeometry::FromHandle(hGeom)->flattenTo2D();
    3161          31 : }
    3162             : 
    3163             : /************************************************************************/
    3164             : /*                            exportToGML()                             */
    3165             : /************************************************************************/
    3166             : 
    3167             : /**
    3168             :  * \fn char *OGRGeometry::exportToGML( const char* const *
    3169             :  * papszOptions = NULL ) const;
    3170             :  *
    3171             :  * \brief Convert a geometry into GML format.
    3172             :  *
    3173             :  * The GML geometry is expressed directly in terms of GML basic data
    3174             :  * types assuming the this is available in the gml namespace.  The returned
    3175             :  * string should be freed with CPLFree() when no longer required.
    3176             :  *
    3177             :  * The supported options are :
    3178             :  * <ul>
    3179             :  * <li> FORMAT=GML2/GML3/GML32.
    3180             :  *      If not set, it will default to GML 2.1.2 output.
    3181             :  * </li>
    3182             :  * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
    3183             :  *      To use gml:Curve element for linestrings.
    3184             :  *      Otherwise gml:LineString will be used .
    3185             :  * </li>
    3186             :  * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
    3187             :  *      SRSNAME_FORMAT in GDAL &gt;=2.2). Defaults to YES.
    3188             :  *      If YES, SRS with EPSG authority will be written with the
    3189             :  *      "urn:ogc:def:crs:EPSG::" prefix.
    3190             :  *      In the case the SRS should be treated as lat/long or
    3191             :  *      northing/easting, then the function will take care of coordinate order
    3192             :  *      swapping if the data axis to CRS axis mapping indicates it.
    3193             :  *      If set to NO, SRS with EPSG authority will be written with the "EPSG:"
    3194             :  *      prefix, even if they are in lat/long order.
    3195             :  * </li>
    3196             :  * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
    3197             :  *      Defaults to OGC_URN.  If SHORT, then srsName will be in
    3198             :  *      the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
    3199             :  *      in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
    3200             :  *      then srsName will be in the form
    3201             :  *      http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
    3202             :  *      OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
    3203             :  *      or northing/easting, then the function will take care of coordinate
    3204             :  *      order swapping if the data axis to CRS axis mapping indicates it.
    3205             :  * </li>
    3206             :  * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
    3207             :  *      top-level geometry element with the provided value.
    3208             :  *      Required for GML 3.2 compatibility.
    3209             :  * </li>
    3210             :  * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
    3211             :  *      FORMAT=GML3/GML32) Default to POSLIST.
    3212             :  *      For 2.5D geometries, define the location where to attach the
    3213             :  *      srsDimension attribute.
    3214             :  *      There are diverging implementations. Some put in on the
    3215             :  *      &lt;gml:posList&gt; element, other on the top geometry element.
    3216             :  * </li>
    3217             :  * <li> NAMESPACE_DECL=YES/NO. If set to YES,
    3218             :  *      xmlns:gml="http://www.opengis.net/gml" will be added to the root node
    3219             :  *      for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
    3220             :  * </li>
    3221             :  * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
    3222             :  *      Resolution for the coordinate precision of the X and Y coordinates.
    3223             :  *      Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
    3224             :  *      to 5 decimal digits. 0 for the default behavior.
    3225             :  * </li>
    3226             :  * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
    3227             :  *      Resolution for the coordinate precision of the Z coordinates.
    3228             :  *      Expressed in the units of the Z axis of the SRS.
    3229             :  *      0 for the default behavior.
    3230             :  * </li>
    3231             :  * </ul>
    3232             :  *
    3233             :  * This method is the same as the C function OGR_G_ExportToGMLEx().
    3234             :  *
    3235             :  * @param papszOptions NULL-terminated list of options.
    3236             :  * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
    3237             :  */
    3238             : 
    3239         265 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
    3240             : {
    3241         265 :     return OGR_G_ExportToGMLEx(
    3242             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
    3243         265 :         const_cast<char **>(papszOptions));
    3244             : }
    3245             : 
    3246             : /************************************************************************/
    3247             : /*                            exportToKML()                             */
    3248             : /************************************************************************/
    3249             : 
    3250             : /**
    3251             :  * \fn char *OGRGeometry::exportToKML() const;
    3252             :  *
    3253             :  * \brief Convert a geometry into KML format.
    3254             :  *
    3255             :  * The returned string should be freed with CPLFree() when no longer required.
    3256             :  *
    3257             :  * This method is the same as the C function OGR_G_ExportToKML().
    3258             :  *
    3259             :  * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
    3260             :  */
    3261             : 
    3262           0 : char *OGRGeometry::exportToKML() const
    3263             : {
    3264           0 :     return OGR_G_ExportToKML(
    3265           0 :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
    3266             : }
    3267             : 
    3268             : /************************************************************************/
    3269             : /*                            exportToJson()                            */
    3270             : /************************************************************************/
    3271             : 
    3272             : /**
    3273             :  * \fn char *OGRGeometry::exportToJson() const;
    3274             :  *
    3275             :  * \brief Convert a geometry into GeoJSON format.
    3276             :  *
    3277             :  * The returned string should be freed with CPLFree() when no longer required.
    3278             :  *
    3279             :  * The following options are supported :
    3280             :  * <ul>
    3281             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
    3282             :  * (added in GDAL 3.9)</li>
    3283             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
    3284             :  * (added in GDAL 3.9)</li>
    3285             :  * </ul>
    3286             :  *
    3287             :  * This method is the same as the C function OGR_G_ExportToJson().
    3288             :  *
    3289             :  * @param papszOptions Null terminated list of options, or null (added in 3.9)
    3290             :  * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
    3291             :  */
    3292             : 
    3293          43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
    3294             : {
    3295          43 :     OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
    3296          43 :     return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
    3297          43 :                                 const_cast<char **>(papszOptions));
    3298             : }
    3299             : 
    3300             : /************************************************************************/
    3301             : /*                 OGRSetGenerate_DB2_V72_BYTE_ORDER()                  */
    3302             : /************************************************************************/
    3303             : 
    3304             : /**
    3305             :  * \brief Special entry point to enable the hack for generating DB2 V7.2 style
    3306             :  * WKB.
    3307             :  *
    3308             :  * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
    3309             :  * order in WKB.  This entry point is used to turn on or off the generation of
    3310             :  * such WKB.
    3311             :  */
    3312           4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
    3313             : 
    3314             : {
    3315             : #if defined(HACK_FOR_IBM_DB2_V72)
    3316           4 :     OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
    3317           4 :     return OGRERR_NONE;
    3318             : #else
    3319             :     if (bGenerate_DB2_V72_BYTE_ORDER)
    3320             :         return OGRERR_FAILURE;
    3321             :     else
    3322             :         return OGRERR_NONE;
    3323             : #endif
    3324             : }
    3325             : 
    3326             : /************************************************************************/
    3327             : /*                 OGRGetGenerate_DB2_V72_BYTE_ORDER()                  */
    3328             : /*                                                                      */
    3329             : /*      This is a special entry point to get the value of static flag   */
    3330             : /*      OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER.                      */
    3331             : /************************************************************************/
    3332           0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
    3333             : {
    3334           0 :     return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
    3335             : }
    3336             : 
    3337             : /************************************************************************/
    3338             : /*                         createGEOSContext()                          */
    3339             : /************************************************************************/
    3340             : 
    3341             : /** Create a new GEOS context.
    3342             :  * @return a new GEOS context (to be freed with freeGEOSContext())
    3343             :  */
    3344       86741 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
    3345             : {
    3346             : #ifndef HAVE_GEOS
    3347             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3348             :     return nullptr;
    3349             : #else
    3350       86741 :     return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
    3351             : #endif
    3352             : }
    3353             : 
    3354             : /************************************************************************/
    3355             : /*                          freeGEOSContext()                           */
    3356             : /************************************************************************/
    3357             : 
    3358             : /** Destroy a GEOS context.
    3359             :  * @param hGEOSCtxt GEOS context
    3360             :  */
    3361       84068 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
    3362             : {
    3363             :     (void)hGEOSCtxt;
    3364             : #ifdef HAVE_GEOS
    3365       84068 :     if (hGEOSCtxt != nullptr)
    3366             :     {
    3367       84068 :         finishGEOS_r(hGEOSCtxt);
    3368             :     }
    3369             : #endif
    3370       84068 : }
    3371             : #ifdef HAVE_GEOS
    3372             : 
    3373             : /************************************************************************/
    3374             : /*                      canConvertToMultiPolygon()                      */
    3375             : /************************************************************************/
    3376             : 
    3377         155 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
    3378             : {
    3379         686 :     for (const auto *poSubGeom : *poGC)
    3380             :     {
    3381             :         const OGRwkbGeometryType eSubGeomType =
    3382         613 :             wkbFlatten(poSubGeom->getGeometryType());
    3383         613 :         if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
    3384         423 :             eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
    3385             :         {
    3386          82 :             return false;
    3387             :         }
    3388             :     }
    3389             : 
    3390          73 :     return true;
    3391             : }
    3392             : 
    3393             : /************************************************************************/
    3394             : /*                         GEOSWarningSilencer                          */
    3395             : /************************************************************************/
    3396             : 
    3397             : /** Class that can be used to silence GEOS messages while in-scope. */
    3398             : class GEOSWarningSilencer
    3399             : {
    3400             :   public:
    3401         152 :     explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
    3402         152 :         : m_poContext(poContext)
    3403             :     {
    3404         152 :         GEOSContext_setErrorHandler_r(m_poContext, nullptr);
    3405         152 :         GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
    3406         152 :     }
    3407             : 
    3408         152 :     ~GEOSWarningSilencer()
    3409         152 :     {
    3410         152 :         GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
    3411         152 :         GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
    3412         152 :     }
    3413             : 
    3414             :     CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
    3415             : 
    3416             :   private:
    3417             :     GEOSContextHandle_t m_poContext{nullptr};
    3418             : };
    3419             : 
    3420             : /************************************************************************/
    3421             : /*                           repairForGEOS()                            */
    3422             : /************************************************************************/
    3423             : 
    3424             : /** Modify an OGRGeometry so that it can be converted into GEOS.
    3425             :  *  Modifications include closing unclosed rings and adding redundant vertices
    3426             :  *  to reach minimum point limits in GEOS.
    3427             :  *
    3428             :  *  It is assumed that the input is a non-curved type that can be
    3429             :  *  represented in GEOS.
    3430             :  *
    3431             :  * @param poGeom the geometry to modify
    3432             :  * @return an OGRGeometry that can be converted to GEOS using WKB
    3433             :  */
    3434          22 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
    3435             : {
    3436             : #if GEOS_VERSION_MAJOR >= 3 ||                                                 \
    3437             :     (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
    3438             :     static constexpr int MIN_RING_POINTS = 3;
    3439             : #else
    3440             :     static constexpr int MIN_RING_POINTS = 4;
    3441             : #endif
    3442             : 
    3443          22 :     const auto eType = wkbFlatten(poGeom->getGeometryType());
    3444             : 
    3445          22 :     if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    3446             :     {
    3447           4 :         std::unique_ptr<OGRGeometryCollection> poRet;
    3448           4 :         if (eType == wkbGeometryCollection)
    3449             :         {
    3450           2 :             poRet = std::make_unique<OGRGeometryCollection>();
    3451             :         }
    3452           2 :         else if (eType == wkbMultiPolygon)
    3453             :         {
    3454           2 :             poRet = std::make_unique<OGRMultiPolygon>();
    3455             :         }
    3456           0 :         else if (eType == wkbMultiLineString)
    3457             :         {
    3458           0 :             poRet = std::make_unique<OGRMultiLineString>();
    3459             :         }
    3460           0 :         else if (eType == wkbMultiPoint)
    3461             :         {
    3462           0 :             poRet = std::make_unique<OGRMultiPoint>();
    3463             :         }
    3464             :         else
    3465             :         {
    3466           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3467             :                      "Unexpected geometry type: %s",
    3468             :                      OGRGeometryTypeToName(eType));
    3469           0 :             return nullptr;
    3470             :         }
    3471             : 
    3472           4 :         const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
    3473          12 :         for (const auto *poSubGeomIn : *poColl)
    3474             :         {
    3475           8 :             std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
    3476           8 :             poRet->addGeometry(std::move(poSubGeom));
    3477             :         }
    3478             : 
    3479           4 :         return poRet;
    3480             :     }
    3481             : 
    3482          18 :     if (eType == wkbPoint)
    3483             :     {
    3484           0 :         return std::unique_ptr<OGRGeometry>(poGeom->clone());
    3485             :     }
    3486          18 :     if (eType == wkbLineString)
    3487             :     {
    3488             :         std::unique_ptr<OGRLineString> poLineString(
    3489           4 :             poGeom->toLineString()->clone());
    3490           2 :         if (poLineString->getNumPoints() == 1)
    3491             :         {
    3492           4 :             OGRPoint oPoint;
    3493           2 :             poLineString->getPoint(0, &oPoint);
    3494           2 :             poLineString->addPoint(&oPoint);
    3495             :         }
    3496           2 :         return poLineString;
    3497             :     }
    3498          16 :     if (eType == wkbPolygon)
    3499             :     {
    3500          32 :         std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
    3501          16 :         poPolygon->closeRings();
    3502             : 
    3503             :         // make sure rings have enough points
    3504          34 :         for (auto *poRing : *poPolygon)
    3505             :         {
    3506          22 :             while (poRing->getNumPoints() < MIN_RING_POINTS)
    3507             :             {
    3508           8 :                 OGRPoint oPoint;
    3509           4 :                 poRing->getPoint(0, &oPoint);
    3510           4 :                 poRing->addPoint(&oPoint);
    3511             :             }
    3512             :         }
    3513             : 
    3514          16 :         return poPolygon;
    3515             :     }
    3516             : 
    3517           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
    3518             :              OGRGeometryTypeToName(eType));
    3519           0 :     return nullptr;
    3520             : }
    3521             : 
    3522             : /************************************************************************/
    3523             : /*                         convertToGEOSGeom()                          */
    3524             : /************************************************************************/
    3525             : 
    3526      243553 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
    3527             :                                   const OGRGeometry *poGeom)
    3528             : {
    3529      243553 :     GEOSGeom hGeom = nullptr;
    3530      243553 :     const size_t nDataSize = poGeom->WkbSize();
    3531             :     unsigned char *pabyData =
    3532      243553 :         static_cast<unsigned char *>(CPLMalloc(nDataSize));
    3533             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    3534             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
    3535      243553 :     OGRwkbVariant eWkbVariant = wkbVariantIso;
    3536             : #else
    3537             :     OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
    3538             : #endif
    3539      243553 :     if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
    3540             :     {
    3541      242537 :         hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
    3542             :     }
    3543      243553 :     CPLFree(pabyData);
    3544             : 
    3545      243553 :     return hGeom;
    3546             : }
    3547             : #endif
    3548             : 
    3549             : /************************************************************************/
    3550             : /*                            exportToGEOS()                            */
    3551             : /************************************************************************/
    3552             : 
    3553             : /** Returns a GEOSGeom object corresponding to the geometry.
    3554             :  *
    3555             :  * @param hGEOSCtxt GEOS context
    3556             :  * @param bRemoveEmptyParts Whether empty parts of the geometry should be
    3557             :  * removed before exporting to GEOS (GDAL >= 3.10)
    3558             :  * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
    3559             :  * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
    3560             :  * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
    3561             :  * @return a GEOSGeom object corresponding to the geometry (to be freed with
    3562             :  * GEOSGeom_destroy_r()), or NULL in case of error
    3563             :  */
    3564      243539 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
    3565             :                                    bool bRemoveEmptyParts,
    3566             :                                    bool bAddPointsIfNeeded) const
    3567             : {
    3568             :     (void)hGEOSCtxt;
    3569             :     (void)bRemoveEmptyParts;
    3570             :     (void)bAddPointsIfNeeded;
    3571             : 
    3572             : #ifndef HAVE_GEOS
    3573             : 
    3574             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3575             :     return nullptr;
    3576             : 
    3577             : #else
    3578             : 
    3579      243539 :     if (hGEOSCtxt == nullptr)
    3580           0 :         return nullptr;
    3581             : 
    3582      243539 :     const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
    3583             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3584             :     // POINT EMPTY is exported to WKB as if it were POINT(0 0),
    3585             :     // so that particular case is necessary.
    3586             :     if (eType == wkbPoint && IsEmpty())
    3587             :     {
    3588             :         return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
    3589             :     }
    3590             : #endif
    3591             : 
    3592      243539 :     GEOSGeom hGeom = nullptr;
    3593             : 
    3594      243539 :     std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
    3595      243539 :     const OGRGeometry *poGeosInput = this;
    3596             : 
    3597      243539 :     const bool bHasZ = poGeosInput->Is3D();
    3598      243539 :     bool bHasM = poGeosInput->IsMeasured();
    3599             : 
    3600      243539 :     if (poGeosInput->hasCurveGeometry())
    3601             :     {
    3602         868 :         poModifiedInput.reset(poGeosInput->getLinearGeometry());
    3603         868 :         poGeosInput = poModifiedInput.get();
    3604             :     }
    3605             : 
    3606      243539 :     if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
    3607             :     {
    3608           1 :         if (!poModifiedInput)
    3609             :         {
    3610           1 :             poModifiedInput.reset(poGeosInput->clone());
    3611           1 :             poGeosInput = poModifiedInput.get();
    3612             :         }
    3613           1 :         poModifiedInput->removeEmptyParts();
    3614             :     }
    3615             : 
    3616             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3617             :     // GEOS < 3.12 doesn't support M dimension
    3618             :     if (bHasM)
    3619             :     {
    3620             :         if (!poModifiedInput)
    3621             :         {
    3622             :             poModifiedInput.reset(poGeosInput->clone());
    3623             :             poGeosInput = poModifiedInput.get();
    3624             :         }
    3625             :         poModifiedInput->setMeasured(false);
    3626             :         bHasM = false;
    3627             :     }
    3628             : #endif
    3629             : 
    3630      243539 :     if (eType == wkbTriangle)
    3631             :     {
    3632             :         poModifiedInput =
    3633          98 :             std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
    3634          98 :         poGeosInput = poModifiedInput.get();
    3635             :     }
    3636      243441 :     else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
    3637             :     {
    3638         844 :         if (!poModifiedInput)
    3639             :         {
    3640         844 :             poModifiedInput.reset(poGeosInput->clone());
    3641             :         }
    3642             : 
    3643        2532 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3644         844 :             std::move(poModifiedInput),
    3645         844 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
    3646         844 :         poGeosInput = poModifiedInput.get();
    3647             :     }
    3648      242752 :     else if (eType == wkbGeometryCollection &&
    3649         155 :              CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
    3650             :     {
    3651          73 :         if (!poModifiedInput)
    3652             :         {
    3653          71 :             poModifiedInput.reset(poGeosInput->clone());
    3654             :         }
    3655             : 
    3656             :         // Force into a MultiPolygon, then back to a GeometryCollection.
    3657             :         // This gets rid of fancy types like TIN and PolyhedralSurface that
    3658             :         // GEOS doesn't understand and flattens nested collections.
    3659         219 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3660          73 :             std::move(poModifiedInput),
    3661          73 :             OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
    3662         219 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3663          73 :             std::move(poModifiedInput),
    3664          73 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
    3665             : 
    3666          73 :         poGeosInput = poModifiedInput.get();
    3667             :     }
    3668             : 
    3669             :     {
    3670             :         // Rather than check for conditions that would prevent conversion to
    3671             :         // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
    3672             :         // conversion as-is. If the conversion fails, we don't want any
    3673             :         // warnings emitted; we'll repair the input and try again.
    3674           0 :         std::optional<GEOSWarningSilencer> oSilencer;
    3675      243539 :         if (bAddPointsIfNeeded)
    3676             :         {
    3677         152 :             oSilencer.emplace(hGEOSCtxt);
    3678             :         }
    3679             : 
    3680      243539 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3681             :     }
    3682             : 
    3683      243539 :     if (hGeom == nullptr && bAddPointsIfNeeded)
    3684             :     {
    3685          14 :         poModifiedInput = repairForGEOS(poGeosInput);
    3686          14 :         poGeosInput = poModifiedInput.get();
    3687             : 
    3688          14 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3689             :     }
    3690             : 
    3691      243539 :     return hGeom;
    3692             : 
    3693             : #endif  // HAVE_GEOS
    3694             : }
    3695             : 
    3696             : /************************************************************************/
    3697             : /*                          hasCurveGeometry()                          */
    3698             : /************************************************************************/
    3699             : 
    3700             : /**
    3701             :  * \brief Returns if this geometry is or has curve geometry.
    3702             :  *
    3703             :  * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
    3704             :  * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
    3705             :  *
    3706             :  * If bLookForNonLinear is set to TRUE, it will be actually looked if
    3707             :  * the geometry or its subgeometries are or contain a non-linear
    3708             :  * geometry in them. In which case, if the method returns TRUE, it
    3709             :  * means that getLinearGeometry() would return an approximate version
    3710             :  * of the geometry. Otherwise, getLinearGeometry() would do a
    3711             :  * conversion, but with just converting container type, like
    3712             :  * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
    3713             :  * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
    3714             :  * conversion.
    3715             :  *
    3716             :  * This method is the same as the C function OGR_G_HasCurveGeometry().
    3717             :  *
    3718             :  * @param bLookForNonLinear set it to TRUE to check if the geometry is
    3719             :  * or contains a CIRCULARSTRING.
    3720             :  *
    3721             :  * @return TRUE if this geometry is or has curve geometry.
    3722             :  *
    3723             :  */
    3724             : 
    3725      279419 : bool OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
    3726             : {
    3727      279419 :     return FALSE;
    3728             : }
    3729             : 
    3730             : /************************************************************************/
    3731             : /*                         getLinearGeometry()                          */
    3732             : /************************************************************************/
    3733             : 
    3734             : /**
    3735             :  * \brief Return, possibly approximate, non-curve version of this geometry.
    3736             :  *
    3737             :  * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
    3738             :  * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
    3739             :  *
    3740             :  * The ownership of the returned geometry belongs to the caller.
    3741             :  *
    3742             :  * The reverse method is OGRGeometry::getCurveGeometry().
    3743             :  *
    3744             :  * This method is the same as the C function OGR_G_GetLinearGeometry().
    3745             :  *
    3746             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    3747             :  * arc, zero to use the default setting.
    3748             :  * @param papszOptions options as a null-terminated list of strings.
    3749             :  *                     See OGRGeometryFactory::curveToLineString() for
    3750             :  *                     valid options.
    3751             :  *
    3752             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3753             :  *
    3754             :  */
    3755             : 
    3756             : OGRGeometry *
    3757          90 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
    3758             :                                CPL_UNUSED const char *const *papszOptions) const
    3759             : {
    3760          90 :     return clone();
    3761             : }
    3762             : 
    3763             : /************************************************************************/
    3764             : /*                          getCurveGeometry()                          */
    3765             : /************************************************************************/
    3766             : 
    3767             : /**
    3768             :  * \brief Return curve version of this geometry.
    3769             :  *
    3770             :  * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
    3771             :  * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
    3772             :  * curve geometries.
    3773             :  *
    3774             :  * If the geometry has no curve portion, the returned geometry will be a clone
    3775             :  * of it.
    3776             :  *
    3777             :  * The ownership of the returned geometry belongs to the caller.
    3778             :  *
    3779             :  * The reverse method is OGRGeometry::getLinearGeometry().
    3780             :  *
    3781             :  * This function is the same as C function OGR_G_GetCurveGeometry().
    3782             :  *
    3783             :  * @param papszOptions options as a null-terminated list of strings.
    3784             :  *                     Unused for now. Must be set to NULL.
    3785             :  *
    3786             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3787             :  *
    3788             :  */
    3789             : 
    3790             : OGRGeometry *
    3791           5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
    3792             : {
    3793           5 :     return clone();
    3794             : }
    3795             : 
    3796             : /************************************************************************/
    3797             : /*                              Distance()                              */
    3798             : /************************************************************************/
    3799             : 
    3800             : /**
    3801             :  * \brief Compute distance between two geometries.
    3802             :  *
    3803             :  * Returns the shortest distance between the two geometries. The distance is
    3804             :  * expressed into the same unit as the coordinates of the geometries.
    3805             :  *
    3806             :  * This method is the same as the C function OGR_G_Distance().
    3807             :  *
    3808             :  * This method is built on the GEOS library, check it for the definition
    3809             :  * of the geometry operation.
    3810             :  * If OGR is built without the GEOS library, this method will always fail,
    3811             :  * issuing a CPLE_NotSupported error.
    3812             :  *
    3813             :  * @param poOtherGeom the other geometry to compare against.
    3814             :  *
    3815             :  * @return the distance between the geometries or -1 if an error occurs.
    3816             :  */
    3817             : 
    3818          25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
    3819             : 
    3820             : {
    3821          25 :     if (nullptr == poOtherGeom)
    3822             :     {
    3823           0 :         CPLDebug("OGR",
    3824             :                  "OGRGeometry::Distance called with NULL geometry pointer");
    3825           0 :         return -1.0;
    3826             :     }
    3827             : 
    3828          25 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    3829             :     {
    3830             : #ifndef HAVE_SFCGAL
    3831             : 
    3832           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3833           0 :         return -1.0;
    3834             : 
    3835             : #else
    3836             : 
    3837             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3838             :         if (poThis == nullptr)
    3839             :             return -1.0;
    3840             : 
    3841             :         sfcgal_geometry_t *poOther =
    3842             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3843             :         if (poOther == nullptr)
    3844             :         {
    3845             :             sfcgal_geometry_delete(poThis);
    3846             :             return -1.0;
    3847             :         }
    3848             : 
    3849             :         const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
    3850             : 
    3851             :         sfcgal_geometry_delete(poThis);
    3852             :         sfcgal_geometry_delete(poOther);
    3853             : 
    3854             :         return dfDistance > 0.0 ? dfDistance : -1.0;
    3855             : 
    3856             : #endif
    3857             :     }
    3858             : 
    3859             :     else
    3860             :     {
    3861             : #ifndef HAVE_GEOS
    3862             : 
    3863             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3864             :         return -1.0;
    3865             : 
    3866             : #else
    3867             : 
    3868          25 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    3869             :         // GEOSGeom is a pointer
    3870          25 :         GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3871          25 :         GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
    3872             : 
    3873          25 :         int bIsErr = 0;
    3874          25 :         double dfDistance = 0.0;
    3875             : 
    3876          25 :         if (hThis != nullptr && hOther != nullptr)
    3877             :         {
    3878          25 :             bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
    3879             :         }
    3880             : 
    3881          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hThis);
    3882          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hOther);
    3883          25 :         freeGEOSContext(hGEOSCtxt);
    3884             : 
    3885          25 :         if (bIsErr > 0)
    3886             :         {
    3887          25 :             return dfDistance;
    3888             :         }
    3889             : 
    3890             :         /* Calculations error */
    3891           0 :         return -1.0;
    3892             : 
    3893             : #endif /* HAVE_GEOS */
    3894             :     }
    3895             : }
    3896             : 
    3897             : /************************************************************************/
    3898             : /*                           OGR_G_Distance()                           */
    3899             : /************************************************************************/
    3900             : /**
    3901             :  * \brief Compute distance between two geometries.
    3902             :  *
    3903             :  * Returns the shortest distance between the two geometries. The distance is
    3904             :  * expressed into the same unit as the coordinates of the geometries.
    3905             :  *
    3906             :  * This function is the same as the C++ method OGRGeometry::Distance().
    3907             :  *
    3908             :  * This function is built on the GEOS library, check it for the definition
    3909             :  * of the geometry operation.
    3910             :  * If OGR is built without the GEOS library, this function will always fail,
    3911             :  * issuing a CPLE_NotSupported error.
    3912             :  *
    3913             :  * @param hFirst the first geometry to compare against.
    3914             :  * @param hOther the other geometry to compare against.
    3915             :  *
    3916             :  * @return the distance between the geometries or -1 if an error occurs.
    3917             :  */
    3918             : 
    3919           2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
    3920             : 
    3921             : {
    3922           2 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
    3923             : 
    3924           4 :     return OGRGeometry::FromHandle(hFirst)->Distance(
    3925           4 :         OGRGeometry::FromHandle(hOther));
    3926             : }
    3927             : 
    3928             : /************************************************************************/
    3929             : /*                             Distance3D()                             */
    3930             : /************************************************************************/
    3931             : 
    3932             : /**
    3933             :  * \brief Returns the 3D distance between two geometries
    3934             :  *
    3935             :  * The distance is expressed into the same unit as the coordinates of the
    3936             :  * geometries.
    3937             :  *
    3938             :  * This method is built on the SFCGAL library, check it for the definition
    3939             :  * of the geometry operation.
    3940             :  * If OGR is built without the SFCGAL library, this method will always return
    3941             :  * -1.0
    3942             :  *
    3943             :  * This function is the same as the C function OGR_G_Distance3D().
    3944             :  *
    3945             :  * @return distance between the two geometries
    3946             :  */
    3947             : 
    3948           1 : double OGRGeometry::Distance3D(
    3949             :     UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
    3950             : {
    3951           1 :     if (poOtherGeom == nullptr)
    3952             :     {
    3953           0 :         CPLDebug("OGR",
    3954             :                  "OGRTriangle::Distance3D called with NULL geometry pointer");
    3955           0 :         return -1.0;
    3956             :     }
    3957             : 
    3958           1 :     if (!(poOtherGeom->Is3D() && Is3D()))
    3959             :     {
    3960           0 :         CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
    3961             :                         "geometry(geometries)");
    3962           0 :         return -1.0;
    3963             :     }
    3964             : 
    3965             : #ifndef HAVE_SFCGAL
    3966             : 
    3967           1 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3968           1 :     return -1.0;
    3969             : 
    3970             : #else
    3971             : 
    3972             :     sfcgal_init();
    3973             :     sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3974             :     if (poThis == nullptr)
    3975             :         return -1.0;
    3976             : 
    3977             :     sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3978             :     if (poOther == nullptr)
    3979             :     {
    3980             :         sfcgal_geometry_delete(poThis);
    3981             :         return -1.0;
    3982             :     }
    3983             : 
    3984             :     const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
    3985             : 
    3986             :     sfcgal_geometry_delete(poThis);
    3987             :     sfcgal_geometry_delete(poOther);
    3988             : 
    3989             :     return dfDistance > 0 ? dfDistance : -1.0;
    3990             : 
    3991             : #endif
    3992             : }
    3993             : 
    3994             : /************************************************************************/
    3995             : /*                          OGR_G_Distance3D()                          */
    3996             : /************************************************************************/
    3997             : /**
    3998             :  * \brief Returns the 3D distance between two geometries
    3999             :  *
    4000             :  * The distance is expressed into the same unit as the coordinates of the
    4001             :  * geometries.
    4002             :  *
    4003             :  * This method is built on the SFCGAL library, check it for the definition
    4004             :  * of the geometry operation.
    4005             :  * If OGR is built without the SFCGAL library, this method will always return
    4006             :  * -1.0
    4007             :  *
    4008             :  * This function is the same as the C++ method OGRGeometry::Distance3D().
    4009             :  *
    4010             :  * @param hFirst the first geometry to compare against.
    4011             :  * @param hOther the other geometry to compare against.
    4012             :  * @return distance between the two geometries
    4013             :  *
    4014             :  * @return the distance between the geometries or -1 if an error occurs.
    4015             :  */
    4016             : 
    4017           1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
    4018             : 
    4019             : {
    4020           1 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
    4021             : 
    4022           2 :     return OGRGeometry::FromHandle(hFirst)->Distance3D(
    4023           2 :         OGRGeometry::FromHandle(hOther));
    4024             : }
    4025             : 
    4026             : /************************************************************************/
    4027             : /*                      OGRGeometryRebuildCurves()                      */
    4028             : /************************************************************************/
    4029             : 
    4030             : #ifdef HAVE_GEOS
    4031        5099 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
    4032             :                                              const OGRGeometry *poOtherGeom,
    4033             :                                              OGRGeometry *poOGRProduct)
    4034             : {
    4035       10198 :     if (poOGRProduct != nullptr &&
    4036       10112 :         wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
    4037        5013 :         (poGeom->hasCurveGeometry(true) ||
    4038        3858 :          (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
    4039             :     {
    4040           8 :         OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
    4041           8 :         delete poOGRProduct;
    4042           8 :         return poCurveGeom;
    4043             :     }
    4044        5091 :     return poOGRProduct;
    4045             : }
    4046             : 
    4047             : /************************************************************************/
    4048             : /*                       BuildGeometryFromGEOS()                        */
    4049             : /************************************************************************/
    4050             : 
    4051        4949 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
    4052             :                                           GEOSGeom hGeosProduct,
    4053             :                                           const OGRGeometry *poSelf,
    4054             :                                           const OGRGeometry *poOtherGeom)
    4055             : {
    4056        4949 :     OGRGeometry *poOGRProduct = nullptr;
    4057        4949 :     if (hGeosProduct != nullptr)
    4058             :     {
    4059             :         poOGRProduct =
    4060        4947 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
    4061        4947 :         if (poOGRProduct != nullptr &&
    4062       12708 :             poSelf->getSpatialReference() != nullptr &&
    4063        2814 :             (poOtherGeom == nullptr ||
    4064        2814 :              (poOtherGeom->getSpatialReference() != nullptr &&
    4065        2677 :               poOtherGeom->getSpatialReference()->IsSame(
    4066             :                   poSelf->getSpatialReference()))))
    4067             :         {
    4068        2733 :             poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
    4069             :         }
    4070             :         poOGRProduct =
    4071        4947 :             OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
    4072        4947 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
    4073             :     }
    4074        4949 :     return poOGRProduct;
    4075             : }
    4076             : 
    4077             : /************************************************************************/
    4078             : /*                     BuildGeometryFromTwoGeoms()                      */
    4079             : /************************************************************************/
    4080             : 
    4081        3932 : static OGRGeometry *BuildGeometryFromTwoGeoms(
    4082             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    4083             :     GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
    4084             :                                        const GEOSGeometry *,
    4085             :                                        const GEOSGeometry *))
    4086             : {
    4087        3932 :     OGRGeometry *poOGRProduct = nullptr;
    4088             : 
    4089        3932 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    4090        3932 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    4091        3932 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    4092        3932 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    4093             :     {
    4094             :         GEOSGeom hGeosProduct =
    4095        3932 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
    4096             : 
    4097             :         poOGRProduct =
    4098        3932 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
    4099             :     }
    4100        3932 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    4101        3932 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    4102        3932 :     poSelf->freeGEOSContext(hGEOSCtxt);
    4103             : 
    4104        3932 :     return poOGRProduct;
    4105             : }
    4106             : 
    4107             : /************************************************************************/
    4108             : /*                      OGRGEOSBooleanPredicate()                       */
    4109             : /************************************************************************/
    4110             : 
    4111       22773 : static bool OGRGEOSBooleanPredicate(
    4112             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    4113             :     char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
    4114             :                               const GEOSGeometry *))
    4115             : {
    4116       22773 :     bool bResult = false;
    4117             : 
    4118       22773 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    4119       22773 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    4120       22773 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    4121       22773 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    4122             :     {
    4123       22218 :         bResult =
    4124       22218 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
    4125             :     }
    4126       22773 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    4127       22773 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    4128       22773 :     poSelf->freeGEOSContext(hGEOSCtxt);
    4129             : 
    4130       22773 :     return bResult;
    4131             : }
    4132             : 
    4133             : #endif  // HAVE_GEOS
    4134             : 
    4135             : /************************************************************************/
    4136             : /*                             MakeValid()                              */
    4137             : /************************************************************************/
    4138             : 
    4139             : /**
    4140             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4141             :  *
    4142             :  * Already-valid geometries are cloned without further intervention
    4143             :  * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
    4144             :  * may be subject to non-significant transformations, such as duplicated point
    4145             :  * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
    4146             :  * geometry collections could be returned a single geometry. GDAL 3.10
    4147             :  * returns the same type of geometry).
    4148             :  *
    4149             :  * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
    4150             :  * post-processing step is often desired.
    4151             :  *
    4152             :  * This method is the same as the C function OGR_G_MakeValid().
    4153             :  *
    4154             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4155             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4156             :  * library, this function will return a clone of the input geometry if it is
    4157             :  * valid, or NULL if it is invalid.
    4158             :  *
    4159             :  * Certain geometries cannot be read using GEOS, for example if Polygon rings
    4160             :  * are not closed or do not contain enough vertices. If a geometry cannot be
    4161             :  * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
    4162             :  * attempt to modify these geometries such that they can be read and
    4163             :  * repaired by GEOS.
    4164             :  *
    4165             :  * @param papszOptions NULL terminated list of options, or NULL. The following
    4166             :  * options are available:
    4167             :  * <ul>
    4168             :  * <li>METHOD=LINEWORK/STRUCTURE.
    4169             :  *     LINEWORK is the default method, which combines all rings into a set of
    4170             :  *     noded lines and then extracts valid polygons from that linework.
    4171             :  *     The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
    4172             :  *     all rings valid, then merges shells and
    4173             :  *     subtracts holes from shells to generate valid result. Assumes that
    4174             :  *     holes and shells are correctly categorized.</li>
    4175             :  * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
    4176             :  *     NO (default): collapses are converted to empty geometries
    4177             :  *     YES: collapses are converted to a valid geometry of lower dimension.</li>
    4178             :  * </ul>
    4179             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4180             :  *
    4181             :  * @since GDAL 3.0
    4182             :  */
    4183         153 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
    4184             : {
    4185             :     (void)papszOptions;
    4186             : #ifndef HAVE_GEOS
    4187             :     if (IsValid())
    4188             :         return clone();
    4189             : 
    4190             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4191             :     return nullptr;
    4192             : #else
    4193         153 :     if (IsSFCGALCompatible())
    4194             :     {
    4195           0 :         if (IsValid())
    4196           0 :             return clone();
    4197             :     }
    4198         153 :     else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
    4199             :     {
    4200           3 :         GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
    4201           3 :         bool bIsValid = false;
    4202           3 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4203           3 :         if (hGeosGeom)
    4204             :         {
    4205           3 :             bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
    4206           3 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4207             :         }
    4208           3 :         freeGEOSContext(hGEOSCtxt);
    4209           3 :         if (bIsValid)
    4210           1 :             return clone();
    4211             :     }
    4212             : 
    4213         152 :     const bool bStructureMethod = EQUAL(
    4214             :         CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
    4215         152 :     CPL_IGNORE_RET_VAL(bStructureMethod);
    4216             : #if !(GEOS_VERSION_MAJOR > 3 ||                                                \
    4217             :       (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    4218             :     if (bStructureMethod)
    4219             :     {
    4220             :         CPLError(CE_Failure, CPLE_NotSupported,
    4221             :                  "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
    4222             :         return nullptr;
    4223             :     }
    4224             : #endif
    4225             : 
    4226         152 :     OGRGeometry *poOGRProduct = nullptr;
    4227             : 
    4228         152 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4229         152 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
    4230         152 :     if (hGeosGeom != nullptr)
    4231             :     {
    4232             :         GEOSGeom hGEOSRet;
    4233             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4234             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4235         152 :         if (bStructureMethod)
    4236             :         {
    4237             :             GEOSMakeValidParams *params =
    4238          15 :                 GEOSMakeValidParams_create_r(hGEOSCtxt);
    4239          15 :             CPLAssert(params);
    4240          15 :             GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
    4241             :                                             GEOS_MAKE_VALID_STRUCTURE);
    4242          15 :             GEOSMakeValidParams_setKeepCollapsed_r(
    4243             :                 hGEOSCtxt, params,
    4244          15 :                 CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
    4245          15 :             hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
    4246          15 :             GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
    4247             :         }
    4248             :         else
    4249             : #endif
    4250             :         {
    4251         137 :             hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
    4252             :         }
    4253         152 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4254             : 
    4255         152 :         if (hGEOSRet != nullptr)
    4256             :         {
    4257             :             poOGRProduct =
    4258         152 :                 OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
    4259         152 :             if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
    4260           6 :                 poOGRProduct->assignSpatialReference(getSpatialReference());
    4261             :             poOGRProduct =
    4262         152 :                 OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
    4263         152 :             GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
    4264             : 
    4265             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4266             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4267             :             // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
    4268             :             // if the input is a multiple geometry
    4269         152 :             if (poOGRProduct && bStructureMethod &&
    4270         310 :                 OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
    4271           6 :                 !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
    4272             :                                      wkbGeometryCollection))
    4273             :             {
    4274           6 :                 poOGRProduct = OGRGeometryFactory::forceTo(
    4275           6 :                                    std::unique_ptr<OGRGeometry>(poOGRProduct),
    4276           3 :                                    getGeometryType())
    4277           3 :                                    .release();
    4278             :             }
    4279             : #endif
    4280             :         }
    4281             :     }
    4282         152 :     freeGEOSContext(hGEOSCtxt);
    4283             : 
    4284         152 :     return poOGRProduct;
    4285             : #endif
    4286             : }
    4287             : 
    4288             : /************************************************************************/
    4289             : /*                          OGR_G_MakeValid()                           */
    4290             : /************************************************************************/
    4291             : 
    4292             : /**
    4293             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4294             :  *
    4295             :  * Already-valid geometries are cloned without further intervention.
    4296             :  *
    4297             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4298             :  *
    4299             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4300             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4301             :  * library, this function will return a clone of the input geometry if it is
    4302             :  * valid, or NULL if it is invalid
    4303             :  *
    4304             :  * @param hGeom The Geometry to make valid.
    4305             :  *
    4306             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4307             :  * or NULL if an error occurs.
    4308             :  *
    4309             :  * @since GDAL 3.0
    4310             :  */
    4311             : 
    4312           0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
    4313             : 
    4314             : {
    4315           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
    4316             : 
    4317           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
    4318             : }
    4319             : 
    4320             : /************************************************************************/
    4321             : /*                         OGR_G_MakeValidEx()                          */
    4322             : /************************************************************************/
    4323             : 
    4324             : /**
    4325             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4326             :  *
    4327             :  * Already-valid geometries are cloned without further intervention.
    4328             :  *
    4329             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4330             :  *
    4331             :  * See documentation of that method for possible options.
    4332             :  *
    4333             :  * @param hGeom The Geometry to make valid.
    4334             :  * @param papszOptions Options.
    4335             :  *
    4336             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4337             :  * or NULL if an error occurs.
    4338             :  *
    4339             :  * @since GDAL 3.4
    4340             :  */
    4341             : 
    4342          25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
    4343             : 
    4344             : {
    4345          25 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
    4346             : 
    4347          25 :     return OGRGeometry::ToHandle(
    4348          50 :         OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
    4349             : }
    4350             : 
    4351             : /************************************************************************/
    4352             : /*                             Normalize()                              */
    4353             : /************************************************************************/
    4354             : 
    4355             : /**
    4356             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4357             :  *
    4358             :  * This method is the same as the C function OGR_G_Normalize().
    4359             :  *
    4360             :  * This function is built on the GEOS library; check it for the definition
    4361             :  * of the geometry operation.
    4362             :  * If OGR is built without the GEOS library, this function will always fail,
    4363             :  * issuing a CPLE_NotSupported error.
    4364             :  *
    4365             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4366             :  *
    4367             :  * @since GDAL 3.3
    4368             :  */
    4369          51 : OGRGeometry *OGRGeometry::Normalize() const
    4370             : {
    4371             : #ifndef HAVE_GEOS
    4372             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4373             :     return nullptr;
    4374             : #else
    4375          51 :     OGRGeometry *poOGRProduct = nullptr;
    4376             : 
    4377          51 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4378          51 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4379          51 :     if (hGeosGeom != nullptr)
    4380             :     {
    4381             : 
    4382          51 :         int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
    4383             : 
    4384          51 :         if (hGEOSRet == 0)
    4385             :         {
    4386             :             poOGRProduct =
    4387          51 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
    4388             :         }
    4389             :         else
    4390             :         {
    4391           0 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4392             :         }
    4393             :     }
    4394          51 :     freeGEOSContext(hGEOSCtxt);
    4395             : 
    4396          51 :     return poOGRProduct;
    4397             : #endif
    4398             : }
    4399             : 
    4400             : /************************************************************************/
    4401             : /*                          OGR_G_Normalize()                           */
    4402             : /************************************************************************/
    4403             : 
    4404             : /**
    4405             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4406             :  *
    4407             :  * This function is the same as the C++ method OGRGeometry::Normalize().
    4408             :  *
    4409             :  * This function is built on the GEOS library; check it for the definition
    4410             :  * of the geometry operation.
    4411             :  * If OGR is built without the GEOS library, this function will always fail,
    4412             :  * issuing a CPLE_NotSupported error.
    4413             :  * @param hGeom The Geometry to normalize.
    4414             :  *
    4415             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4416             :  * or NULL if an error occurs.
    4417             :  *
    4418             :  * @since GDAL 3.3
    4419             :  */
    4420             : 
    4421          21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
    4422             : 
    4423             : {
    4424          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
    4425             : 
    4426          21 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
    4427             : }
    4428             : 
    4429             : /************************************************************************/
    4430             : /*                             ConvexHull()                             */
    4431             : /************************************************************************/
    4432             : 
    4433             : /**
    4434             :  * \brief Compute convex hull.
    4435             :  *
    4436             :  * A new geometry object is created and returned containing the convex
    4437             :  * hull of the geometry on which the method is invoked.
    4438             :  *
    4439             :  * This method is the same as the C function OGR_G_ConvexHull().
    4440             :  *
    4441             :  * This method is built on the GEOS library, check it for the definition
    4442             :  * of the geometry operation.
    4443             :  * If OGR is built without the GEOS library, this method will always fail,
    4444             :  * issuing a CPLE_NotSupported error.
    4445             :  *
    4446             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4447             :  */
    4448             : 
    4449           6 : OGRGeometry *OGRGeometry::ConvexHull() const
    4450             : 
    4451             : {
    4452           6 :     if (IsSFCGALCompatible())
    4453             :     {
    4454             : #ifndef HAVE_SFCGAL
    4455             : 
    4456           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    4457           0 :         return nullptr;
    4458             : 
    4459             : #else
    4460             : 
    4461             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    4462             :         if (poThis == nullptr)
    4463             :             return nullptr;
    4464             : 
    4465             :         sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
    4466             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    4467             :         if (h_prodGeom)
    4468             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    4469             : 
    4470             :         sfcgal_geometry_delete(poThis);
    4471             :         sfcgal_geometry_delete(poRes);
    4472             : 
    4473             :         return h_prodGeom;
    4474             : 
    4475             : #endif
    4476             :     }
    4477             : 
    4478             :     else
    4479             :     {
    4480             : #ifndef HAVE_GEOS
    4481             : 
    4482             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4483             :         return nullptr;
    4484             : 
    4485             : #else
    4486             : 
    4487           6 :         OGRGeometry *poOGRProduct = nullptr;
    4488             : 
    4489           6 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4490           6 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4491           6 :         if (hGeosGeom != nullptr)
    4492             :         {
    4493           6 :             GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
    4494           6 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4495             : 
    4496             :             poOGRProduct =
    4497           6 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4498             :         }
    4499           6 :         freeGEOSContext(hGEOSCtxt);
    4500             : 
    4501           6 :         return poOGRProduct;
    4502             : 
    4503             : #endif /* HAVE_GEOS */
    4504             :     }
    4505             : }
    4506             : 
    4507             : /************************************************************************/
    4508             : /*                          OGR_G_ConvexHull()                          */
    4509             : /************************************************************************/
    4510             : /**
    4511             :  * \brief Compute convex hull.
    4512             :  *
    4513             :  * A new geometry object is created and returned containing the convex
    4514             :  * hull of the geometry on which the method is invoked.
    4515             :  *
    4516             :  * This function is the same as the C++ method OGRGeometry::ConvexHull().
    4517             :  *
    4518             :  * This function is built on the GEOS library, check it for the definition
    4519             :  * of the geometry operation.
    4520             :  * If OGR is built without the GEOS library, this function will always fail,
    4521             :  * issuing a CPLE_NotSupported error.
    4522             :  *
    4523             :  * @param hTarget The Geometry to calculate the convex hull of.
    4524             :  *
    4525             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4526             :  * or NULL if an error occurs.
    4527             :  */
    4528             : 
    4529           1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
    4530             : 
    4531             : {
    4532           1 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
    4533             : 
    4534           1 :     return OGRGeometry::ToHandle(
    4535           1 :         OGRGeometry::FromHandle(hTarget)->ConvexHull());
    4536             : }
    4537             : 
    4538             : /************************************************************************/
    4539             : /*                            ConcaveHull()                             */
    4540             : /************************************************************************/
    4541             : 
    4542             : /**
    4543             :  * \brief Compute the concave hull of a geometry.
    4544             :  *
    4545             :  * The concave hull is fully contained within the convex hull and also
    4546             :  * contains all the points of the input, but in a smaller area.
    4547             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4548             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4549             :  * that contains all the points in the input Geometry.
    4550             :  *
    4551             :  * A new geometry object is created and returned containing the concave
    4552             :  * hull of the geometry on which the method is invoked.
    4553             :  *
    4554             :  * This method is the same as the C function OGR_G_ConcaveHull().
    4555             :  *
    4556             :  * This method is built on the GEOS >= 3.11 library
    4557             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4558             :  * fail, issuing a CPLE_NotSupported error.
    4559             :  *
    4560             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4561             :  * @param bAllowHoles Whether holes are allowed.
    4562             :  *
    4563             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4564             :  *
    4565             :  * @since GDAL 3.6
    4566             :  * @see OGRGeometry::ConcaveHullOfPolygons()
    4567             :  */
    4568             : 
    4569           8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
    4570             : {
    4571             : #ifndef HAVE_GEOS
    4572             :     (void)dfRatio;
    4573             :     (void)bAllowHoles;
    4574             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4575             :     return nullptr;
    4576             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4577             :     (void)dfRatio;
    4578             :     (void)bAllowHoles;
    4579             :     CPLError(CE_Failure, CPLE_NotSupported,
    4580             :              "GEOS 3.11 or later needed for ConcaveHull.");
    4581             :     return nullptr;
    4582             : #else
    4583           8 :     OGRGeometry *poOGRProduct = nullptr;
    4584             : 
    4585           8 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4586           8 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4587           8 :     if (hGeosGeom != nullptr)
    4588             :     {
    4589             :         GEOSGeom hGeosHull =
    4590           8 :             GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
    4591           8 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4592             : 
    4593             :         poOGRProduct =
    4594           8 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4595             :     }
    4596           8 :     freeGEOSContext(hGEOSCtxt);
    4597             : 
    4598           8 :     return poOGRProduct;
    4599             : #endif /* HAVE_GEOS */
    4600             : }
    4601             : 
    4602             : /************************************************************************/
    4603             : /*                         OGR_G_ConcaveHull()                          */
    4604             : /************************************************************************/
    4605             : /**
    4606             :  * \brief Compute the concave hull of a geometry.
    4607             :  *
    4608             :  * The concave hull is fully contained within the convex hull and also
    4609             :  * contains all the points of the input, but in a smaller area.
    4610             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4611             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4612             :  * that contains all the points in the input Geometry.
    4613             :  *
    4614             :  * A new geometry object is created and returned containing the convex
    4615             :  * hull of the geometry on which the function is invoked.
    4616             :  *
    4617             :  * This function is the same as the C++ method OGRGeometry::ConcaveHull().
    4618             :  *
    4619             :  * This function is built on the GEOS >= 3.11 library
    4620             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4621             :  * fail, issuing a CPLE_NotSupported error.
    4622             :  *
    4623             :  * @param hTarget The Geometry to calculate the concave hull of.
    4624             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4625             :  * @param bAllowHoles Whether holes are allowed.
    4626             :  *
    4627             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4628             :  * or NULL if an error occurs.
    4629             :  *
    4630             :  * @since GDAL 3.6
    4631             :  * @see OGR_G_ConcaveHullOfPolygons()
    4632             :  */
    4633             : 
    4634           2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
    4635             :                                bool bAllowHoles)
    4636             : 
    4637             : {
    4638           2 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
    4639             : 
    4640           2 :     return OGRGeometry::ToHandle(
    4641           2 :         OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
    4642             : }
    4643             : 
    4644             : /************************************************************************/
    4645             : /*                       ConcaveHullOfPolygons()                        */
    4646             : /************************************************************************/
    4647             : 
    4648             : /**
    4649             :  * \brief Compute the concave hull of a set of polygons, respecting
    4650             :  * the polygons as constraints.
    4651             :  *
    4652             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4653             :  * polygons.
    4654             :  * The computed hull "fills the gap" between the polygons,
    4655             :  * and does not intersect their interior.
    4656             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4657             :  * determined by a numeric target parameter.
    4658             :  *
    4659             :  * The concave hull is constructed by removing the longest outer edges
    4660             :  * of the Delaunay Triangulation of the space between the polygons,
    4661             :  * until the target criterion parameter is reached.
    4662             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4663             :  * between polygons to be no larger than this value.
    4664             :  * This can be expressed as a ratio between the lengths of the longest and
    4665             :  * shortest edges.
    4666             :  *
    4667             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4668             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4669             :  * for more details.
    4670             :  *
    4671             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4672             :  * be non-overlapping).
    4673             :  *
    4674             :  * A new geometry object is created and returned containing the concave
    4675             :  * hull of the geometry on which the method is invoked.
    4676             :  *
    4677             :  * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
    4678             :  *
    4679             :  * This method is built on the GEOS >= 3.11 library
    4680             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4681             :  * fail, issuing a CPLE_NotSupported error.
    4682             :  *
    4683             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4684             :  *                      difference between the longest and shortest edge lengths
    4685             :  *                      between the polygons.
    4686             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4687             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4688             :  *                      the original polygons.
    4689             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4690             :  *                 polygons.
    4691             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4692             :  *
    4693             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4694             :  *
    4695             :  * @since GDAL 3.13
    4696             :  * @see OGRGeometry::ConcaveHull()
    4697             :  */
    4698             : 
    4699          13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
    4700             :                                                 bool bIsTight,
    4701             :                                                 bool bAllowHoles) const
    4702             : {
    4703             : #ifndef HAVE_GEOS
    4704             :     (void)dfLengthRatio;
    4705             :     (void)bIsTight;
    4706             :     (void)bAllowHoles;
    4707             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4708             :     return nullptr;
    4709             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4710             :     (void)dfLengthRatio;
    4711             :     (void)bIsTight;
    4712             :     (void)bAllowHoles;
    4713             :     CPLError(CE_Failure, CPLE_NotSupported,
    4714             :              "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
    4715             :     return nullptr;
    4716             : #else
    4717          13 :     OGRGeometry *poOGRProduct = nullptr;
    4718             : 
    4719          13 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4720          13 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4721          13 :     if (hGeosGeom != nullptr)
    4722             :     {
    4723          13 :         GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
    4724             :             hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
    4725          13 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4726             : 
    4727             :         poOGRProduct =
    4728          13 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4729             :     }
    4730          13 :     freeGEOSContext(hGEOSCtxt);
    4731             : 
    4732          13 :     return poOGRProduct;
    4733             : #endif /* HAVE_GEOS */
    4734             : }
    4735             : 
    4736             : /************************************************************************/
    4737             : /*                    OGR_G_ConcaveHullOfPolygons()                     */
    4738             : /************************************************************************/
    4739             : /**
    4740             :  * \brief Compute the concave hull of a set of polygons, respecting
    4741             :  * the polygons as constraints.
    4742             :  *
    4743             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4744             :  * polygons.
    4745             :  * The computed hull "fills the gap" between the polygons,
    4746             :  * and does not intersect their interior.
    4747             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4748             :  * determined by a numeric target parameter.
    4749             :  *
    4750             :  * The concave hull is constructed by removing the longest outer edges
    4751             :  * of the Delaunay Triangulation of the space between the polygons,
    4752             :  * until the target criterion parameter is reached.
    4753             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4754             :  * between polygons to be no larger than this value.
    4755             :  * This can be expressed as a ratio between the lengths of the longest and
    4756             :  * shortest edges.
    4757             :  *
    4758             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4759             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4760             :  * for more details.
    4761             :  *
    4762             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4763             :  * be non-overlapping).
    4764             :  *
    4765             :  * A new geometry object is created and returned containing the concave
    4766             :  * hull of the geometry on which the method is invoked.
    4767             :  *
    4768             :  * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
    4769             :  *
    4770             :  * This function is built on the GEOS >= 3.11 library
    4771             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4772             :  * fail, issuing a CPLE_NotSupported error.
    4773             :  *
    4774             :  * @param hTarget The Geometry to calculate the concave hull of.
    4775             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4776             :  *                      difference between the longest and shortest edge lengths
    4777             :  *                      between the polygons.
    4778             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4779             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4780             :  *                      the original polygons.
    4781             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4782             :  *                 polygons.
    4783             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4784             :  *
    4785             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4786             :  * or NULL if an error occurs.
    4787             :  *
    4788             :  * @since GDAL 3.13
    4789             :  * @see OGR_G_ConcaveHull()
    4790             :  */
    4791             : 
    4792           7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
    4793             :                                          double dfLengthRatio, bool bIsTight,
    4794             :                                          bool bAllowHoles)
    4795             : 
    4796             : {
    4797           7 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
    4798             : 
    4799           7 :     return OGRGeometry::ToHandle(
    4800             :         OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
    4801           7 :             dfLengthRatio, bIsTight, bAllowHoles));
    4802             : }
    4803             : 
    4804             : /************************************************************************/
    4805             : /*                              Boundary()                              */
    4806             : /************************************************************************/
    4807             : 
    4808             : /**
    4809             :  * \brief Compute boundary.
    4810             :  *
    4811             :  * A new geometry object is created and returned containing the boundary
    4812             :  * of the geometry on which the method is invoked.
    4813             :  *
    4814             :  * This method is the same as the C function OGR_G_Boundary().
    4815             :  *
    4816             :  * This method is built on the GEOS library, check it for the definition
    4817             :  * of the geometry operation.
    4818             :  * If OGR is built without the GEOS library, this method will always fail,
    4819             :  * issuing a CPLE_NotSupported error.
    4820             :  *
    4821             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4822             :  *
    4823             :  */
    4824             : 
    4825           6 : OGRGeometry *OGRGeometry::Boundary() const
    4826             : 
    4827             : {
    4828             : #ifndef HAVE_GEOS
    4829             : 
    4830             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4831             :     return nullptr;
    4832             : 
    4833             : #else
    4834             : 
    4835           6 :     OGRGeometry *poOGRProduct = nullptr;
    4836             : 
    4837           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4838           6 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4839           6 :     if (hGeosGeom != nullptr)
    4840             :     {
    4841           6 :         GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
    4842           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4843             : 
    4844             :         poOGRProduct =
    4845           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4846             :     }
    4847           6 :     freeGEOSContext(hGEOSCtxt);
    4848             : 
    4849           6 :     return poOGRProduct;
    4850             : 
    4851             : #endif  // HAVE_GEOS
    4852             : }
    4853             : 
    4854             : //! @cond Doxygen_Suppress
    4855             : /**
    4856             :  * \brief Compute boundary (deprecated)
    4857             :  *
    4858             :  * @deprecated
    4859             :  *
    4860             :  * @see Boundary()
    4861             :  */
    4862           0 : OGRGeometry *OGRGeometry::getBoundary() const
    4863             : 
    4864             : {
    4865           0 :     return Boundary();
    4866             : }
    4867             : 
    4868             : //! @endcond
    4869             : 
    4870             : /************************************************************************/
    4871             : /*                           OGR_G_Boundary()                           */
    4872             : /************************************************************************/
    4873             : /**
    4874             :  * \brief Compute boundary.
    4875             :  *
    4876             :  * A new geometry object is created and returned containing the boundary
    4877             :  * of the geometry on which the method is invoked.
    4878             :  *
    4879             :  * This function is the same as the C++ method OGR_G_Boundary().
    4880             :  *
    4881             :  * This function is built on the GEOS library, check it for the definition
    4882             :  * of the geometry operation.
    4883             :  * If OGR is built without the GEOS library, this function will always fail,
    4884             :  * issuing a CPLE_NotSupported error.
    4885             :  *
    4886             :  * @param hTarget The Geometry to calculate the boundary of.
    4887             :  *
    4888             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4889             :  * or NULL if an error occurs.
    4890             :  *
    4891             :  */
    4892           6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
    4893             : 
    4894             : {
    4895           6 :     VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
    4896             : 
    4897           6 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4898             : }
    4899             : 
    4900             : /**
    4901             :  * \brief Compute boundary (deprecated)
    4902             :  *
    4903             :  * @deprecated
    4904             :  *
    4905             :  * @see OGR_G_Boundary()
    4906             :  */
    4907           0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
    4908             : 
    4909             : {
    4910           0 :     VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
    4911             : 
    4912           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4913             : }
    4914             : 
    4915             : /************************************************************************/
    4916             : /*                               Buffer()                               */
    4917             : /************************************************************************/
    4918             : 
    4919             : /**
    4920             :  * \brief Compute buffer of geometry.
    4921             :  *
    4922             :  * Builds a new geometry containing the buffer region around the geometry
    4923             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4924             :  * the buffer distance of the original geometry.
    4925             :  *
    4926             :  * Some buffer sections are properly described as curves, but are converted to
    4927             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4928             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4929             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4930             :  * large numbers of vertices in the resulting buffer geometry while small
    4931             :  * numbers reduce the accuracy of the result.
    4932             :  *
    4933             :  * This method is the same as the C function OGR_G_Buffer().
    4934             :  *
    4935             :  * This method is built on the GEOS library, check it for the definition
    4936             :  * of the geometry operation.
    4937             :  * If OGR is built without the GEOS library, this method will always fail,
    4938             :  * issuing a CPLE_NotSupported error.
    4939             :  *
    4940             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4941             :  *               the same unit as the coordinates of the geometry.
    4942             :  *
    4943             :  * @param nQuadSegs the number of segments used to approximate a 90
    4944             :  * degree (quadrant) of curvature.
    4945             :  *
    4946             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4947             :  */
    4948             : 
    4949          42 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
    4950             : 
    4951             : {
    4952             :     (void)dfDist;
    4953             :     (void)nQuadSegs;
    4954             : #ifndef HAVE_GEOS
    4955             : 
    4956             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4957             :     return nullptr;
    4958             : 
    4959             : #else
    4960             : 
    4961          42 :     OGRGeometry *poOGRProduct = nullptr;
    4962             : 
    4963          42 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4964          42 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4965          42 :     if (hGeosGeom != nullptr)
    4966             :     {
    4967             :         GEOSGeom hGeosProduct =
    4968          42 :             GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
    4969          42 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4970             : 
    4971             :         poOGRProduct =
    4972          42 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4973             :     }
    4974          42 :     freeGEOSContext(hGEOSCtxt);
    4975             : 
    4976          42 :     return poOGRProduct;
    4977             : 
    4978             : #endif  // HAVE_GEOS
    4979             : }
    4980             : 
    4981             : /************************************************************************/
    4982             : /*                            OGR_G_Buffer()                            */
    4983             : /************************************************************************/
    4984             : 
    4985             : /**
    4986             :  * \brief Compute buffer of geometry.
    4987             :  *
    4988             :  * Builds a new geometry containing the buffer region around the geometry
    4989             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4990             :  * the buffer distance of the original geometry.
    4991             :  *
    4992             :  * Some buffer sections are properly described as curves, but are converted to
    4993             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4994             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4995             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4996             :  * large numbers of vertices in the resulting buffer geometry while small
    4997             :  * numbers reduce the accuracy of the result.
    4998             :  *
    4999             :  * This function is the same as the C++ method OGRGeometry::Buffer().
    5000             :  *
    5001             :  * This function is built on the GEOS library, check it for the definition
    5002             :  * of the geometry operation.
    5003             :  * If OGR is built without the GEOS library, this function will always fail,
    5004             :  * issuing a CPLE_NotSupported error.
    5005             :  *
    5006             :  * @param hTarget the geometry.
    5007             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5008             :  *               the same unit as the coordinates of the geometry.
    5009             :  *
    5010             :  * @param nQuadSegs the number of segments used to approximate a 90 degree
    5011             :  * (quadrant) of curvature.
    5012             :  *
    5013             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5014             :  * or NULL if an error occurs.
    5015             :  */
    5016             : 
    5017          42 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
    5018             : 
    5019             : {
    5020          42 :     VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
    5021             : 
    5022          42 :     return OGRGeometry::ToHandle(
    5023          42 :         OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
    5024             : }
    5025             : 
    5026             : /**
    5027             :  * \brief Compute buffer of geometry.
    5028             :  *
    5029             :  * Builds a new geometry containing the buffer region around the geometry
    5030             :  * on which it is invoked.  The buffer is a polygon containing the region within
    5031             :  * the buffer distance of the original geometry.
    5032             :  *
    5033             :  * This function is built on the GEOS library, check it for the definition
    5034             :  * of the geometry operation.
    5035             :  * If OGR is built without the GEOS library, this function will always fail,
    5036             :  * issuing a CPLE_NotSupported error.
    5037             :  *
    5038             :  * The following options are supported. See the GEOS library for more detailed
    5039             :  * descriptions.
    5040             :  *
    5041             :  * <ul>
    5042             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    5043             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    5044             :  * <li>MITRE_LIMIT=double</li>
    5045             :  * <li>QUADRANT_SEGMENTS=int</li>
    5046             :  * <li>SINGLE_SIDED=YES/NO</li>
    5047             :  * </ul>
    5048             :  *
    5049             :  * This function is the same as the C function OGR_G_BufferEx().
    5050             :  *
    5051             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5052             :  *               the same unit as the coordinates of the geometry.
    5053             :  * @param papszOptions NULL terminated list of options (may be NULL)
    5054             :  *
    5055             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5056             :  *
    5057             :  * @since GDAL 3.10
    5058             :  */
    5059             : 
    5060          35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
    5061             :                                    CSLConstList papszOptions) const
    5062             : {
    5063             :     (void)dfDist;
    5064             :     (void)papszOptions;
    5065             : #ifndef HAVE_GEOS
    5066             : 
    5067             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5068             :     return nullptr;
    5069             : 
    5070             : #else
    5071          35 :     OGRGeometry *poOGRProduct = nullptr;
    5072          35 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5073             : 
    5074          35 :     auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
    5075          35 :     bool bParamsAreValid = true;
    5076             : 
    5077         166 :     for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
    5078             :     {
    5079         131 :         if (EQUAL(pszParam, "ENDCAP_STYLE"))
    5080             :         {
    5081             :             int nStyle;
    5082          25 :             if (EQUAL(pszValue, "ROUND"))
    5083             :             {
    5084          22 :                 nStyle = GEOSBUF_CAP_ROUND;
    5085             :             }
    5086           3 :             else if (EQUAL(pszValue, "FLAT"))
    5087             :             {
    5088           1 :                 nStyle = GEOSBUF_CAP_FLAT;
    5089             :             }
    5090           2 :             else if (EQUAL(pszValue, "SQUARE"))
    5091             :             {
    5092           1 :                 nStyle = GEOSBUF_CAP_SQUARE;
    5093             :             }
    5094             :             else
    5095             :             {
    5096           1 :                 bParamsAreValid = false;
    5097           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    5098             :                          "Invalid value for ENDCAP_STYLE: %s", pszValue);
    5099           2 :                 break;
    5100             :             }
    5101             : 
    5102          24 :             if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
    5103             :             {
    5104           0 :                 bParamsAreValid = false;
    5105             :             }
    5106             :         }
    5107         106 :         else if (EQUAL(pszParam, "JOIN_STYLE"))
    5108             :         {
    5109             :             int nStyle;
    5110          25 :             if (EQUAL(pszValue, "ROUND"))
    5111             :             {
    5112          21 :                 nStyle = GEOSBUF_JOIN_ROUND;
    5113             :             }
    5114           4 :             else if (EQUAL(pszValue, "MITRE"))
    5115             :             {
    5116           3 :                 nStyle = GEOSBUF_JOIN_MITRE;
    5117             :             }
    5118           1 :             else if (EQUAL(pszValue, "BEVEL"))
    5119             :             {
    5120           0 :                 nStyle = GEOSBUF_JOIN_BEVEL;
    5121             :             }
    5122             :             else
    5123             :             {
    5124           1 :                 bParamsAreValid = false;
    5125           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    5126             :                          "Invalid value for JOIN_STYLE: %s", pszValue);
    5127           1 :                 break;
    5128             :             }
    5129             : 
    5130          24 :             if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
    5131             :             {
    5132           0 :                 bParamsAreValid = false;
    5133           0 :                 break;
    5134             :             }
    5135             :         }
    5136          81 :         else if (EQUAL(pszParam, "MITRE_LIMIT"))
    5137             :         {
    5138             :             try
    5139             :             {
    5140             :                 std::size_t end;
    5141          30 :                 double dfLimit = std::stod(pszValue, &end);
    5142             : 
    5143          24 :                 if (end != strlen(pszValue))
    5144             :                 {
    5145           0 :                     throw std::invalid_argument("");
    5146             :                 }
    5147             : 
    5148          24 :                 if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
    5149             :                                                       dfLimit))
    5150             :                 {
    5151           0 :                     bParamsAreValid = false;
    5152           0 :                     break;
    5153             :                 }
    5154             :             }
    5155           4 :             catch (const std::invalid_argument &)
    5156             :             {
    5157           2 :                 bParamsAreValid = false;
    5158           2 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5159             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5160             :             }
    5161           0 :             catch (const std::out_of_range &)
    5162             :             {
    5163           0 :                 bParamsAreValid = false;
    5164           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5165             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5166             :             }
    5167             :         }
    5168          55 :         else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
    5169             :         {
    5170             :             try
    5171             :             {
    5172             :                 std::size_t end;
    5173          38 :                 int nQuadSegs = std::stoi(pszValue, &end, 10);
    5174             : 
    5175          26 :                 if (end != strlen(pszValue))
    5176             :                 {
    5177           0 :                     throw std::invalid_argument("");
    5178             :                 }
    5179             : 
    5180          26 :                 if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
    5181             :                                                             nQuadSegs))
    5182             :                 {
    5183           0 :                     bParamsAreValid = false;
    5184           0 :                     break;
    5185             :                 }
    5186             :             }
    5187           6 :             catch (const std::invalid_argument &)
    5188             :             {
    5189           3 :                 bParamsAreValid = false;
    5190           3 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5191             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5192             :             }
    5193           2 :             catch (const std::out_of_range &)
    5194             :             {
    5195           1 :                 bParamsAreValid = false;
    5196           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5197             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5198             :             }
    5199             :         }
    5200          25 :         else if (EQUAL(pszParam, "SINGLE_SIDED"))
    5201             :         {
    5202          24 :             bool bSingleSided = CPLTestBool(pszValue);
    5203             : 
    5204          24 :             if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
    5205             :                                                    bSingleSided))
    5206             :             {
    5207           0 :                 bParamsAreValid = false;
    5208           0 :                 break;
    5209             :             }
    5210             :         }
    5211             :         else
    5212             :         {
    5213           1 :             bParamsAreValid = false;
    5214           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    5215             :                      "Unsupported buffer option: %s", pszValue);
    5216             :         }
    5217             :     }
    5218             : 
    5219          35 :     if (bParamsAreValid)
    5220             :     {
    5221          26 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    5222          26 :         if (hGeosGeom != nullptr)
    5223             :         {
    5224             :             GEOSGeom hGeosProduct =
    5225          26 :                 GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
    5226          26 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    5227             : 
    5228          26 :             if (hGeosProduct != nullptr)
    5229             :             {
    5230          26 :                 poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
    5231             :                                                      this, nullptr);
    5232             :             }
    5233             :         }
    5234             :     }
    5235             : 
    5236          35 :     GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
    5237          35 :     freeGEOSContext(hGEOSCtxt);
    5238          35 :     return poOGRProduct;
    5239             : #endif
    5240             : }
    5241             : 
    5242             : /**
    5243             :  * \brief Compute buffer of geometry.
    5244             :  *
    5245             :  * Builds a new geometry containing the buffer region around the geometry
    5246             :  * on which it is invoked.  The buffer is a polygon containing the region within
    5247             :  * the buffer distance of the original geometry.
    5248             :  *
    5249             :  * This function is built on the GEOS library, check it for the definition
    5250             :  * of the geometry operation.
    5251             :  * If OGR is built without the GEOS library, this function will always fail,
    5252             :  * issuing a CPLE_NotSupported error.
    5253             :  *
    5254             :  * The following options are supported. See the GEOS library for more detailed
    5255             :  * descriptions.
    5256             :  *
    5257             :  * <ul>
    5258             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    5259             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    5260             :  * <li>MITRE_LIMIT=double</li>
    5261             :  * <li>QUADRANT_SEGMENTS=int</li>
    5262             :  * <li>SINGLE_SIDED=YES/NO</li>
    5263             :  * </ul>
    5264             :  *
    5265             :  * This function is the same as the C++ method OGRGeometry::BufferEx().
    5266             :  *
    5267             :  * @param hTarget the geometry.
    5268             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5269             :  *               the same unit as the coordinates of the geometry.
    5270             :  * @param papszOptions NULL terminated list of options (may be NULL)
    5271             :  *
    5272             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5273             :  * or NULL if an error occurs.
    5274             :  *
    5275             :  * @since GDAL 3.10
    5276             :  */
    5277             : 
    5278          12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
    5279             :                             CSLConstList papszOptions)
    5280             : 
    5281             : {
    5282          12 :     VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
    5283             : 
    5284          12 :     return OGRGeometry::ToHandle(
    5285          12 :         OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
    5286             : }
    5287             : 
    5288             : /************************************************************************/
    5289             : /*                            Intersection()                            */
    5290             : /************************************************************************/
    5291             : 
    5292             : /**
    5293             :  * \brief Compute intersection.
    5294             :  *
    5295             :  * Generates a new geometry which is the region of intersection of the
    5296             :  * two geometries operated on.  The Intersects() method can be used to test if
    5297             :  * two geometries intersect.
    5298             :  *
    5299             :  * Geometry validity is not checked. In case you are unsure of the validity
    5300             :  * of the input geometries, call IsValid() before, otherwise the result might
    5301             :  * be wrong.
    5302             :  *
    5303             :  * This method is the same as the C function OGR_G_Intersection().
    5304             :  *
    5305             :  * This method is built on the GEOS library, check it for the definition
    5306             :  * of the geometry operation.
    5307             :  * If OGR is built without the GEOS library, this method will always fail,
    5308             :  * issuing a CPLE_NotSupported error.
    5309             :  *
    5310             :  * @param poOtherGeom the other geometry intersected with "this" geometry.
    5311             :  *
    5312             :  * @return a new geometry to be freed by the caller, or NULL if there is no
    5313             :  * intersection or if an error occurs.
    5314             :  *
    5315             :  */
    5316             : 
    5317             : OGRGeometry *
    5318        3105 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5319             : 
    5320             : {
    5321        3105 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5322             :     {
    5323             : #ifndef HAVE_SFCGAL
    5324             : 
    5325           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5326           0 :         return nullptr;
    5327             : 
    5328             : #else
    5329             : 
    5330             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5331             :         if (poThis == nullptr)
    5332             :             return nullptr;
    5333             : 
    5334             :         sfcgal_geometry_t *poOther =
    5335             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5336             :         if (poOther == nullptr)
    5337             :         {
    5338             :             sfcgal_geometry_delete(poThis);
    5339             :             return nullptr;
    5340             :         }
    5341             : 
    5342             :         sfcgal_geometry_t *poRes =
    5343             :             sfcgal_geometry_intersection_3d(poThis, poOther);
    5344             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    5345             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5346             :             poOtherGeom->getSpatialReference() != nullptr &&
    5347             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5348             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5349             : 
    5350             :         sfcgal_geometry_delete(poThis);
    5351             :         sfcgal_geometry_delete(poOther);
    5352             :         sfcgal_geometry_delete(poRes);
    5353             : 
    5354             :         return h_prodGeom;
    5355             : 
    5356             : #endif
    5357             :     }
    5358             : 
    5359             :     else
    5360             :     {
    5361             : #ifndef HAVE_GEOS
    5362             : 
    5363             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5364             :         return nullptr;
    5365             : 
    5366             : #else
    5367        3105 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
    5368             : #endif /* HAVE_GEOS */
    5369             :     }
    5370             : }
    5371             : 
    5372             : /************************************************************************/
    5373             : /*                         OGR_G_Intersection()                         */
    5374             : /************************************************************************/
    5375             : 
    5376             : /**
    5377             :  * \brief Compute intersection.
    5378             :  *
    5379             :  * Generates a new geometry which is the region of intersection of the
    5380             :  * two geometries operated on.  The OGR_G_Intersects() function can be used to
    5381             :  * test if two geometries intersect.
    5382             :  *
    5383             :  * Geometry validity is not checked. In case you are unsure of the validity
    5384             :  * of the input geometries, call IsValid() before, otherwise the result might
    5385             :  * be wrong.
    5386             :  *
    5387             :  * This function is the same as the C++ method OGRGeometry::Intersection().
    5388             :  *
    5389             :  * This function is built on the GEOS library, check it for the definition
    5390             :  * of the geometry operation.
    5391             :  * If OGR is built without the GEOS library, this function will always fail,
    5392             :  * issuing a CPLE_NotSupported error.
    5393             :  *
    5394             :  * @param hThis the geometry.
    5395             :  * @param hOther the other geometry.
    5396             :  *
    5397             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5398             :  * or NULL if there is not intersection of if an error occurs.
    5399             :  */
    5400             : 
    5401          12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
    5402             : 
    5403             : {
    5404          12 :     VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
    5405             : 
    5406          24 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
    5407          24 :         OGRGeometry::FromHandle(hOther)));
    5408             : }
    5409             : 
    5410             : /************************************************************************/
    5411             : /*                               Union()                                */
    5412             : /************************************************************************/
    5413             : 
    5414             : /**
    5415             :  * \brief Compute union.
    5416             :  *
    5417             :  * Generates a new geometry which is the region of union of the
    5418             :  * two geometries operated on.
    5419             :  *
    5420             :  * Geometry validity is not checked. In case you are unsure of the validity
    5421             :  * of the input geometries, call IsValid() before, otherwise the result might
    5422             :  * be wrong.
    5423             :  *
    5424             :  * This method is the same as the C function OGR_G_Union().
    5425             :  *
    5426             :  * This method is built on the GEOS library, check it for the definition
    5427             :  * of the geometry operation.
    5428             :  * If OGR is built without the GEOS library, this method will always fail,
    5429             :  * issuing a CPLE_NotSupported error.
    5430             :  *
    5431             :  * @param poOtherGeom the other geometry unioned with "this" geometry.
    5432             :  *
    5433             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5434             :  */
    5435             : 
    5436             : OGRGeometry *
    5437          68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5438             : 
    5439             : {
    5440          68 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5441             :     {
    5442             : #ifndef HAVE_SFCGAL
    5443             : 
    5444           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5445           0 :         return nullptr;
    5446             : 
    5447             : #else
    5448             : 
    5449             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5450             :         if (poThis == nullptr)
    5451             :             return nullptr;
    5452             : 
    5453             :         sfcgal_geometry_t *poOther =
    5454             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5455             :         if (poOther == nullptr)
    5456             :         {
    5457             :             sfcgal_geometry_delete(poThis);
    5458             :             return nullptr;
    5459             :         }
    5460             : 
    5461             :         sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
    5462             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5463             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5464             :             poOtherGeom->getSpatialReference() != nullptr &&
    5465             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5466             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5467             : 
    5468             :         sfcgal_geometry_delete(poThis);
    5469             :         sfcgal_geometry_delete(poOther);
    5470             :         sfcgal_geometry_delete(poRes);
    5471             : 
    5472             :         return h_prodGeom;
    5473             : 
    5474             : #endif
    5475             :     }
    5476             : 
    5477             :     else
    5478             :     {
    5479             : #ifndef HAVE_GEOS
    5480             : 
    5481             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5482             :         return nullptr;
    5483             : 
    5484             : #else
    5485          68 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
    5486             : #endif /* HAVE_GEOS */
    5487             :     }
    5488             : }
    5489             : 
    5490             : /************************************************************************/
    5491             : /*                            OGR_G_Union()                             */
    5492             : /************************************************************************/
    5493             : 
    5494             : /**
    5495             :  * \brief Compute union.
    5496             :  *
    5497             :  * Generates a new geometry which is the region of union of the
    5498             :  * two geometries operated on.
    5499             :  *
    5500             :  * Geometry validity is not checked. In case you are unsure of the validity
    5501             :  * of the input geometries, call IsValid() before, otherwise the result might
    5502             :  * be wrong.
    5503             :  *
    5504             :  * This function is the same as the C++ method OGRGeometry::Union().
    5505             :  *
    5506             :  * This function is built on the GEOS library, check it for the definition
    5507             :  * of the geometry operation.
    5508             :  * If OGR is built without the GEOS library, this function will always fail,
    5509             :  * issuing a CPLE_NotSupported error.
    5510             :  *
    5511             :  * @param hThis the geometry.
    5512             :  * @param hOther the other geometry.
    5513             :  *
    5514             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5515             :  * or NULL if an error occurs.
    5516             :  */
    5517             : 
    5518          10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
    5519             : 
    5520             : {
    5521          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
    5522             : 
    5523          20 :     return OGRGeometry::ToHandle(
    5524          20 :         OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
    5525             : }
    5526             : 
    5527             : /************************************************************************/
    5528             : /*                           UnionCascaded()                            */
    5529             : /************************************************************************/
    5530             : 
    5531             : /**
    5532             :  * \brief Compute union using cascading.
    5533             :  *
    5534             :  * Geometry validity is not checked. In case you are unsure of the validity
    5535             :  * of the input geometries, call IsValid() before, otherwise the result might
    5536             :  * be wrong.
    5537             :  *
    5538             :  * The input geometry must be a MultiPolygon.
    5539             :  *
    5540             :  * This method is the same as the C function OGR_G_UnionCascaded().
    5541             :  *
    5542             :  * This method is built on the GEOS library, check it for the definition
    5543             :  * of the geometry operation.
    5544             :  * If OGR is built without the GEOS library, this method will always fail,
    5545             :  * issuing a CPLE_NotSupported error.
    5546             :  *
    5547             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5548             :  *
    5549             :  *
    5550             :  * @deprecated Use UnaryUnion() instead
    5551             :  */
    5552             : 
    5553           2 : OGRGeometry *OGRGeometry::UnionCascaded() const
    5554             : 
    5555             : {
    5556             : #ifndef HAVE_GEOS
    5557             : 
    5558             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5559             :     return nullptr;
    5560             : #else
    5561             : 
    5562             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5563             :     if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
    5564             :     {
    5565             :         // GEOS < 3.11 crashes on an empty multipolygon input
    5566             :         auto poRet = new OGRGeometryCollection();
    5567             :         poRet->assignSpatialReference(getSpatialReference());
    5568             :         return poRet;
    5569             :     }
    5570             : #endif
    5571           2 :     OGRGeometry *poOGRProduct = nullptr;
    5572             : 
    5573           2 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5574           2 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5575           2 :     if (hThisGeosGeom != nullptr)
    5576             :     {
    5577           2 :         GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
    5578           2 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5579             : 
    5580             :         poOGRProduct =
    5581           2 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5582             :     }
    5583           2 :     freeGEOSContext(hGEOSCtxt);
    5584             : 
    5585           2 :     return poOGRProduct;
    5586             : 
    5587             : #endif  // HAVE_GEOS
    5588             : }
    5589             : 
    5590             : /************************************************************************/
    5591             : /*                        OGR_G_UnionCascaded()                         */
    5592             : /************************************************************************/
    5593             : 
    5594             : /**
    5595             :  * \brief Compute union using cascading.
    5596             :  *
    5597             :  * Geometry validity is not checked. In case you are unsure of the validity
    5598             :  * of the input geometries, call IsValid() before, otherwise the result might
    5599             :  * be wrong.
    5600             :  *
    5601             :  * The input geometry must be a MultiPolygon.
    5602             :  *
    5603             :  * This function is the same as the C++ method OGRGeometry::UnionCascaded().
    5604             :  *
    5605             :  * This function is built on the GEOS library, check it for the definition
    5606             :  * of the geometry operation.
    5607             :  * If OGR is built without the GEOS library, this function will always fail,
    5608             :  * issuing a CPLE_NotSupported error.
    5609             :  *
    5610             :  * @param hThis the geometry.
    5611             :  *
    5612             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5613             :  * or NULL if an error occurs.
    5614             :  *
    5615             :  * @deprecated Use OGR_G_UnaryUnion() instead
    5616             :  */
    5617             : 
    5618           2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
    5619             : 
    5620             : {
    5621           2 :     VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
    5622             : 
    5623           2 :     return OGRGeometry::ToHandle(
    5624           2 :         OGRGeometry::FromHandle(hThis)->UnionCascaded());
    5625             : }
    5626             : 
    5627             : /************************************************************************/
    5628             : /*                             UnaryUnion()                             */
    5629             : /************************************************************************/
    5630             : 
    5631             : /**
    5632             :  * \brief Returns the union of all components of a single geometry.
    5633             :  *
    5634             :  * Usually used to convert a collection into the smallest set of polygons that
    5635             :  * cover the same area.
    5636             :  *
    5637             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5638             :  *
    5639             :  * This method is the same as the C function OGR_G_UnaryUnion().
    5640             :  *
    5641             :  * This method is built on the GEOS library, check it for the definition
    5642             :  * of the geometry operation.
    5643             :  * If OGR is built without the GEOS library, this method will always fail,
    5644             :  * issuing a CPLE_NotSupported error.
    5645             :  *
    5646             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5647             :  *
    5648             :  * @since GDAL 3.7
    5649             :  */
    5650             : 
    5651         636 : OGRGeometry *OGRGeometry::UnaryUnion() const
    5652             : 
    5653             : {
    5654             : #ifndef HAVE_GEOS
    5655             : 
    5656             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5657             :     return nullptr;
    5658             : #else
    5659             : 
    5660             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5661             :     if (IsEmpty())
    5662             :     {
    5663             :         // GEOS < 3.11 crashes on an empty geometry
    5664             :         auto poRet = new OGRGeometryCollection();
    5665             :         poRet->assignSpatialReference(getSpatialReference());
    5666             :         return poRet;
    5667             :     }
    5668             : #endif
    5669         636 :     OGRGeometry *poOGRProduct = nullptr;
    5670             : 
    5671         636 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5672         636 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5673         636 :     if (hThisGeosGeom != nullptr)
    5674             :     {
    5675         636 :         GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
    5676         636 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5677             : 
    5678             :         poOGRProduct =
    5679         636 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5680             :     }
    5681         636 :     freeGEOSContext(hGEOSCtxt);
    5682             : 
    5683         636 :     return poOGRProduct;
    5684             : 
    5685             : #endif  // HAVE_GEOS
    5686             : }
    5687             : 
    5688             : /************************************************************************/
    5689             : /*                          OGR_G_UnaryUnion()                          */
    5690             : /************************************************************************/
    5691             : 
    5692             : /**
    5693             :  * \brief Returns the union of all components of a single geometry.
    5694             :  *
    5695             :  * Usually used to convert a collection into the smallest set of polygons that
    5696             :  * cover the same area.
    5697             :  *
    5698             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5699             :  *
    5700             :  * Geometry validity is not checked. In case you are unsure of the validity
    5701             :  * of the input geometries, call IsValid() before, otherwise the result might
    5702             :  * be wrong.
    5703             :  *
    5704             :  * This function is the same as the C++ method OGRGeometry::UnaryUnion().
    5705             :  *
    5706             :  * This function is built on the GEOS library, check it for the definition
    5707             :  * of the geometry operation.
    5708             :  * If OGR is built without the GEOS library, this function will always fail,
    5709             :  * issuing a CPLE_NotSupported error.
    5710             :  *
    5711             :  * @param hThis the geometry.
    5712             :  *
    5713             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5714             :  * or NULL if an error occurs.
    5715             :  *
    5716             :  * @since GDAL 3.7
    5717             :  */
    5718             : 
    5719           3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
    5720             : 
    5721             : {
    5722           3 :     VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
    5723             : 
    5724           3 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
    5725             : }
    5726             : 
    5727             : /************************************************************************/
    5728             : /*                             Difference()                             */
    5729             : /************************************************************************/
    5730             : 
    5731             : /**
    5732             :  * \brief Compute difference.
    5733             :  *
    5734             :  * Generates a new geometry which is the region of this geometry with the
    5735             :  * region of the second geometry removed.
    5736             :  *
    5737             :  * Geometry validity is not checked. In case you are unsure of the validity
    5738             :  * of the input geometries, call IsValid() before, otherwise the result might
    5739             :  * be wrong.
    5740             :  *
    5741             :  * This method is the same as the C function OGR_G_Difference().
    5742             :  *
    5743             :  * This method is built on the GEOS library, check it for the definition
    5744             :  * of the geometry operation.
    5745             :  * If OGR is built without the GEOS library, this method will always fail,
    5746             :  * issuing a CPLE_NotSupported error.
    5747             :  *
    5748             :  * @param poOtherGeom the other geometry removed from "this" geometry.
    5749             :  *
    5750             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5751             :  * is empty or if an error occurs.
    5752             :  */
    5753             : 
    5754             : OGRGeometry *
    5755         752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5756             : 
    5757             : {
    5758         752 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5759             :     {
    5760             : #ifndef HAVE_SFCGAL
    5761             : 
    5762           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5763           0 :         return nullptr;
    5764             : 
    5765             : #else
    5766             : 
    5767             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5768             :         if (poThis == nullptr)
    5769             :             return nullptr;
    5770             : 
    5771             :         sfcgal_geometry_t *poOther =
    5772             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5773             :         if (poOther == nullptr)
    5774             :         {
    5775             :             sfcgal_geometry_delete(poThis);
    5776             :             return nullptr;
    5777             :         }
    5778             : 
    5779             :         sfcgal_geometry_t *poRes =
    5780             :             sfcgal_geometry_difference_3d(poThis, poOther);
    5781             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5782             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5783             :             poOtherGeom->getSpatialReference() != nullptr &&
    5784             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5785             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5786             : 
    5787             :         sfcgal_geometry_delete(poThis);
    5788             :         sfcgal_geometry_delete(poOther);
    5789             :         sfcgal_geometry_delete(poRes);
    5790             : 
    5791             :         return h_prodGeom;
    5792             : 
    5793             : #endif
    5794             :     }
    5795             : 
    5796             :     else
    5797             :     {
    5798             : #ifndef HAVE_GEOS
    5799             : 
    5800             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5801             :         return nullptr;
    5802             : 
    5803             : #else
    5804         752 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
    5805             : #endif /* HAVE_GEOS */
    5806             :     }
    5807             : }
    5808             : 
    5809             : /************************************************************************/
    5810             : /*                          OGR_G_Difference()                          */
    5811             : /************************************************************************/
    5812             : 
    5813             : /**
    5814             :  * \brief Compute difference.
    5815             :  *
    5816             :  * Generates a new geometry which is the region of this geometry with the
    5817             :  * region of the other geometry removed.
    5818             :  *
    5819             :  * Geometry validity is not checked. In case you are unsure of the validity
    5820             :  * of the input geometries, call IsValid() before, otherwise the result might
    5821             :  * be wrong.
    5822             :  *
    5823             :  * This function is the same as the C++ method OGRGeometry::Difference().
    5824             :  *
    5825             :  * This function is built on the GEOS library, check it for the definition
    5826             :  * of the geometry operation.
    5827             :  * If OGR is built without the GEOS library, this function will always fail,
    5828             :  * issuing a CPLE_NotSupported error.
    5829             :  *
    5830             :  * @param hThis the geometry.
    5831             :  * @param hOther the other geometry.
    5832             :  *
    5833             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5834             :  * or NULL if the difference is empty or if an error occurs.
    5835             :  */
    5836             : 
    5837           6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
    5838             : 
    5839             : {
    5840           6 :     VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
    5841             : 
    5842          12 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
    5843          12 :         OGRGeometry::FromHandle(hOther)));
    5844             : }
    5845             : 
    5846             : /************************************************************************/
    5847             : /*                           SymDifference()                            */
    5848             : /************************************************************************/
    5849             : 
    5850             : /**
    5851             :  * \brief Compute symmetric difference.
    5852             :  *
    5853             :  * Generates a new geometry which is the symmetric difference of this
    5854             :  * geometry and the second geometry passed into the method.
    5855             :  *
    5856             :  * Geometry validity is not checked. In case you are unsure of the validity
    5857             :  * of the input geometries, call IsValid() before, otherwise the result might
    5858             :  * be wrong.
    5859             :  *
    5860             :  * This method is the same as the C function OGR_G_SymDifference().
    5861             :  *
    5862             :  * This method is built on the GEOS library, check it for the definition
    5863             :  * of the geometry operation.
    5864             :  * If OGR is built without the GEOS library, this method will always fail,
    5865             :  * issuing a CPLE_NotSupported error.
    5866             :  *
    5867             :  * @param poOtherGeom the other geometry.
    5868             :  *
    5869             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5870             :  * is empty or if an error occurs.
    5871             :  *
    5872             :  */
    5873             : 
    5874           7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
    5875             : 
    5876             : {
    5877             :     (void)poOtherGeom;
    5878           7 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5879             :     {
    5880             : #ifndef HAVE_SFCGAL
    5881           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5882           0 :         return nullptr;
    5883             : #else
    5884             :         OGRGeometry *poFirstDifference = Difference(poOtherGeom);
    5885             :         if (poFirstDifference == nullptr)
    5886             :             return nullptr;
    5887             : 
    5888             :         OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
    5889             :         if (poOtherDifference == nullptr)
    5890             :         {
    5891             :             delete poFirstDifference;
    5892             :             return nullptr;
    5893             :         }
    5894             : 
    5895             :         OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
    5896             :         delete poFirstDifference;
    5897             :         delete poOtherDifference;
    5898             :         return poSymDiff;
    5899             : #endif
    5900             :     }
    5901             : 
    5902             : #ifndef HAVE_GEOS
    5903             : 
    5904             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5905             :     return nullptr;
    5906             : 
    5907             : #else
    5908           7 :     return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
    5909             : #endif  // HAVE_GEOS
    5910             : }
    5911             : 
    5912             : //! @cond Doxygen_Suppress
    5913             : /**
    5914             :  * \brief Compute symmetric difference (deprecated)
    5915             :  *
    5916             :  * @deprecated
    5917             :  *
    5918             :  * @see OGRGeometry::SymDifference()
    5919             :  */
    5920             : OGRGeometry *
    5921           0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
    5922             : 
    5923             : {
    5924           0 :     return SymDifference(poOtherGeom);
    5925             : }
    5926             : 
    5927             : //! @endcond
    5928             : 
    5929             : /************************************************************************/
    5930             : /*                        OGR_G_SymDifference()                         */
    5931             : /************************************************************************/
    5932             : 
    5933             : /**
    5934             :  * \brief Compute symmetric difference.
    5935             :  *
    5936             :  * Generates a new geometry which is the symmetric difference of this
    5937             :  * geometry and the other geometry.
    5938             :  *
    5939             :  * Geometry validity is not checked. In case you are unsure of the validity
    5940             :  * of the input geometries, call IsValid() before, otherwise the result might
    5941             :  * be wrong.
    5942             :  *
    5943             :  * This function is the same as the C++ method
    5944             :  * OGRGeometry::SymmetricDifference().
    5945             :  *
    5946             :  * This function is built on the GEOS library, check it for the definition
    5947             :  * of the geometry operation.
    5948             :  * If OGR is built without the GEOS library, this function will always fail,
    5949             :  * issuing a CPLE_NotSupported error.
    5950             :  *
    5951             :  * @param hThis the geometry.
    5952             :  * @param hOther the other geometry.
    5953             :  *
    5954             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5955             :  * or NULL if the difference is empty or if an error occurs.
    5956             :  *
    5957             :  */
    5958             : 
    5959           7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5960             : 
    5961             : {
    5962           7 :     VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
    5963             : 
    5964          14 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5965          14 :         OGRGeometry::FromHandle(hOther)));
    5966             : }
    5967             : 
    5968             : /**
    5969             :  * \brief Compute symmetric difference (deprecated)
    5970             :  *
    5971             :  * @deprecated
    5972             :  *
    5973             :  * @see OGR_G_SymmetricDifference()
    5974             :  */
    5975           0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5976             : 
    5977             : {
    5978           0 :     VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
    5979             : 
    5980           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5981           0 :         OGRGeometry::FromHandle(hOther)));
    5982             : }
    5983             : 
    5984             : /************************************************************************/
    5985             : /*                              Disjoint()                              */
    5986             : /************************************************************************/
    5987             : 
    5988             : /**
    5989             :  * \brief Test for disjointness.
    5990             :  *
    5991             :  * Tests if this geometry and the other passed into the method are disjoint.
    5992             :  *
    5993             :  * Geometry validity is not checked. In case you are unsure of the validity
    5994             :  * of the input geometries, call IsValid() before, otherwise the result might
    5995             :  * be wrong.
    5996             :  *
    5997             :  * This method is the same as the C function OGR_G_Disjoint().
    5998             :  *
    5999             :  * This method is built on the GEOS library, check it for the definition
    6000             :  * of the geometry operation.
    6001             :  * If OGR is built without the GEOS library, this method will always fail,
    6002             :  * issuing a CPLE_NotSupported error.
    6003             :  *
    6004             :  * @param poOtherGeom the geometry to compare to this geometry.
    6005             :  *
    6006             :  * @return TRUE if they are disjoint, otherwise FALSE.
    6007             :  */
    6008             : 
    6009           8 : bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
    6010             : 
    6011             : {
    6012             :     (void)poOtherGeom;
    6013             : #ifndef HAVE_GEOS
    6014             : 
    6015             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6016             :     return FALSE;
    6017             : 
    6018             : #else
    6019           8 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
    6020             : #endif  // HAVE_GEOS
    6021             : }
    6022             : 
    6023             : /************************************************************************/
    6024             : /*                           OGR_G_Disjoint()                           */
    6025             : /************************************************************************/
    6026             : 
    6027             : /**
    6028             :  * \brief Test for disjointness.
    6029             :  *
    6030             :  * Tests if this geometry and the other geometry are disjoint.
    6031             :  *
    6032             :  * Geometry validity is not checked. In case you are unsure of the validity
    6033             :  * of the input geometries, call IsValid() before, otherwise the result might
    6034             :  * be wrong.
    6035             :  *
    6036             :  * This function is the same as the C++ method OGRGeometry::Disjoint().
    6037             :  *
    6038             :  * This function is built on the GEOS library, check it for the definition
    6039             :  * of the geometry operation.
    6040             :  * If OGR is built without the GEOS library, this function will always fail,
    6041             :  * issuing a CPLE_NotSupported error.
    6042             :  *
    6043             :  * @param hThis the geometry to compare.
    6044             :  * @param hOther the other geometry to compare.
    6045             :  *
    6046             :  * @return TRUE if they are disjoint, otherwise FALSE.
    6047             :  */
    6048           8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
    6049             : 
    6050             : {
    6051           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
    6052             : 
    6053          16 :     return OGRGeometry::FromHandle(hThis)->Disjoint(
    6054          16 :         OGRGeometry::FromHandle(hOther));
    6055             : }
    6056             : 
    6057             : /************************************************************************/
    6058             : /*                              Touches()                               */
    6059             : /************************************************************************/
    6060             : 
    6061             : /**
    6062             :  * \brief Test for touching.
    6063             :  *
    6064             :  * Tests if this geometry and the other passed into the method are touching.
    6065             :  *
    6066             :  * Geometry validity is not checked. In case you are unsure of the validity
    6067             :  * of the input geometries, call IsValid() before, otherwise the result might
    6068             :  * be wrong.
    6069             :  *
    6070             :  * This method is the same as the C function OGR_G_Touches().
    6071             :  *
    6072             :  * This method is built on the GEOS library, check it for the definition
    6073             :  * of the geometry operation.
    6074             :  * If OGR is built without the GEOS library, this method will always fail,
    6075             :  * issuing a CPLE_NotSupported error.
    6076             :  *
    6077             :  * @param poOtherGeom the geometry to compare to this geometry.
    6078             :  *
    6079             :  * @return TRUE if they are touching, otherwise FALSE.
    6080             :  */
    6081             : 
    6082          11 : bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
    6083             : 
    6084             : {
    6085             :     (void)poOtherGeom;
    6086             : #ifndef HAVE_GEOS
    6087             : 
    6088             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6089             :     return FALSE;
    6090             : 
    6091             : #else
    6092          11 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
    6093             : #endif  // HAVE_GEOS
    6094             : }
    6095             : 
    6096             : /************************************************************************/
    6097             : /*                           OGR_G_Touches()                            */
    6098             : /************************************************************************/
    6099             : /**
    6100             :  * \brief Test for touching.
    6101             :  *
    6102             :  * Tests if this geometry and the other geometry are touching.
    6103             :  *
    6104             :  * Geometry validity is not checked. In case you are unsure of the validity
    6105             :  * of the input geometries, call IsValid() before, otherwise the result might
    6106             :  * be wrong.
    6107             :  *
    6108             :  * This function is the same as the C++ method OGRGeometry::Touches().
    6109             :  *
    6110             :  * This function is built on the GEOS library, check it for the definition
    6111             :  * of the geometry operation.
    6112             :  * If OGR is built without the GEOS library, this function will always fail,
    6113             :  * issuing a CPLE_NotSupported error.
    6114             :  *
    6115             :  * @param hThis the geometry to compare.
    6116             :  * @param hOther the other geometry to compare.
    6117             :  *
    6118             :  * @return TRUE if they are touching, otherwise FALSE.
    6119             :  */
    6120             : 
    6121           8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
    6122             : 
    6123             : {
    6124           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
    6125             : 
    6126          16 :     return OGRGeometry::FromHandle(hThis)->Touches(
    6127          16 :         OGRGeometry::FromHandle(hOther));
    6128             : }
    6129             : 
    6130             : /************************************************************************/
    6131             : /*                              Crosses()                               */
    6132             : /************************************************************************/
    6133             : 
    6134             : /**
    6135             :  * \brief Test for crossing.
    6136             :  *
    6137             :  * Tests if this geometry and the other passed into the method are crossing.
    6138             :  *
    6139             :  * Geometry validity is not checked. In case you are unsure of the validity
    6140             :  * of the input geometries, call IsValid() before, otherwise the result might
    6141             :  * be wrong.
    6142             :  *
    6143             :  * This method is the same as the C function OGR_G_Crosses().
    6144             :  *
    6145             :  * This method is built on the GEOS library, check it for the definition
    6146             :  * of the geometry operation.
    6147             :  * If OGR is built without the GEOS library, this method will always fail,
    6148             :  * issuing a CPLE_NotSupported error.
    6149             :  *
    6150             :  * @param poOtherGeom the geometry to compare to this geometry.
    6151             :  *
    6152             :  * @return TRUE if they are crossing, otherwise FALSE.
    6153             :  */
    6154             : 
    6155           8 : bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    6156             : 
    6157             : {
    6158           8 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    6159             :     {
    6160             : #ifndef HAVE_SFCGAL
    6161             : 
    6162           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    6163           0 :         return FALSE;
    6164             : 
    6165             : #else
    6166             : 
    6167             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    6168             :         if (poThis == nullptr)
    6169             :             return FALSE;
    6170             : 
    6171             :         sfcgal_geometry_t *poOther =
    6172             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    6173             :         if (poOther == nullptr)
    6174             :         {
    6175             :             sfcgal_geometry_delete(poThis);
    6176             :             return FALSE;
    6177             :         }
    6178             : 
    6179             :         int res = sfcgal_geometry_intersects_3d(poThis, poOther);
    6180             : 
    6181             :         sfcgal_geometry_delete(poThis);
    6182             :         sfcgal_geometry_delete(poOther);
    6183             : 
    6184             :         return (res == 1) ? TRUE : FALSE;
    6185             : 
    6186             : #endif
    6187             :     }
    6188             : 
    6189             :     else
    6190             :     {
    6191             : 
    6192             : #ifndef HAVE_GEOS
    6193             : 
    6194             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6195             :         return FALSE;
    6196             : 
    6197             : #else
    6198           8 :         return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
    6199             : #endif /* HAVE_GEOS */
    6200             :     }
    6201             : }
    6202             : 
    6203             : /************************************************************************/
    6204             : /*                           OGR_G_Crosses()                            */
    6205             : /************************************************************************/
    6206             : /**
    6207             :  * \brief Test for crossing.
    6208             :  *
    6209             :  * Tests if this geometry and the other geometry are crossing.
    6210             :  *
    6211             :  * Geometry validity is not checked. In case you are unsure of the validity
    6212             :  * of the input geometries, call IsValid() before, otherwise the result might
    6213             :  * be wrong.
    6214             :  *
    6215             :  * This function is the same as the C++ method OGRGeometry::Crosses().
    6216             :  *
    6217             :  * This function is built on the GEOS library, check it for the definition
    6218             :  * of the geometry operation.
    6219             :  * If OGR is built without the GEOS library, this function will always fail,
    6220             :  * issuing a CPLE_NotSupported error.
    6221             :  *
    6222             :  * @param hThis the geometry to compare.
    6223             :  * @param hOther the other geometry to compare.
    6224             :  *
    6225             :  * @return TRUE if they are crossing, otherwise FALSE.
    6226             :  */
    6227             : 
    6228           8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
    6229             : 
    6230             : {
    6231           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
    6232             : 
    6233          16 :     return OGRGeometry::FromHandle(hThis)->Crosses(
    6234          16 :         OGRGeometry::FromHandle(hOther));
    6235             : }
    6236             : 
    6237             : /************************************************************************/
    6238             : /*                               Within()                               */
    6239             : /************************************************************************/
    6240             : 
    6241             : /**
    6242             :  * \brief Test for containment.
    6243             :  *
    6244             :  * Tests if actual geometry object is within the passed geometry.
    6245             :  *
    6246             :  * Geometry validity is not checked. In case you are unsure of the validity
    6247             :  * of the input geometries, call IsValid() before, otherwise the result might
    6248             :  * be wrong.
    6249             :  *
    6250             :  * This method is the same as the C function OGR_G_Within().
    6251             :  *
    6252             :  * This method is built on the GEOS library, check it for the definition
    6253             :  * of the geometry operation.
    6254             :  * If OGR is built without the GEOS library, this method will always fail,
    6255             :  * issuing a CPLE_NotSupported error.
    6256             :  *
    6257             :  * @param poOtherGeom the geometry to compare to this geometry.
    6258             :  *
    6259             :  * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
    6260             :  */
    6261             : 
    6262       22417 : bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
    6263             : 
    6264             : {
    6265             :     (void)poOtherGeom;
    6266             : #ifndef HAVE_GEOS
    6267             : 
    6268             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6269             :     return FALSE;
    6270             : 
    6271             : #else
    6272       22417 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
    6273             : #endif  // HAVE_GEOS
    6274             : }
    6275             : 
    6276             : /************************************************************************/
    6277             : /*                            OGR_G_Within()                            */
    6278             : /************************************************************************/
    6279             : 
    6280             : /**
    6281             :  * \brief Test for containment.
    6282             :  *
    6283             :  * Tests if this geometry is within the other geometry.
    6284             :  *
    6285             :  * Geometry validity is not checked. In case you are unsure of the validity
    6286             :  * of the input geometries, call IsValid() before, otherwise the result might
    6287             :  * be wrong.
    6288             :  *
    6289             :  * This function is the same as the C++ method OGRGeometry::Within().
    6290             :  *
    6291             :  * This function is built on the GEOS library, check it for the definition
    6292             :  * of the geometry operation.
    6293             :  * If OGR is built without the GEOS library, this function will always fail,
    6294             :  * issuing a CPLE_NotSupported error.
    6295             :  *
    6296             :  * @param hThis the geometry to compare.
    6297             :  * @param hOther the other geometry to compare.
    6298             :  *
    6299             :  * @return TRUE if hThis is within hOther, otherwise FALSE.
    6300             :  */
    6301        7374 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
    6302             : 
    6303             : {
    6304        7374 :     VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
    6305             : 
    6306       14748 :     return OGRGeometry::FromHandle(hThis)->Within(
    6307       14748 :         OGRGeometry::FromHandle(hOther));
    6308             : }
    6309             : 
    6310             : /************************************************************************/
    6311             : /*                              Contains()                              */
    6312             : /************************************************************************/
    6313             : 
    6314             : /**
    6315             :  * \brief Test for containment.
    6316             :  *
    6317             :  * Tests if actual geometry object contains the passed geometry.
    6318             :  *
    6319             :  * Geometry validity is not checked. In case you are unsure of the validity
    6320             :  * of the input geometries, call IsValid() before, otherwise the result might
    6321             :  * be wrong.
    6322             :  *
    6323             :  * This method is the same as the C function OGR_G_Contains().
    6324             :  *
    6325             :  * This method is built on the GEOS library, check it for the definition
    6326             :  * of the geometry operation.
    6327             :  * If OGR is built without the GEOS library, this method will always fail,
    6328             :  * issuing a CPLE_NotSupported error.
    6329             :  *
    6330             :  * @param poOtherGeom the geometry to compare to this geometry.
    6331             :  *
    6332             :  * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
    6333             :  */
    6334             : 
    6335         322 : bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
    6336             : 
    6337             : {
    6338             :     (void)poOtherGeom;
    6339             : #ifndef HAVE_GEOS
    6340             : 
    6341             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6342             :     return FALSE;
    6343             : 
    6344             : #else
    6345         322 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
    6346             : #endif  // HAVE_GEOS
    6347             : }
    6348             : 
    6349             : /************************************************************************/
    6350             : /*                           OGR_G_Contains()                           */
    6351             : /************************************************************************/
    6352             : 
    6353             : /**
    6354             :  * \brief Test for containment.
    6355             :  *
    6356             :  * Tests if this geometry contains the other geometry.
    6357             :  *
    6358             :  * Geometry validity is not checked. In case you are unsure of the validity
    6359             :  * of the input geometries, call IsValid() before, otherwise the result might
    6360             :  * be wrong.
    6361             :  *
    6362             :  * This function is the same as the C++ method OGRGeometry::Contains().
    6363             :  *
    6364             :  * This function is built on the GEOS library, check it for the definition
    6365             :  * of the geometry operation.
    6366             :  * If OGR is built without the GEOS library, this function will always fail,
    6367             :  * issuing a CPLE_NotSupported error.
    6368             :  *
    6369             :  * @param hThis the geometry to compare.
    6370             :  * @param hOther the other geometry to compare.
    6371             :  *
    6372             :  * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
    6373             :  */
    6374          10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
    6375             : 
    6376             : {
    6377          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
    6378             : 
    6379          20 :     return OGRGeometry::FromHandle(hThis)->Contains(
    6380          20 :         OGRGeometry::FromHandle(hOther));
    6381             : }
    6382             : 
    6383             : /************************************************************************/
    6384             : /*                              Overlaps()                              */
    6385             : /************************************************************************/
    6386             : 
    6387             : /**
    6388             :  * \brief Test for overlap.
    6389             :  *
    6390             :  * Tests if this geometry and the other passed into the method overlap, that is
    6391             :  * their intersection has a non-zero area.
    6392             :  *
    6393             :  * Geometry validity is not checked. In case you are unsure of the validity
    6394             :  * of the input geometries, call IsValid() before, otherwise the result might
    6395             :  * be wrong.
    6396             :  *
    6397             :  * This method is the same as the C function OGR_G_Overlaps().
    6398             :  *
    6399             :  * This method is built on the GEOS library, check it for the definition
    6400             :  * of the geometry operation.
    6401             :  * If OGR is built without the GEOS library, this method will always fail,
    6402             :  * issuing a CPLE_NotSupported error.
    6403             :  *
    6404             :  * @param poOtherGeom the geometry to compare to this geometry.
    6405             :  *
    6406             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6407             :  */
    6408             : 
    6409           7 : bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
    6410             : 
    6411             : {
    6412             :     (void)poOtherGeom;
    6413             : #ifndef HAVE_GEOS
    6414             : 
    6415             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6416             :     return FALSE;
    6417             : 
    6418             : #else
    6419           7 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
    6420             : #endif  // HAVE_GEOS
    6421             : }
    6422             : 
    6423             : /************************************************************************/
    6424             : /*                           OGR_G_Overlaps()                           */
    6425             : /************************************************************************/
    6426             : /**
    6427             :  * \brief Test for overlap.
    6428             :  *
    6429             :  * Tests if this geometry and the other geometry overlap, that is their
    6430             :  * intersection has a non-zero area.
    6431             :  *
    6432             :  * Geometry validity is not checked. In case you are unsure of the validity
    6433             :  * of the input geometries, call IsValid() before, otherwise the result might
    6434             :  * be wrong.
    6435             :  *
    6436             :  * This function is the same as the C++ method OGRGeometry::Overlaps().
    6437             :  *
    6438             :  * This function is built on the GEOS library, check it for the definition
    6439             :  * of the geometry operation.
    6440             :  * If OGR is built without the GEOS library, this function will always fail,
    6441             :  * issuing a CPLE_NotSupported error.
    6442             :  *
    6443             :  * @param hThis the geometry to compare.
    6444             :  * @param hOther the other geometry to compare.
    6445             :  *
    6446             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6447             :  */
    6448             : 
    6449           7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
    6450             : 
    6451             : {
    6452           7 :     VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
    6453             : 
    6454          14 :     return OGRGeometry::FromHandle(hThis)->Overlaps(
    6455          14 :         OGRGeometry::FromHandle(hOther));
    6456             : }
    6457             : 
    6458             : /************************************************************************/
    6459             : /*                             closeRings()                             */
    6460             : /************************************************************************/
    6461             : 
    6462             : /**
    6463             :  * \brief Force rings to be closed.
    6464             :  *
    6465             :  * If this geometry, or any contained geometries has polygon rings that
    6466             :  * are not closed, they will be closed by adding the starting point at
    6467             :  * the end.
    6468             :  */
    6469             : 
    6470        1264 : void OGRGeometry::closeRings()
    6471             : {
    6472        1264 : }
    6473             : 
    6474             : /************************************************************************/
    6475             : /*                          OGR_G_CloseRings()                          */
    6476             : /************************************************************************/
    6477             : 
    6478             : /**
    6479             :  * \brief Force rings to be closed.
    6480             :  *
    6481             :  * If this geometry, or any contained geometries has polygon rings that
    6482             :  * are not closed, they will be closed by adding the starting point at
    6483             :  * the end.
    6484             :  *
    6485             :  * @param hGeom handle to the geometry.
    6486             :  */
    6487             : 
    6488           6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
    6489             : 
    6490             : {
    6491           6 :     VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
    6492             : 
    6493           6 :     OGRGeometry::FromHandle(hGeom)->closeRings();
    6494             : }
    6495             : 
    6496             : /************************************************************************/
    6497             : /*                              Centroid()                              */
    6498             : /************************************************************************/
    6499             : 
    6500             : /**
    6501             :  * \brief Compute the geometry centroid.
    6502             :  *
    6503             :  * The centroid location is applied to the passed in OGRPoint object.
    6504             :  * The centroid is not necessarily within the geometry.
    6505             :  *
    6506             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6507             :  * however the current implementation based on GEOS can operate on other
    6508             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6509             :  * multipolygons.
    6510             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6511             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6512             :  * (multipolygons).
    6513             :  *
    6514             :  * This function is the same as the C function OGR_G_Centroid().
    6515             :  *
    6516             :  * This function is built on the GEOS library, check it for the definition
    6517             :  * of the geometry operation.
    6518             :  * If OGR is built without the GEOS library, this function will always fail,
    6519             :  * issuing a CPLE_NotSupported error.
    6520             :  *
    6521             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6522             :  *
    6523             :  * to OGRPolygon)
    6524             :  */
    6525             : 
    6526           5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
    6527             : 
    6528             : {
    6529           5 :     if (poPoint == nullptr)
    6530           0 :         return OGRERR_FAILURE;
    6531             : 
    6532             : #ifndef HAVE_GEOS
    6533             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6534             :     return OGRERR_FAILURE;
    6535             : 
    6536             : #else
    6537             : 
    6538           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6539             :     GEOSGeom hThisGeosGeom =
    6540           5 :         exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
    6541             : 
    6542           5 :     if (hThisGeosGeom != nullptr)
    6543             :     {
    6544           5 :         GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
    6545           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6546             : 
    6547           5 :         if (hOtherGeosGeom == nullptr)
    6548             :         {
    6549           0 :             freeGEOSContext(hGEOSCtxt);
    6550           0 :             return OGRERR_FAILURE;
    6551             :         }
    6552             : 
    6553             :         OGRGeometry *poCentroidGeom =
    6554           5 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6555             : 
    6556           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6557             : 
    6558           5 :         if (poCentroidGeom == nullptr)
    6559             :         {
    6560           0 :             freeGEOSContext(hGEOSCtxt);
    6561           0 :             return OGRERR_FAILURE;
    6562             :         }
    6563           5 :         if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6564             :         {
    6565           0 :             delete poCentroidGeom;
    6566           0 :             freeGEOSContext(hGEOSCtxt);
    6567           0 :             return OGRERR_FAILURE;
    6568             :         }
    6569             : 
    6570           5 :         if (getSpatialReference() != nullptr)
    6571           0 :             poCentroidGeom->assignSpatialReference(getSpatialReference());
    6572             : 
    6573           5 :         OGRPoint *poCentroid = poCentroidGeom->toPoint();
    6574             : 
    6575           5 :         if (!poCentroid->IsEmpty())
    6576             :         {
    6577           4 :             poPoint->setX(poCentroid->getX());
    6578           4 :             poPoint->setY(poCentroid->getY());
    6579             :         }
    6580             :         else
    6581             :         {
    6582           1 :             poPoint->empty();
    6583             :         }
    6584             : 
    6585           5 :         delete poCentroidGeom;
    6586             : 
    6587           5 :         freeGEOSContext(hGEOSCtxt);
    6588           5 :         return OGRERR_NONE;
    6589             :     }
    6590             :     else
    6591             :     {
    6592           0 :         freeGEOSContext(hGEOSCtxt);
    6593           0 :         return OGRERR_FAILURE;
    6594             :     }
    6595             : 
    6596             : #endif  // HAVE_GEOS
    6597             : }
    6598             : 
    6599             : /************************************************************************/
    6600             : /*                           OGR_G_Centroid()                           */
    6601             : /************************************************************************/
    6602             : 
    6603             : /**
    6604             :  * \brief Compute the geometry centroid.
    6605             :  *
    6606             :  * The centroid location is applied to the passed in OGRPoint object.
    6607             :  * The centroid is not necessarily within the geometry.
    6608             :  *
    6609             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6610             :  * however the current implementation based on GEOS can operate on other
    6611             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6612             :  * multipolygons.
    6613             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6614             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6615             :  * (multipolygons).
    6616             :  *
    6617             :  * This function is the same as the C++ method OGRGeometry::Centroid().
    6618             :  *
    6619             :  * This function is built on the GEOS library, check it for the definition
    6620             :  * of the geometry operation.
    6621             :  * If OGR is built without the GEOS library, this function will always fail,
    6622             :  * issuing a CPLE_NotSupported error.
    6623             :  *
    6624             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6625             :  */
    6626             : 
    6627           5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
    6628             : 
    6629             : {
    6630           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
    6631             : 
    6632           5 :     OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
    6633           5 :     if (poCentroidGeom == nullptr)
    6634           0 :         return OGRERR_FAILURE;
    6635           5 :     if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6636             :     {
    6637           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6638             :                  "Passed wrong geometry type as centroid argument.");
    6639           0 :         return OGRERR_FAILURE;
    6640             :     }
    6641             : 
    6642           5 :     return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
    6643             : }
    6644             : 
    6645             : /************************************************************************/
    6646             : /*                        OGR_G_PointOnSurface()                        */
    6647             : /************************************************************************/
    6648             : 
    6649             : /**
    6650             :  * \brief Returns a point guaranteed to lie on the surface.
    6651             :  *
    6652             :  * This method relates to the SFCOM ISurface::get_PointOnSurface() method
    6653             :  * however the current implementation based on GEOS can operate on other
    6654             :  * geometry types than the types that are supported by SQL/MM-Part 3 :
    6655             :  * surfaces (polygons) and multisurfaces (multipolygons).
    6656             :  *
    6657             :  * This method is built on the GEOS library, check it for the definition
    6658             :  * of the geometry operation.
    6659             :  * If OGR is built without the GEOS library, this method will always fail,
    6660             :  * issuing a CPLE_NotSupported error.
    6661             :  *
    6662             :  * @param hGeom the geometry to operate on.
    6663             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6664             :  * or NULL if an error occurs.
    6665             :  *
    6666             :  */
    6667             : 
    6668           4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
    6669             : 
    6670             : {
    6671           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
    6672             : 
    6673             : #ifndef HAVE_GEOS
    6674             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6675             :     return nullptr;
    6676             : #else
    6677             : 
    6678           4 :     OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
    6679             : 
    6680           4 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    6681           4 :     GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
    6682             : 
    6683           4 :     if (hThisGeosGeom != nullptr)
    6684             :     {
    6685             :         GEOSGeom hOtherGeosGeom =
    6686           4 :             GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
    6687           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6688             : 
    6689           4 :         if (hOtherGeosGeom == nullptr)
    6690             :         {
    6691           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6692           0 :             return nullptr;
    6693             :         }
    6694             : 
    6695             :         OGRGeometry *poInsidePointGeom =
    6696           4 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6697             : 
    6698           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6699             : 
    6700           4 :         if (poInsidePointGeom == nullptr)
    6701             :         {
    6702           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6703           0 :             return nullptr;
    6704             :         }
    6705           4 :         if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
    6706             :         {
    6707           0 :             delete poInsidePointGeom;
    6708           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6709           0 :             return nullptr;
    6710             :         }
    6711             : 
    6712           4 :         if (poThis->getSpatialReference() != nullptr)
    6713           0 :             poInsidePointGeom->assignSpatialReference(
    6714           0 :                 poThis->getSpatialReference());
    6715             : 
    6716           4 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6717           4 :         return OGRGeometry::ToHandle(poInsidePointGeom);
    6718             :     }
    6719             : 
    6720           0 :     OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6721           0 :     return nullptr;
    6722             : #endif
    6723             : }
    6724             : 
    6725             : /************************************************************************/
    6726             : /*                       PointOnSurfaceInternal()                       */
    6727             : /************************************************************************/
    6728             : 
    6729             : //! @cond Doxygen_Suppress
    6730           0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
    6731             : {
    6732           0 :     if (poPoint == nullptr || poPoint->IsEmpty())
    6733           0 :         return OGRERR_FAILURE;
    6734             : 
    6735           0 :     OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
    6736             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
    6737           0 :     if (hInsidePoint == nullptr)
    6738           0 :         return OGRERR_FAILURE;
    6739             : 
    6740           0 :     OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
    6741           0 :     if (poInsidePoint->IsEmpty())
    6742             :     {
    6743           0 :         poPoint->empty();
    6744             :     }
    6745             :     else
    6746             :     {
    6747           0 :         poPoint->setX(poInsidePoint->getX());
    6748           0 :         poPoint->setY(poInsidePoint->getY());
    6749             :     }
    6750             : 
    6751           0 :     OGR_G_DestroyGeometry(hInsidePoint);
    6752             : 
    6753           0 :     return OGRERR_NONE;
    6754             : }
    6755             : 
    6756             : //! @endcond
    6757             : 
    6758             : /************************************************************************/
    6759             : /*                              Simplify()                              */
    6760             : /************************************************************************/
    6761             : 
    6762             : /**
    6763             :  * \brief Simplify the geometry.
    6764             :  *
    6765             :  * This function is the same as the C function OGR_G_Simplify().
    6766             :  *
    6767             :  * This function is built on the GEOS library, check it for the definition
    6768             :  * of the geometry operation.
    6769             :  * If OGR is built without the GEOS library, this function will always fail,
    6770             :  * issuing a CPLE_NotSupported error.
    6771             :  *
    6772             :  * @param dTolerance the distance tolerance for the simplification.
    6773             :  *
    6774             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6775             :  *
    6776             :  */
    6777             : 
    6778          55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
    6779             : 
    6780             : {
    6781             :     (void)dTolerance;
    6782             : #ifndef HAVE_GEOS
    6783             : 
    6784             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6785             :     return nullptr;
    6786             : 
    6787             : #else
    6788          55 :     OGRGeometry *poOGRProduct = nullptr;
    6789             : 
    6790          55 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6791          55 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6792          55 :     if (hThisGeosGeom != nullptr)
    6793             :     {
    6794             :         GEOSGeom hGeosProduct =
    6795          55 :             GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
    6796          55 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6797             :         poOGRProduct =
    6798          55 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6799             :     }
    6800          55 :     freeGEOSContext(hGEOSCtxt);
    6801          55 :     return poOGRProduct;
    6802             : 
    6803             : #endif  // HAVE_GEOS
    6804             : }
    6805             : 
    6806             : /************************************************************************/
    6807             : /*                           OGR_G_Simplify()                           */
    6808             : /************************************************************************/
    6809             : 
    6810             : /**
    6811             :  * \brief Compute a simplified geometry.
    6812             :  *
    6813             :  * This function is the same as the C++ method OGRGeometry::Simplify().
    6814             :  *
    6815             :  * This function is built on the GEOS library, check it for the definition
    6816             :  * of the geometry operation.
    6817             :  * If OGR is built without the GEOS library, this function will always fail,
    6818             :  * issuing a CPLE_NotSupported error.
    6819             :  *
    6820             :  * @param hThis the geometry.
    6821             :  * @param dTolerance the distance tolerance for the simplification.
    6822             :  *
    6823             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6824             :  * or NULL if an error occurs.
    6825             :  *
    6826             :  */
    6827             : 
    6828           1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
    6829             : 
    6830             : {
    6831           1 :     VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
    6832           1 :     return OGRGeometry::ToHandle(
    6833           1 :         OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
    6834             : }
    6835             : 
    6836             : /************************************************************************/
    6837             : /*                      SimplifyPreserveTopology()                      */
    6838             : /************************************************************************/
    6839             : 
    6840             : /**
    6841             :  * \brief Simplify the geometry while preserving topology.
    6842             :  *
    6843             :  * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
    6844             :  *
    6845             :  * This function is built on the GEOS library, check it for the definition
    6846             :  * of the geometry operation.
    6847             :  * If OGR is built without the GEOS library, this function will always fail,
    6848             :  * issuing a CPLE_NotSupported error.
    6849             :  *
    6850             :  * @param dTolerance the distance tolerance for the simplification.
    6851             :  *
    6852             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6853             :  *
    6854             :  */
    6855             : 
    6856          17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
    6857             : 
    6858             : {
    6859             :     (void)dTolerance;
    6860             : #ifndef HAVE_GEOS
    6861             : 
    6862             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6863             :     return nullptr;
    6864             : 
    6865             : #else
    6866          17 :     OGRGeometry *poOGRProduct = nullptr;
    6867             : 
    6868          17 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6869          17 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6870          17 :     if (hThisGeosGeom != nullptr)
    6871             :     {
    6872          17 :         GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
    6873             :             hGEOSCtxt, hThisGeosGeom, dTolerance);
    6874          17 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6875             :         poOGRProduct =
    6876          17 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6877             :     }
    6878          17 :     freeGEOSContext(hGEOSCtxt);
    6879          17 :     return poOGRProduct;
    6880             : 
    6881             : #endif  // HAVE_GEOS
    6882             : }
    6883             : 
    6884             : /************************************************************************/
    6885             : /*                   OGR_G_SimplifyPreserveTopology()                   */
    6886             : /************************************************************************/
    6887             : 
    6888             : /**
    6889             :  * \brief Simplify the geometry while preserving topology.
    6890             :  *
    6891             :  * This function is the same as the C++ method
    6892             :  * OGRGeometry::SimplifyPreserveTopology().
    6893             :  *
    6894             :  * This function is built on the GEOS library, check it for the definition
    6895             :  * of the geometry operation.
    6896             :  * If OGR is built without the GEOS library, this function will always fail,
    6897             :  * issuing a CPLE_NotSupported error.
    6898             :  *
    6899             :  * @param hThis the geometry.
    6900             :  * @param dTolerance the distance tolerance for the simplification.
    6901             :  *
    6902             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6903             :  * or NULL if an error occurs.
    6904             :  *
    6905             :  */
    6906             : 
    6907           1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
    6908             :                                             double dTolerance)
    6909             : 
    6910             : {
    6911           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
    6912           1 :     return OGRGeometry::ToHandle(
    6913           1 :         OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
    6914             : }
    6915             : 
    6916             : /************************************************************************/
    6917             : /*                          roundCoordinates()                          */
    6918             : /************************************************************************/
    6919             : 
    6920             : /** Round coordinates of the geometry to the specified precision.
    6921             :  *
    6922             :  * Note that this is not the same as OGRGeometry::SetPrecision(). The later
    6923             :  * will return valid geometries, whereas roundCoordinates() does not make
    6924             :  * such guarantee and may return geometries with invalidities, if they are
    6925             :  * not compatible with the specified precision. roundCoordinates() supports
    6926             :  * curve geometries, whereas SetPrecision() does not currently.
    6927             :  *
    6928             :  * One use case for roundCoordinates() is to undo the effect of
    6929             :  * quantizeCoordinates().
    6930             :  *
    6931             :  * @param sPrecision Contains the precision requirements.
    6932             :  * @since GDAL 3.9
    6933             :  */
    6934          39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
    6935             : {
    6936             :     struct Rounder : public OGRDefaultGeometryVisitor
    6937             :     {
    6938             :         const OGRGeomCoordinatePrecision &m_precision;
    6939             :         const double m_invXYResolution;
    6940             :         const double m_invZResolution;
    6941             :         const double m_invMResolution;
    6942             : 
    6943          39 :         explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
    6944          39 :             : m_precision(sPrecisionIn),
    6945          39 :               m_invXYResolution(m_precision.dfXYResolution !=
    6946             :                                         OGRGeomCoordinatePrecision::UNKNOWN
    6947          39 :                                     ? 1.0 / m_precision.dfXYResolution
    6948             :                                     : 0.0),
    6949          39 :               m_invZResolution(m_precision.dfZResolution !=
    6950             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6951          39 :                                    ? 1.0 / m_precision.dfZResolution
    6952             :                                    : 0.0),
    6953          39 :               m_invMResolution(m_precision.dfMResolution !=
    6954             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6955          39 :                                    ? 1.0 / m_precision.dfMResolution
    6956         117 :                                    : 0.0)
    6957             :         {
    6958          39 :         }
    6959             : 
    6960             :         using OGRDefaultGeometryVisitor::visit;
    6961             : 
    6962         379 :         void visit(OGRPoint *poPoint) override
    6963             :         {
    6964         379 :             if (m_precision.dfXYResolution !=
    6965             :                 OGRGeomCoordinatePrecision::UNKNOWN)
    6966             :             {
    6967         379 :                 poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
    6968         379 :                               m_precision.dfXYResolution);
    6969         379 :                 poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
    6970         379 :                               m_precision.dfXYResolution);
    6971             :             }
    6972         758 :             if (m_precision.dfZResolution !=
    6973         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6974           4 :                 poPoint->Is3D())
    6975             :             {
    6976           4 :                 poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
    6977           4 :                               m_precision.dfZResolution);
    6978             :             }
    6979         758 :             if (m_precision.dfMResolution !=
    6980         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6981           4 :                 poPoint->IsMeasured())
    6982             :             {
    6983           4 :                 poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
    6984           4 :                               m_precision.dfMResolution);
    6985             :             }
    6986         379 :         }
    6987             :     };
    6988             : 
    6989          78 :     Rounder rounder(sPrecision);
    6990          39 :     accept(&rounder);
    6991          39 : }
    6992             : 
    6993             : /************************************************************************/
    6994             : /*                            SetPrecision()                            */
    6995             : /************************************************************************/
    6996             : 
    6997             : /** Set the geometry's precision, rounding all its coordinates to the precision
    6998             :  * grid, and making sure the geometry is still valid.
    6999             :  *
    7000             :  * This is a stronger version of roundCoordinates().
    7001             :  *
    7002             :  * Note that at time of writing GEOS does no supported curve geometries. So
    7003             :  * currently if this function is called on such a geometry, OGR will first call
    7004             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    7005             :  * that it is unlikely to yield to the expected result.
    7006             :  *
    7007             :  * This function is the same as the C function OGR_G_SetPrecision().
    7008             :  *
    7009             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    7010             :  * GEOS library. Check it for the definition of the geometry operation.
    7011             :  * If OGR is built without the GEOS library, this function will always fail,
    7012             :  * issuing a CPLE_NotSupported error.
    7013             :  *
    7014             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    7015             :  *                 precision.
    7016             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    7017             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    7018             :  *
    7019             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7020             :  *
    7021             :  * @since GDAL 3.9
    7022             :  */
    7023             : 
    7024           6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
    7025             : {
    7026             :     (void)dfGridSize;
    7027             :     (void)nFlags;
    7028             : #ifndef HAVE_GEOS
    7029             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7030             :     return nullptr;
    7031             : 
    7032             : #else
    7033           6 :     OGRGeometry *poOGRProduct = nullptr;
    7034             : 
    7035           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7036           6 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7037           6 :     if (hThisGeosGeom != nullptr)
    7038             :     {
    7039           6 :         GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
    7040             :             hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
    7041           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7042             :         poOGRProduct =
    7043           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7044             :     }
    7045           6 :     freeGEOSContext(hGEOSCtxt);
    7046           6 :     return poOGRProduct;
    7047             : 
    7048             : #endif  // HAVE_GEOS
    7049             : }
    7050             : 
    7051             : /************************************************************************/
    7052             : /*                         OGR_G_SetPrecision()                         */
    7053             : /************************************************************************/
    7054             : 
    7055             : /** Set the geometry's precision, rounding all its coordinates to the precision
    7056             :  * grid, and making sure the geometry is still valid.
    7057             :  *
    7058             :  * This is a stronger version of roundCoordinates().
    7059             :  *
    7060             :  * Note that at time of writing GEOS does no supported curve geometries. So
    7061             :  * currently if this function is called on such a geometry, OGR will first call
    7062             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    7063             :  * that it is unlikely to yield to the expected result.
    7064             :  *
    7065             :  * This function is the same as the C++ method OGRGeometry::SetPrecision().
    7066             :  *
    7067             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    7068             :  * GEOS library. Check it for the definition of the geometry operation.
    7069             :  * If OGR is built without the GEOS library, this function will always fail,
    7070             :  * issuing a CPLE_NotSupported error.
    7071             :  *
    7072             :  * @param hThis the geometry.
    7073             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    7074             :  *                 precision.
    7075             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    7076             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    7077             :  *
    7078             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7079             :  * or NULL if an error occurs.
    7080             :  *
    7081             :  * @since GDAL 3.9
    7082             :  */
    7083           1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
    7084             :                                 int nFlags)
    7085             : {
    7086           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
    7087           1 :     return OGRGeometry::ToHandle(
    7088           1 :         OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
    7089             : }
    7090             : 
    7091             : /************************************************************************/
    7092             : /*                       DelaunayTriangulation()                        */
    7093             : /************************************************************************/
    7094             : 
    7095             : /**
    7096             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    7097             :  *
    7098             :  * This function is the same as the C function OGR_G_DelaunayTriangulation().
    7099             :  *
    7100             :  * This function is built on the GEOS library, v3.4 or above.
    7101             :  * If OGR is built without the GEOS library, this function will always fail,
    7102             :  * issuing a CPLE_NotSupported error.
    7103             :  *
    7104             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    7105             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    7106             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    7107             :  *
    7108             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7109             :  */
    7110             : 
    7111             : #ifndef HAVE_GEOS
    7112             : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
    7113             :                                                 int /*bOnlyEdges*/) const
    7114             : {
    7115             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7116             :     return nullptr;
    7117             : }
    7118             : #else
    7119           1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
    7120             :                                                 int bOnlyEdges) const
    7121             : {
    7122           1 :     OGRGeometry *poOGRProduct = nullptr;
    7123             : 
    7124           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7125           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7126           1 :     if (hThisGeosGeom != nullptr)
    7127             :     {
    7128           1 :         GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
    7129             :             hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
    7130           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7131             :         poOGRProduct =
    7132           1 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7133             :     }
    7134           1 :     freeGEOSContext(hGEOSCtxt);
    7135           1 :     return poOGRProduct;
    7136             : }
    7137             : #endif
    7138             : 
    7139             : /************************************************************************/
    7140             : /*                    OGR_G_DelaunayTriangulation()                     */
    7141             : /************************************************************************/
    7142             : 
    7143             : /**
    7144             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    7145             :  *
    7146             :  * This function is the same as the C++ method
    7147             :  * OGRGeometry::DelaunayTriangulation().
    7148             :  *
    7149             :  * This function is built on the GEOS library, v3.4 or above.
    7150             :  * If OGR is built without the GEOS library, this function will always fail,
    7151             :  * issuing a CPLE_NotSupported error.
    7152             :  *
    7153             :  * @param hThis the geometry.
    7154             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    7155             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    7156             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    7157             :  *
    7158             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7159             :  * or NULL if an error occurs.
    7160             :  */
    7161             : 
    7162           1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
    7163             :                                          int bOnlyEdges)
    7164             : 
    7165             : {
    7166           1 :     VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
    7167             : 
    7168           1 :     return OGRGeometry::ToHandle(
    7169             :         OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
    7170           1 :                                                               bOnlyEdges));
    7171             : }
    7172             : 
    7173             : /************************************************************************/
    7174             : /*                  ConstrainedDelaunayTriangulation()                  */
    7175             : /************************************************************************/
    7176             : 
    7177             : /**
    7178             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7179             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7180             :  * geometry collection.
    7181             :  *
    7182             :  * This function is the same as the C function
    7183             :  * OGR_G_ConstrainedDelaunayTriangulation().
    7184             :  *
    7185             :  * This function is built on the GEOS library, v3.10 or above.
    7186             :  * If OGR is built without the GEOS library, this function will always fail,
    7187             :  * issuing a CPLE_NotSupported error.
    7188             :  *
    7189             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7190             :  *
    7191             :  * @since OGR 3.12
    7192             :  */
    7193             : 
    7194           3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
    7195             : {
    7196             : #ifndef HAVE_GEOS
    7197             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7198             :     return nullptr;
    7199             : #elif !(GEOS_VERSION_MAJOR > 3 ||                                              \
    7200             :         (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    7201             :     CPLError(
    7202             :         CE_Failure, CPLE_NotSupported,
    7203             :         "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
    7204             :     return nullptr;
    7205             : #else
    7206             : 
    7207           3 :     OGRGeometry *poOGRProduct = nullptr;
    7208             : 
    7209           3 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7210           3 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7211           3 :     if (hThisGeosGeom != nullptr)
    7212             :     {
    7213             :         GEOSGeom hGeosProduct =
    7214           3 :             GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
    7215           3 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7216             :         poOGRProduct =
    7217           3 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7218             :     }
    7219           3 :     freeGEOSContext(hGEOSCtxt);
    7220           3 :     return poOGRProduct;
    7221             : #endif
    7222             : }
    7223             : 
    7224             : /************************************************************************/
    7225             : /*               OGR_G_ConstrainedDelaunayTriangulation()               */
    7226             : /************************************************************************/
    7227             : 
    7228             : /**
    7229             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7230             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7231             :  * geometry collection.
    7232             :  *
    7233             :  * This function is the same as the C++ method
    7234             :  * OGRGeometry::ConstrainedDelaunayTriangulation().
    7235             :  *
    7236             :  * This function is built on the GEOS library, v3.10 or above.
    7237             :  * If OGR is built without the GEOS library, this function will always fail,
    7238             :  * issuing a CPLE_NotSupported error.
    7239             :  *
    7240             :  * @param hThis the geometry.
    7241             :  *
    7242             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7243             :  * or NULL if an error occurs.
    7244             :  *
    7245             :  * @since OGR 3.12
    7246             :  */
    7247             : 
    7248           3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
    7249             : {
    7250           3 :     VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
    7251             : 
    7252           3 :     return OGRGeometry::ToHandle(
    7253           3 :         OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
    7254             : }
    7255             : 
    7256             : /************************************************************************/
    7257             : /*                             Polygonize()                             */
    7258             : /************************************************************************/
    7259             : /* Contributor: Alessandro Furieri, a.furieri@lqt.it                    */
    7260             : /* Developed for Faunalia (http://www.faunalia.it) with funding from    */
    7261             : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED        */
    7262             : /*                   AMBIENTALE                                         */
    7263             : /************************************************************************/
    7264             : 
    7265             : /**
    7266             :  * \brief Polygonizes a set of sparse edges.
    7267             :  *
    7268             :  * A new geometry object is created and returned containing a collection
    7269             :  * of reassembled Polygons: NULL will be returned if the input collection
    7270             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7271             :  * into Polygons is impossible due to topological inconsistencies.
    7272             :  *
    7273             :  * This method is the same as the C function OGR_G_Polygonize().
    7274             :  *
    7275             :  * This method is built on the GEOS library, check it for the definition
    7276             :  * of the geometry operation.
    7277             :  * If OGR is built without the GEOS library, this method will always fail,
    7278             :  * issuing a CPLE_NotSupported error.
    7279             :  *
    7280             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7281             :  *
    7282             :  */
    7283             : 
    7284         117 : OGRGeometry *OGRGeometry::Polygonize() const
    7285             : 
    7286             : {
    7287             : #ifndef HAVE_GEOS
    7288             : 
    7289             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7290             :     return nullptr;
    7291             : 
    7292             : #else
    7293             : 
    7294         117 :     const OGRGeometryCollection *poColl = nullptr;
    7295         233 :     if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
    7296         116 :         wkbFlatten(getGeometryType()) == wkbMultiLineString)
    7297         116 :         poColl = toGeometryCollection();
    7298             :     else
    7299           1 :         return nullptr;
    7300             : 
    7301         116 :     const int nCount = poColl->getNumGeometries();
    7302             : 
    7303         116 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7304         116 :     bool bError = false;
    7305             : 
    7306         116 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7307             : 
    7308         116 :     GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
    7309         747 :     for (int ig = 0; ig < nCount; ig++)
    7310             :     {
    7311         631 :         GEOSGeom hGeosGeom = nullptr;
    7312         631 :         const OGRGeometry *poChild = poColl->getGeometryRef(ig);
    7313        1262 :         if (poChild == nullptr ||
    7314         631 :             wkbFlatten(poChild->getGeometryType()) != wkbLineString)
    7315           1 :             bError = true;
    7316             :         else
    7317             :         {
    7318         630 :             hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
    7319         630 :             if (hGeosGeom == nullptr)
    7320           0 :                 bError = true;
    7321             :         }
    7322         631 :         pahGeosGeomList[ig] = hGeosGeom;
    7323             :     }
    7324             : 
    7325         116 :     if (!bError)
    7326             :     {
    7327             :         GEOSGeom hGeosPolygs =
    7328         115 :             GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
    7329             : 
    7330             :         poPolygsOGRGeom =
    7331         115 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7332             :     }
    7333             : 
    7334         747 :     for (int ig = 0; ig < nCount; ig++)
    7335             :     {
    7336         631 :         GEOSGeom hGeosGeom = pahGeosGeomList[ig];
    7337         631 :         if (hGeosGeom != nullptr)
    7338         630 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    7339             :     }
    7340         116 :     delete[] pahGeosGeomList;
    7341         116 :     freeGEOSContext(hGEOSCtxt);
    7342             : 
    7343         116 :     return poPolygsOGRGeom;
    7344             : 
    7345             : #endif  // HAVE_GEOS
    7346             : }
    7347             : 
    7348             : /************************************************************************/
    7349             : /*                          OGR_G_Polygonize()                          */
    7350             : /************************************************************************/
    7351             : /**
    7352             :  * \brief Polygonizes a set of sparse edges.
    7353             :  *
    7354             :  * A new geometry object is created and returned containing a collection
    7355             :  * of reassembled Polygons: NULL will be returned if the input collection
    7356             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7357             :  * into Polygons is impossible due to topological inconsistencies.
    7358             :  *
    7359             :  * This function is the same as the C++ method OGRGeometry::Polygonize().
    7360             :  *
    7361             :  * This function is built on the GEOS library, check it for the definition
    7362             :  * of the geometry operation.
    7363             :  * If OGR is built without the GEOS library, this function will always fail,
    7364             :  * issuing a CPLE_NotSupported error.
    7365             :  *
    7366             :  * @param hTarget The Geometry to be polygonized.
    7367             :  *
    7368             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7369             :  * or NULL if an error occurs.
    7370             :  *
    7371             :  */
    7372             : 
    7373           3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
    7374             : 
    7375             : {
    7376           3 :     VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
    7377             : 
    7378           3 :     return OGRGeometry::ToHandle(
    7379           3 :         OGRGeometry::FromHandle(hTarget)->Polygonize());
    7380             : }
    7381             : 
    7382             : /************************************************************************/
    7383             : /*                             BuildArea()                              */
    7384             : /************************************************************************/
    7385             : 
    7386             : /**
    7387             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7388             :  *
    7389             :  * This method is the same as the C function OGR_G_BuildArea().
    7390             :  *
    7391             :  * Polygonization is performed similarly to OGRGeometry::Polygonize().
    7392             :  * Additionally, holes are dropped and the result is unified producing
    7393             :  * a single Polygon or a MultiPolygon.
    7394             :  *
    7395             :  * A new geometry object is created and returned: NULL on failure,
    7396             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7397             :  * Polygon or MultiPolygon on success.
    7398             :  *
    7399             :  * This method is built on the GEOSBuildArea_r() function of the GEOS
    7400             :  * library, check it for the definition of the geometry operation.
    7401             :  * If OGR is built without the GEOS library, this method will always fail,
    7402             :  * issuing a CPLE_NotSupported error.
    7403             :  *
    7404             :  * @return a newly allocated geometry now owned by the caller,
    7405             :  *         or NULL on failure.
    7406             :  *
    7407             :  * @since OGR 3.11
    7408             :  */
    7409             : 
    7410          30 : OGRGeometry *OGRGeometry::BuildArea() const
    7411             : 
    7412             : {
    7413             : #ifndef HAVE_GEOS
    7414             : 
    7415             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7416             :     return nullptr;
    7417             : 
    7418             : #else
    7419             : 
    7420          30 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7421             : 
    7422          30 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7423          30 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7424          30 :     if (hThisGeosGeom != nullptr)
    7425             :     {
    7426          30 :         GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
    7427             :         poPolygsOGRGeom =
    7428          30 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7429          30 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7430             :     }
    7431          30 :     freeGEOSContext(hGEOSCtxt);
    7432             : 
    7433          30 :     return poPolygsOGRGeom;
    7434             : 
    7435             : #endif  // HAVE_GEOS
    7436             : }
    7437             : 
    7438             : /************************************************************************/
    7439             : /*                          OGR_G_BuildArea()                           */
    7440             : /************************************************************************/
    7441             : 
    7442             : /**
    7443             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7444             :  *
    7445             :  * This function is the same as the C++ method OGRGeometry::BuildArea().
    7446             :  *
    7447             :  * Polygonization is performed similarly to OGR_G_Polygonize().
    7448             :  * Additionally, holes are dropped and the result is unified producing
    7449             :  * a single Polygon or a MultiPolygon.
    7450             :  *
    7451             :  * A new geometry object is created and returned: NULL on failure,
    7452             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7453             :  * Polygon or MultiPolygon on success.
    7454             :  *
    7455             :  * This function is built on the GEOSBuildArea_r() function of the GEOS
    7456             :  * library, check it for the definition of the geometry operation.
    7457             :  * If OGR is built without the GEOS library, this function will always fail,
    7458             :  * issuing a CPLE_NotSupported error.
    7459             :  *
    7460             :  * @param hGeom handle on the geometry to polygonize.
    7461             :  *
    7462             :  * @return a handle on newly allocated geometry now owned by the caller,
    7463             :  *         or NULL on failure.
    7464             :  *
    7465             :  * @since OGR 3.11
    7466             :  */
    7467             : 
    7468           0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
    7469             : 
    7470             : {
    7471           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
    7472             : 
    7473           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
    7474             : }
    7475             : 
    7476             : /************************************************************************/
    7477             : /*                               swapXY()                               */
    7478             : /************************************************************************/
    7479             : 
    7480             : /**
    7481             :  * \brief Swap x and y coordinates.
    7482             :  *
    7483             :  */
    7484             : 
    7485           0 : void OGRGeometry::swapXY()
    7486             : 
    7487             : {
    7488           0 : }
    7489             : 
    7490             : /************************************************************************/
    7491             : /*                               swapXY()                               */
    7492             : /************************************************************************/
    7493             : 
    7494             : /**
    7495             :  * \brief Swap x and y coordinates.
    7496             :  *
    7497             :  * @param hGeom geometry.
    7498             :  */
    7499             : 
    7500          32 : void OGR_G_SwapXY(OGRGeometryH hGeom)
    7501             : {
    7502          32 :     VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
    7503             : 
    7504          32 :     OGRGeometry::FromHandle(hGeom)->swapXY();
    7505             : }
    7506             : 
    7507             : /************************************************************************/
    7508             : /*                        Prepared geometry API                         */
    7509             : /************************************************************************/
    7510             : 
    7511             : #if defined(HAVE_GEOS)
    7512             : struct _OGRPreparedGeometry
    7513             : {
    7514             :     GEOSContextHandle_t hGEOSCtxt;
    7515             :     GEOSGeom hGEOSGeom;
    7516             :     const GEOSPreparedGeometry *poPreparedGEOSGeom;
    7517             : };
    7518             : #endif
    7519             : 
    7520             : /************************************************************************/
    7521             : /*                   OGRHasPreparedGeometrySupport()                    */
    7522             : /************************************************************************/
    7523             : 
    7524             : /** Returns if GEOS has prepared geometry support.
    7525             :  * @return TRUE or FALSE
    7526             :  */
    7527           1 : int OGRHasPreparedGeometrySupport()
    7528             : {
    7529             : #if defined(HAVE_GEOS)
    7530           1 :     return TRUE;
    7531             : #else
    7532             :     return FALSE;
    7533             : #endif
    7534             : }
    7535             : 
    7536             : /************************************************************************/
    7537             : /*                     OGRCreatePreparedGeometry()                      */
    7538             : /************************************************************************/
    7539             : 
    7540             : /** Creates a prepared geometry.
    7541             :  *
    7542             :  * To free with OGRDestroyPreparedGeometry()
    7543             :  *
    7544             :  * @param hGeom input geometry to prepare.
    7545             :  * @return handle to a prepared geometry.
    7546             :  * @since GDAL 3.3
    7547             :  */
    7548       53068 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
    7549             : {
    7550             :     (void)hGeom;
    7551             : #if defined(HAVE_GEOS)
    7552       53068 :     OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
    7553       53068 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    7554       53068 :     GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
    7555       53068 :     if (hGEOSGeom == nullptr)
    7556             :     {
    7557           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7558           0 :         return nullptr;
    7559             :     }
    7560             :     const GEOSPreparedGeometry *poPreparedGEOSGeom =
    7561       53068 :         GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
    7562       53068 :     if (poPreparedGEOSGeom == nullptr)
    7563             :     {
    7564           0 :         GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
    7565           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7566           0 :         return nullptr;
    7567             :     }
    7568             : 
    7569       53068 :     OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
    7570       53068 :     poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
    7571       53068 :     poPreparedGeom->hGEOSGeom = hGEOSGeom;
    7572       53068 :     poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
    7573             : 
    7574       53068 :     return poPreparedGeom;
    7575             : #else
    7576             :     return nullptr;
    7577             : #endif
    7578             : }
    7579             : 
    7580             : /************************************************************************/
    7581             : /*                     OGRDestroyPreparedGeometry()                     */
    7582             : /************************************************************************/
    7583             : 
    7584             : /** Destroys a prepared geometry.
    7585             :  * @param hPreparedGeom prepared geometry.
    7586             :  * @since GDAL 3.3
    7587             :  */
    7588       53114 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
    7589             : {
    7590             :     (void)hPreparedGeom;
    7591             : #if defined(HAVE_GEOS)
    7592       53114 :     if (hPreparedGeom != nullptr)
    7593             :     {
    7594       53068 :         GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
    7595             :                                    hPreparedGeom->poPreparedGEOSGeom);
    7596       53068 :         GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
    7597       53068 :         OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
    7598       53068 :         delete hPreparedGeom;
    7599             :     }
    7600             : #endif
    7601       53114 : }
    7602             : 
    7603             : /************************************************************************/
    7604             : /*                   OGRPreparedGeometryIntersects()                    */
    7605             : /************************************************************************/
    7606             : 
    7607             : /** Returns whether a prepared geometry intersects with a geometry.
    7608             :  * @param hPreparedGeom prepared geometry.
    7609             :  * @param hOtherGeom other geometry.
    7610             :  * @return TRUE or FALSE.
    7611             :  * @since GDAL 3.3
    7612             :  */
    7613        5604 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
    7614             :                                   const OGRGeometryH hOtherGeom)
    7615             : {
    7616             :     (void)hPreparedGeom;
    7617             :     (void)hOtherGeom;
    7618             : #if defined(HAVE_GEOS)
    7619        5604 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7620        5604 :     if (hPreparedGeom == nullptr ||
    7621             :         poOtherGeom == nullptr
    7622             :         // The check for IsEmpty() is for buggy GEOS versions.
    7623             :         // See https://github.com/libgeos/geos/pull/423
    7624       11208 :         || poOtherGeom->IsEmpty())
    7625             :     {
    7626           1 :         return FALSE;
    7627             :     }
    7628             : 
    7629             :     GEOSGeom hGEOSOtherGeom =
    7630        5603 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7631        5603 :     if (hGEOSOtherGeom == nullptr)
    7632           0 :         return FALSE;
    7633             : 
    7634             :     const bool bRet =
    7635        5603 :         GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
    7636             :                                  hPreparedGeom->poPreparedGEOSGeom,
    7637        5603 :                                  hGEOSOtherGeom) == 1;
    7638        5603 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7639             : 
    7640        5603 :     return bRet;
    7641             : #else
    7642             :     return FALSE;
    7643             : #endif
    7644             : }
    7645             : 
    7646             : /** Returns whether a prepared geometry contains a geometry.
    7647             :  * @param hPreparedGeom prepared geometry.
    7648             :  * @param hOtherGeom other geometry.
    7649             :  * @return TRUE or FALSE.
    7650             :  */
    7651      120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
    7652             :                                 const OGRGeometryH hOtherGeom)
    7653             : {
    7654             :     (void)hPreparedGeom;
    7655             :     (void)hOtherGeom;
    7656             : #if defined(HAVE_GEOS)
    7657      120516 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7658      120516 :     if (hPreparedGeom == nullptr ||
    7659             :         poOtherGeom == nullptr
    7660             :         // The check for IsEmpty() is for buggy GEOS versions.
    7661             :         // See https://github.com/libgeos/geos/pull/423
    7662      241032 :         || poOtherGeom->IsEmpty())
    7663             :     {
    7664           1 :         return FALSE;
    7665             :     }
    7666             : 
    7667             :     GEOSGeom hGEOSOtherGeom =
    7668      120515 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7669      120515 :     if (hGEOSOtherGeom == nullptr)
    7670           0 :         return FALSE;
    7671             : 
    7672      120515 :     const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
    7673             :                                              hPreparedGeom->poPreparedGEOSGeom,
    7674      120515 :                                              hGEOSOtherGeom) == 1;
    7675      120515 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7676             : 
    7677      120515 :     return bRet;
    7678             : #else
    7679             :     return FALSE;
    7680             : #endif
    7681             : }
    7682             : 
    7683             : /************************************************************************/
    7684             : /*                        OGRGeometryFromEWKB()                         */
    7685             : /************************************************************************/
    7686             : 
    7687        1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
    7688             :                                  int bIsPostGIS1_EWKB)
    7689             : 
    7690             : {
    7691        1445 :     OGRGeometry *poGeometry = nullptr;
    7692             : 
    7693        1445 :     size_t nWKBSize = 0;
    7694        1445 :     const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
    7695        1445 :     if (pabyWKB == nullptr)
    7696           0 :         return nullptr;
    7697             : 
    7698             :     /* -------------------------------------------------------------------- */
    7699             :     /*      Try to ingest the geometry.                                     */
    7700             :     /* -------------------------------------------------------------------- */
    7701        1445 :     (void)OGRGeometryFactory::createFromWkb(
    7702             :         pabyWKB, nullptr, &poGeometry, nWKBSize,
    7703             :         (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
    7704             : 
    7705        1445 :     return poGeometry;
    7706             : }
    7707             : 
    7708             : /************************************************************************/
    7709             : /*                       OGRGeometryFromHexEWKB()                       */
    7710             : /************************************************************************/
    7711             : 
    7712        1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
    7713             :                                     int bIsPostGIS1_EWKB)
    7714             : 
    7715             : {
    7716        1443 :     if (pszBytea == nullptr)
    7717           0 :         return nullptr;
    7718             : 
    7719        1443 :     int nWKBLength = 0;
    7720        1443 :     GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
    7721             : 
    7722             :     OGRGeometry *poGeometry =
    7723        1443 :         OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
    7724             : 
    7725        1443 :     CPLFree(pabyWKB);
    7726             : 
    7727        1443 :     return poGeometry;
    7728             : }
    7729             : 
    7730             : /************************************************************************/
    7731             : /*                        OGRGeometryToHexEWKB()                        */
    7732             : /************************************************************************/
    7733             : 
    7734        1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
    7735             :                            int nPostGISMajor, int nPostGISMinor)
    7736             : {
    7737        1071 :     const size_t nWkbSize = poGeometry->WkbSize();
    7738        1071 :     GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
    7739        1071 :     if (pabyWKB == nullptr)
    7740           0 :         return CPLStrdup("");
    7741             : 
    7742         118 :     if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
    7743        1815 :         wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
    7744         626 :         poGeometry->IsEmpty())
    7745             :     {
    7746           2 :         if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
    7747             :             OGRERR_NONE)
    7748             :         {
    7749           0 :             CPLFree(pabyWKB);
    7750           0 :             return CPLStrdup("");
    7751             :         }
    7752             :     }
    7753        1069 :     else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
    7754             :                                      (nPostGISMajor < 2)
    7755             :                                          ? wkbVariantPostGIS1
    7756        1069 :                                          : wkbVariantOldOgc) != OGRERR_NONE)
    7757             :     {
    7758           0 :         CPLFree(pabyWKB);
    7759           0 :         return CPLStrdup("");
    7760             :     }
    7761             : 
    7762             :     // When converting to hex, each byte takes 2 hex characters.  In addition
    7763             :     // we add in 8 characters to represent the SRID integer in hex, and
    7764             :     // one for a null terminator.
    7765             :     // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
    7766             :     // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
    7767             :     // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
    7768        1071 :     if (nWkbSize >
    7769        1071 :         static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
    7770             :     {
    7771           0 :         CPLFree(pabyWKB);
    7772           0 :         return CPLStrdup("");
    7773             :     }
    7774        1071 :     const size_t nTextSize = nWkbSize * 2 + 8 + 1;
    7775        1071 :     char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
    7776        1071 :     if (pszTextBuf == nullptr)
    7777             :     {
    7778           0 :         CPLFree(pabyWKB);
    7779           0 :         return CPLStrdup("");
    7780             :     }
    7781        1071 :     char *pszTextBufCurrent = pszTextBuf;
    7782             : 
    7783             :     // Convert the 1st byte, which is the endianness flag, to hex.
    7784        1071 :     char *pszHex = CPLBinaryToHex(1, pabyWKB);
    7785        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7786        1071 :     CPLFree(pszHex);
    7787        1071 :     pszTextBufCurrent += 2;
    7788             : 
    7789             :     // Next, get the geom type which is bytes 2 through 5.
    7790             :     GUInt32 geomType;
    7791        1071 :     memcpy(&geomType, pabyWKB + 1, 4);
    7792             : 
    7793             :     // Now add the SRID flag if an SRID is provided.
    7794        1071 :     if (nSRSId > 0)
    7795             :     {
    7796             :         // Change the flag to wkbNDR (little) endianness.
    7797         541 :         constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
    7798         541 :         GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
    7799             :         // Apply the flag.
    7800         541 :         geomType = geomType | nGSrsFlag;
    7801             :     }
    7802             : 
    7803             :     // Now write the geom type which is 4 bytes.
    7804        1071 :     pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
    7805        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7806        1071 :     CPLFree(pszHex);
    7807        1071 :     pszTextBufCurrent += 8;
    7808             : 
    7809             :     // Now include SRID if provided.
    7810        1071 :     if (nSRSId > 0)
    7811             :     {
    7812             :         // Force the srsid to wkbNDR (little) endianness.
    7813         541 :         const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
    7814         541 :         pszHex = CPLBinaryToHex(sizeof(nGSRSId),
    7815             :                                 reinterpret_cast<const GByte *>(&nGSRSId));
    7816         541 :         strcpy(pszTextBufCurrent, pszHex);
    7817         541 :         CPLFree(pszHex);
    7818         541 :         pszTextBufCurrent += 8;
    7819             :     }
    7820             : 
    7821             :     // Copy the rest of the data over - subtract
    7822             :     // 5 since we already copied 5 bytes above.
    7823        1071 :     pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
    7824        1071 :     CPLFree(pabyWKB);
    7825        1071 :     if (!pszHex || pszHex[0] == 0)
    7826             :     {
    7827           0 :         CPLFree(pszTextBuf);
    7828           0 :         return pszHex;
    7829             :     }
    7830        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7831        1071 :     CPLFree(pszHex);
    7832             : 
    7833        1071 :     return pszTextBuf;
    7834             : }
    7835             : 
    7836             : /************************************************************************/
    7837             : /*                       importPreambleFromWkb()                        */
    7838             : /************************************************************************/
    7839             : 
    7840             : //! @cond Doxygen_Suppress
    7841      160168 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
    7842             :                                           size_t nSize,
    7843             :                                           OGRwkbByteOrder &eByteOrder,
    7844             :                                           OGRwkbVariant eWkbVariant)
    7845             : {
    7846      160168 :     if (nSize < 9 && nSize != static_cast<size_t>(-1))
    7847           0 :         return OGRERR_NOT_ENOUGH_DATA;
    7848             : 
    7849             :     /* -------------------------------------------------------------------- */
    7850             :     /*      Get the byte order byte.                                        */
    7851             :     /* -------------------------------------------------------------------- */
    7852      160168 :     int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
    7853      160168 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    7854           0 :         return OGRERR_CORRUPT_DATA;
    7855      160168 :     eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    7856             : 
    7857             :     /* -------------------------------------------------------------------- */
    7858             :     /*      Get the geometry feature type.                                  */
    7859             :     /* -------------------------------------------------------------------- */
    7860             :     OGRwkbGeometryType eGeometryType;
    7861             :     const OGRErr err =
    7862      160168 :         OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
    7863      160168 :     if (wkbHasZ(eGeometryType))
    7864       62309 :         flags |= OGR_G_3D;
    7865      160168 :     if (wkbHasM(eGeometryType))
    7866       59692 :         flags |= OGR_G_MEASURED;
    7867             : 
    7868      160168 :     if (err != OGRERR_NONE || eGeometryType != getGeometryType())
    7869           0 :         return OGRERR_CORRUPT_DATA;
    7870             : 
    7871      160168 :     return OGRERR_NONE;
    7872             : }
    7873             : 
    7874             : /************************************************************************/
    7875             : /*                    importPreambleOfCollectionFromWkb()              */
    7876             : /*                                                                      */
    7877             : /*      Utility method for OGRSimpleCurve, OGRCompoundCurve,            */
    7878             : /*      OGRCurvePolygon and OGRGeometryCollection.                      */
    7879             : /************************************************************************/
    7880             : 
    7881       76787 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
    7882             :     const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
    7883             :     OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
    7884             :     OGRwkbVariant eWkbVariant)
    7885             : {
    7886       76787 :     nGeomCount = 0;
    7887             : 
    7888             :     OGRErr eErr =
    7889       76787 :         importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
    7890       76787 :     if (eErr != OGRERR_NONE)
    7891           0 :         return eErr;
    7892             : 
    7893             :     /* -------------------------------------------------------------------- */
    7894             :     /*      Clear existing Geoms.                                           */
    7895             :     /* -------------------------------------------------------------------- */
    7896       76787 :     int _flags = flags;  // flags set in importPreambleFromWkb
    7897       76787 :     empty();             // may reset flags etc.
    7898             : 
    7899             :     // restore
    7900       76787 :     if (_flags & OGR_G_3D)
    7901       59265 :         set3D(TRUE);
    7902       76787 :     if (_flags & OGR_G_MEASURED)
    7903       56768 :         setMeasured(TRUE);
    7904             : 
    7905             :     /* -------------------------------------------------------------------- */
    7906             :     /*      Get the sub-geometry count.                                     */
    7907             :     /* -------------------------------------------------------------------- */
    7908       76787 :     memcpy(&nGeomCount, pabyData + 5, 4);
    7909             : 
    7910       76787 :     if (OGR_SWAP(eByteOrder))
    7911         386 :         nGeomCount = CPL_SWAP32(nGeomCount);
    7912             : 
    7913      153440 :     if (nGeomCount < 0 ||
    7914       76653 :         static_cast<size_t>(nGeomCount) >
    7915       76653 :             std::numeric_limits<size_t>::max() / nMinSubGeomSize)
    7916             :     {
    7917         134 :         nGeomCount = 0;
    7918         134 :         return OGRERR_CORRUPT_DATA;
    7919             :     }
    7920       76653 :     const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
    7921             : 
    7922             :     // Each ring has a minimum of nMinSubGeomSize bytes.
    7923       76653 :     if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
    7924             :     {
    7925         910 :         CPLError(CE_Failure, CPLE_AppDefined,
    7926             :                  "Length of input WKB is too small");
    7927         910 :         nGeomCount = 0;
    7928         910 :         return OGRERR_NOT_ENOUGH_DATA;
    7929             :     }
    7930             : 
    7931       75743 :     nDataOffset = 9;
    7932       75743 :     if (nSize != static_cast<size_t>(-1))
    7933             :     {
    7934       75723 :         CPLAssert(nSize >= nDataOffset);
    7935       75723 :         nSize -= nDataOffset;
    7936             :     }
    7937             : 
    7938       75743 :     return OGRERR_NONE;
    7939             : }
    7940             : 
    7941             : /************************************************************************/
    7942             : /*                      importCurveCollectionFromWkt()                  */
    7943             : /*                                                                      */
    7944             : /*      Utility method for OGRCompoundCurve, OGRCurvePolygon and        */
    7945             : /*      OGRMultiCurve.                                                  */
    7946             : /************************************************************************/
    7947             : 
    7948        1464 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
    7949             :     const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
    7950             :     int bAllowCurve, int bAllowCompoundCurve,
    7951             :     OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
    7952             : 
    7953             : {
    7954        1464 :     int bHasZ = FALSE;
    7955        1464 :     int bHasM = FALSE;
    7956        1464 :     bool bIsEmpty = false;
    7957        1464 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
    7958        1464 :     flags = 0;
    7959        1464 :     if (eErr != OGRERR_NONE)
    7960          14 :         return eErr;
    7961        1450 :     if (bHasZ)
    7962         206 :         flags |= OGR_G_3D;
    7963        1450 :     if (bHasM)
    7964         132 :         flags |= OGR_G_MEASURED;
    7965        1450 :     if (bIsEmpty)
    7966         111 :         return OGRERR_NONE;
    7967             : 
    7968             :     char szToken[OGR_WKT_TOKEN_MAX];
    7969        1339 :     const char *pszInput = *ppszInput;
    7970        1339 :     eErr = OGRERR_NONE;
    7971             : 
    7972             :     // Skip first '('.
    7973        1339 :     pszInput = OGRWktReadToken(pszInput, szToken);
    7974             : 
    7975             :     /* ==================================================================== */
    7976             :     /*      Read each curve in turn.  Note that we try to reuse the same    */
    7977             :     /*      point list buffer from curve to curve to cut down on            */
    7978             :     /*      allocate/deallocate overhead.                                   */
    7979             :     /* ==================================================================== */
    7980        1339 :     OGRRawPoint *paoPoints = nullptr;
    7981        1339 :     int nMaxPoints = 0;
    7982        1339 :     double *padfZ = nullptr;
    7983             : 
    7984         656 :     do
    7985             :     {
    7986             : 
    7987             :         /* --------------------------------------------------------------------
    7988             :          */
    7989             :         /*      Get the first token, which should be the geometry type. */
    7990             :         /* --------------------------------------------------------------------
    7991             :          */
    7992        1995 :         const char *pszInputBefore = pszInput;
    7993        1995 :         pszInput = OGRWktReadToken(pszInput, szToken);
    7994             : 
    7995             :         /* --------------------------------------------------------------------
    7996             :          */
    7997             :         /*      Do the import. */
    7998             :         /* --------------------------------------------------------------------
    7999             :          */
    8000        1995 :         OGRCurve *poCurve = nullptr;
    8001        1995 :         if (EQUAL(szToken, "("))
    8002             :         {
    8003        1441 :             OGRLineString *poLine = new OGRLineString();
    8004        1441 :             poCurve = poLine;
    8005        1441 :             pszInput = pszInputBefore;
    8006        1441 :             eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
    8007             :                                                  paoPoints, nMaxPoints, padfZ);
    8008             :         }
    8009         554 :         else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
    8010             :         {
    8011          16 :             poCurve = new OGRLineString();
    8012             :         }
    8013             :         // Accept LINESTRING(), but this is an extension to the BNF, also
    8014             :         // accepted by PostGIS.
    8015         538 :         else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
    8016         523 :                  (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
    8017         523 :                   !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
    8018        1235 :                   OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
    8019         159 :                  (bAllowCompoundCurve &&
    8020         159 :                   STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
    8021             :         {
    8022         500 :             OGRGeometry *poGeom = nullptr;
    8023         500 :             pszInput = pszInputBefore;
    8024             :             eErr =
    8025         500 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
    8026         500 :             if (poGeom == nullptr)
    8027             :             {
    8028           1 :                 eErr = OGRERR_CORRUPT_DATA;
    8029             :             }
    8030             :             else
    8031             :             {
    8032         499 :                 poCurve = poGeom->toCurve();
    8033             :             }
    8034             :         }
    8035             :         else
    8036             :         {
    8037          38 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
    8038             :                      szToken);
    8039          38 :             eErr = OGRERR_CORRUPT_DATA;
    8040             :         }
    8041             : 
    8042             :         // If this has M it is an error if poGeom does not have M.
    8043        1995 :         if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
    8044           0 :             eErr = OGRERR_CORRUPT_DATA;
    8045             : 
    8046        1995 :         if (eErr == OGRERR_NONE)
    8047        1950 :             eErr = pfnAddCurveDirectly(this, poCurve);
    8048        1995 :         if (eErr != OGRERR_NONE)
    8049             :         {
    8050          55 :             delete poCurve;
    8051          55 :             break;
    8052             :         }
    8053             : 
    8054             :         /* --------------------------------------------------------------------
    8055             :          */
    8056             :         /*      Read the delimiter following the surface. */
    8057             :         /* --------------------------------------------------------------------
    8058             :          */
    8059        1940 :         pszInput = OGRWktReadToken(pszInput, szToken);
    8060        1940 :     } while (szToken[0] == ',' && eErr == OGRERR_NONE);
    8061             : 
    8062        1339 :     CPLFree(paoPoints);
    8063        1339 :     CPLFree(padfZ);
    8064             : 
    8065             :     /* -------------------------------------------------------------------- */
    8066             :     /*      freak if we don't get a closing bracket.                        */
    8067             :     /* -------------------------------------------------------------------- */
    8068             : 
    8069        1339 :     if (eErr != OGRERR_NONE)
    8070          55 :         return eErr;
    8071             : 
    8072        1284 :     if (szToken[0] != ')')
    8073           9 :         return OGRERR_CORRUPT_DATA;
    8074             : 
    8075        1275 :     *ppszInput = pszInput;
    8076        1275 :     return OGRERR_NONE;
    8077             : }
    8078             : 
    8079             : //! @endcond
    8080             : 
    8081             : /************************************************************************/
    8082             : /*                           OGR_GT_Flatten()                           */
    8083             : /************************************************************************/
    8084             : /**
    8085             :  * \brief Returns the 2D geometry type corresponding to the passed geometry
    8086             :  * type.
    8087             :  *
    8088             :  * This function is intended to work with geometry types as old-style 99-402
    8089             :  * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
    8090             :  * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
    8091             :  *
    8092             :  * @param eType Input geometry type
    8093             :  *
    8094             :  * @return 2D geometry type corresponding to the passed geometry type.
    8095             :  *
    8096             :  */
    8097             : 
    8098     8033540 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
    8099             : {
    8100     8033540 :     eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
    8101     8033540 :     if (eType >= 1000 && eType < 2000)  // ISO Z.
    8102     2776760 :         return static_cast<OGRwkbGeometryType>(eType - 1000);
    8103     5256780 :     if (eType >= 2000 && eType < 3000)  // ISO M.
    8104        6121 :         return static_cast<OGRwkbGeometryType>(eType - 2000);
    8105     5250660 :     if (eType >= 3000 && eType < 4000)  // ISO ZM.
    8106      136119 :         return static_cast<OGRwkbGeometryType>(eType - 3000);
    8107     5114540 :     return eType;
    8108             : }
    8109             : 
    8110             : /************************************************************************/
    8111             : /*                            OGR_GT_HasZ()                             */
    8112             : /************************************************************************/
    8113             : /**
    8114             :  * \brief Return if the geometry type is a 3D geometry type.
    8115             :  *
    8116             :  * @param eType Input geometry type
    8117             :  *
    8118             :  * @return TRUE if the geometry type is a 3D geometry type.
    8119             :  *
    8120             :  */
    8121             : 
    8122     2093170 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
    8123             : {
    8124     2093170 :     if (eType & wkb25DBitInternalUse)
    8125      157218 :         return TRUE;
    8126     1935950 :     if (eType >= 1000 && eType < 2000)  // Accept 1000 for wkbUnknownZ.
    8127         264 :         return TRUE;
    8128     1935690 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8129      121358 :         return TRUE;
    8130     1814330 :     return FALSE;
    8131             : }
    8132             : 
    8133             : /************************************************************************/
    8134             : /*                            OGR_GT_HasM()                             */
    8135             : /************************************************************************/
    8136             : /**
    8137             :  * \brief Return if the geometry type is a measured type.
    8138             :  *
    8139             :  * @param eType Input geometry type
    8140             :  *
    8141             :  * @return TRUE if the geometry type is a measured type.
    8142             :  *
    8143             :  */
    8144             : 
    8145     2154430 : int OGR_GT_HasM(OGRwkbGeometryType eType)
    8146             : {
    8147     2154430 :     if (eType >= 2000 && eType < 3000)  // Accept 2000 for wkbUnknownM.
    8148        2600 :         return TRUE;
    8149     2151830 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8150      121014 :         return TRUE;
    8151     2030820 :     return FALSE;
    8152             : }
    8153             : 
    8154             : /************************************************************************/
    8155             : /*                            OGR_GT_SetZ()                             */
    8156             : /************************************************************************/
    8157             : /**
    8158             :  * \brief Returns the 3D geometry type corresponding to the passed geometry
    8159             :  * type.
    8160             :  *
    8161             :  * @param eType Input geometry type
    8162             :  *
    8163             :  * @return 3D geometry type corresponding to the passed geometry type.
    8164             :  *
    8165             :  */
    8166             : 
    8167        5668 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
    8168             : {
    8169        5668 :     if (OGR_GT_HasZ(eType) || eType == wkbNone)
    8170         497 :         return eType;
    8171        5171 :     if (eType <= wkbGeometryCollection)
    8172        5069 :         return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
    8173             :     else
    8174         102 :         return static_cast<OGRwkbGeometryType>(eType + 1000);
    8175             : }
    8176             : 
    8177             : /************************************************************************/
    8178             : /*                            OGR_GT_SetM()                             */
    8179             : /************************************************************************/
    8180             : /**
    8181             :  * \brief Returns the measured geometry type corresponding to the passed
    8182             :  * geometry type.
    8183             :  *
    8184             :  * @param eType Input geometry type
    8185             :  *
    8186             :  * @return measured geometry type corresponding to the passed geometry type.
    8187             :  *
    8188             :  */
    8189             : 
    8190        2023 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
    8191             : {
    8192        2023 :     if (OGR_GT_HasM(eType) || eType == wkbNone)
    8193         262 :         return eType;
    8194        1761 :     if (eType & wkb25DBitInternalUse)
    8195             :     {
    8196         720 :         eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
    8197         720 :         eType = static_cast<OGRwkbGeometryType>(eType + 1000);
    8198             :     }
    8199        1761 :     return static_cast<OGRwkbGeometryType>(eType + 2000);
    8200             : }
    8201             : 
    8202             : /************************************************************************/
    8203             : /*                         OGR_GT_SetModifier()                         */
    8204             : /************************************************************************/
    8205             : /**
    8206             :  * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
    8207             :  *
    8208             :  * @param eType Input geometry type
    8209             :  * @param bHasZ TRUE if the output geometry type must be 3D.
    8210             :  * @param bHasM TRUE if the output geometry type must be measured.
    8211             :  *
    8212             :  * @return Output geometry type.
    8213             :  *
    8214             :  */
    8215             : 
    8216        5401 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
    8217             :                                       int bHasM)
    8218             : {
    8219        5401 :     if (bHasZ && bHasM)
    8220         342 :         return OGR_GT_SetM(OGR_GT_SetZ(eType));
    8221        5059 :     else if (bHasM)
    8222         334 :         return OGR_GT_SetM(wkbFlatten(eType));
    8223        4725 :     else if (bHasZ)
    8224        1926 :         return OGR_GT_SetZ(wkbFlatten(eType));
    8225             :     else
    8226        2799 :         return wkbFlatten(eType);
    8227             : }
    8228             : 
    8229             : /************************************************************************/
    8230             : /*                         OGR_GT_IsSubClassOf)                         */
    8231             : /************************************************************************/
    8232             : /**
    8233             :  * \brief Returns if a type is a subclass of another one
    8234             :  *
    8235             :  * @param eType Type.
    8236             :  * @param eSuperType Super type
    8237             :  *
    8238             :  * @return TRUE if eType is a subclass of eSuperType.
    8239             :  *
    8240             :  */
    8241             : 
    8242      138754 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
    8243             : {
    8244      138754 :     eSuperType = wkbFlatten(eSuperType);
    8245      138754 :     eType = wkbFlatten(eType);
    8246             : 
    8247      138754 :     if (eSuperType == eType || eSuperType == wkbUnknown)
    8248       21356 :         return TRUE;
    8249             : 
    8250      117398 :     if (eSuperType == wkbGeometryCollection)
    8251       34145 :         return eType == wkbMultiPoint || eType == wkbMultiLineString ||
    8252       70336 :                eType == wkbMultiPolygon || eType == wkbMultiCurve ||
    8253       36191 :                eType == wkbMultiSurface;
    8254             : 
    8255       81207 :     if (eSuperType == wkbCurvePolygon)
    8256       22252 :         return eType == wkbPolygon || eType == wkbTriangle;
    8257             : 
    8258       58955 :     if (eSuperType == wkbMultiCurve)
    8259         249 :         return eType == wkbMultiLineString;
    8260             : 
    8261       58706 :     if (eSuperType == wkbMultiSurface)
    8262         288 :         return eType == wkbMultiPolygon;
    8263             : 
    8264       58418 :     if (eSuperType == wkbCurve)
    8265       23329 :         return eType == wkbLineString || eType == wkbCircularString ||
    8266       23329 :                eType == wkbCompoundCurve;
    8267             : 
    8268       35089 :     if (eSuperType == wkbSurface)
    8269        3553 :         return eType == wkbCurvePolygon || eType == wkbPolygon ||
    8270        7252 :                eType == wkbTriangle || eType == wkbPolyhedralSurface ||
    8271        3699 :                eType == wkbTIN;
    8272             : 
    8273       31390 :     if (eSuperType == wkbPolygon)
    8274         221 :         return eType == wkbTriangle;
    8275             : 
    8276       31169 :     if (eSuperType == wkbPolyhedralSurface)
    8277       16061 :         return eType == wkbTIN;
    8278             : 
    8279       15108 :     return FALSE;
    8280             : }
    8281             : 
    8282             : /************************************************************************/
    8283             : /*                        OGR_GT_GetCollection()                        */
    8284             : /************************************************************************/
    8285             : /**
    8286             :  * \brief Returns the collection type that can contain the passed geometry type
    8287             :  *
    8288             :  * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
    8289             :  * wkbLineString->wkbMultiLineString,
    8290             :  * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
    8291             :  * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
    8292             :  * wkbCurvePolygon->wkbMultiSurface.
    8293             :  * In other cases, wkbUnknown is returned
    8294             :  *
    8295             :  * Passed Z, M, ZM flag is preserved.
    8296             :  *
    8297             :  *
    8298             :  * @param eType Input geometry type
    8299             :  *
    8300             :  * @return the collection type that can contain the passed geometry type or
    8301             :  * wkbUnknown
    8302             :  *
    8303             :  */
    8304             : 
    8305        8702 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
    8306             : {
    8307        8702 :     const bool bHasZ = wkbHasZ(eType);
    8308        8702 :     const bool bHasM = wkbHasM(eType);
    8309        8702 :     if (eType == wkbNone)
    8310           1 :         return wkbNone;
    8311        8701 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8312        8701 :     if (eFGType == wkbPoint)
    8313          69 :         eType = wkbMultiPoint;
    8314             : 
    8315        8632 :     else if (eFGType == wkbLineString)
    8316        1774 :         eType = wkbMultiLineString;
    8317             : 
    8318        6858 :     else if (eFGType == wkbPolygon)
    8319        5409 :         eType = wkbMultiPolygon;
    8320             : 
    8321        1449 :     else if (eFGType == wkbTriangle)
    8322           7 :         eType = wkbTIN;
    8323             : 
    8324        1442 :     else if (OGR_GT_IsCurve(eFGType))
    8325         189 :         eType = wkbMultiCurve;
    8326             : 
    8327        1253 :     else if (OGR_GT_IsSurface(eFGType))
    8328         951 :         eType = wkbMultiSurface;
    8329             : 
    8330             :     else
    8331         302 :         return wkbUnknown;
    8332             : 
    8333        8399 :     if (bHasZ)
    8334         108 :         eType = wkbSetZ(eType);
    8335        8399 :     if (bHasM)
    8336          12 :         eType = wkbSetM(eType);
    8337             : 
    8338        8399 :     return eType;
    8339             : }
    8340             : 
    8341             : /************************************************************************/
    8342             : /*                          OGR_GT_GetSingle()                          */
    8343             : /************************************************************************/
    8344             : /**
    8345             :  * \brief Returns the non-collection type that be contained in the passed
    8346             :  * geometry type.
    8347             :  *
    8348             :  * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
    8349             :  * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
    8350             :  * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
    8351             :  * wkbGeometryCollection -> wkbUnknown
    8352             :  * In other cases, the original geometry is returned.
    8353             :  *
    8354             :  * Passed Z, M, ZM flag is preserved.
    8355             :  *
    8356             :  *
    8357             :  * @param eType Input geometry type
    8358             :  *
    8359             :  * @return the the non-collection type that be contained in the passed geometry
    8360             :  * type or wkbUnknown
    8361             :  *
    8362             :  * @since GDAL 3.11
    8363             :  */
    8364             : 
    8365         473 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
    8366             : {
    8367         473 :     const bool bHasZ = wkbHasZ(eType);
    8368         473 :     const bool bHasM = wkbHasM(eType);
    8369         473 :     if (eType == wkbNone)
    8370           1 :         return wkbNone;
    8371         472 :     const OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8372         472 :     if (eFGType == wkbMultiPoint)
    8373          17 :         eType = wkbPoint;
    8374             : 
    8375         455 :     else if (eFGType == wkbMultiLineString)
    8376           6 :         eType = wkbLineString;
    8377             : 
    8378         449 :     else if (eFGType == wkbMultiPolygon)
    8379           7 :         eType = wkbPolygon;
    8380             : 
    8381         442 :     else if (eFGType == wkbMultiCurve)
    8382           2 :         eType = wkbCompoundCurve;
    8383             : 
    8384         440 :     else if (eFGType == wkbMultiSurface)
    8385           2 :         eType = wkbCurvePolygon;
    8386             : 
    8387         438 :     else if (eFGType == wkbGeometryCollection)
    8388           4 :         return wkbUnknown;
    8389             : 
    8390         468 :     if (bHasZ)
    8391           3 :         eType = wkbSetZ(eType);
    8392         468 :     if (bHasM)
    8393           2 :         eType = wkbSetM(eType);
    8394             : 
    8395         468 :     return eType;
    8396             : }
    8397             : 
    8398             : /************************************************************************/
    8399             : /*                          OGR_GT_GetCurve()                           */
    8400             : /************************************************************************/
    8401             : /**
    8402             :  * \brief Returns the curve geometry type that can contain the passed geometry
    8403             :  * type
    8404             :  *
    8405             :  * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
    8406             :  * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
    8407             :  * and wkbMultiLineString->wkbMultiCurve.
    8408             :  * In other cases, the passed geometry is returned.
    8409             :  *
    8410             :  * Passed Z, M, ZM flag is preserved.
    8411             :  *
    8412             :  * @param eType Input geometry type
    8413             :  *
    8414             :  * @return the curve type that can contain the passed geometry type
    8415             :  *
    8416             :  */
    8417             : 
    8418          32 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
    8419             : {
    8420          32 :     const bool bHasZ = wkbHasZ(eType);
    8421          32 :     const bool bHasM = wkbHasM(eType);
    8422          32 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8423             : 
    8424          32 :     if (eFGType == wkbLineString)
    8425           3 :         eType = wkbCompoundCurve;
    8426             : 
    8427          29 :     else if (eFGType == wkbPolygon)
    8428           1 :         eType = wkbCurvePolygon;
    8429             : 
    8430          28 :     else if (eFGType == wkbTriangle)
    8431           0 :         eType = wkbCurvePolygon;
    8432             : 
    8433          28 :     else if (eFGType == wkbMultiLineString)
    8434           3 :         eType = wkbMultiCurve;
    8435             : 
    8436          25 :     else if (eFGType == wkbMultiPolygon)
    8437           4 :         eType = wkbMultiSurface;
    8438             : 
    8439          32 :     if (bHasZ)
    8440           4 :         eType = wkbSetZ(eType);
    8441          32 :     if (bHasM)
    8442           4 :         eType = wkbSetM(eType);
    8443             : 
    8444          32 :     return eType;
    8445             : }
    8446             : 
    8447             : /************************************************************************/
    8448             : /*                          OGR_GT_GetLinear()                          */
    8449             : /************************************************************************/
    8450             : /**
    8451             :  * \brief Returns the non-curve geometry type that can contain the passed
    8452             :  * geometry type
    8453             :  *
    8454             :  * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
    8455             :  * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
    8456             :  * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
    8457             :  * In other cases, the passed geometry is returned.
    8458             :  *
    8459             :  * Passed Z, M, ZM flag is preserved.
    8460             :  *
    8461             :  * @param eType Input geometry type
    8462             :  *
    8463             :  * @return the non-curve type that can contain the passed geometry type
    8464             :  *
    8465             :  */
    8466             : 
    8467         786 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
    8468             : {
    8469         786 :     const bool bHasZ = wkbHasZ(eType);
    8470         786 :     const bool bHasM = wkbHasM(eType);
    8471         786 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8472             : 
    8473         786 :     if (OGR_GT_IsCurve(eFGType))
    8474          56 :         eType = wkbLineString;
    8475             : 
    8476         730 :     else if (OGR_GT_IsSurface(eFGType))
    8477          42 :         eType = wkbPolygon;
    8478             : 
    8479         688 :     else if (eFGType == wkbMultiCurve)
    8480         187 :         eType = wkbMultiLineString;
    8481             : 
    8482         501 :     else if (eFGType == wkbMultiSurface)
    8483         167 :         eType = wkbMultiPolygon;
    8484             : 
    8485         786 :     if (bHasZ)
    8486         154 :         eType = wkbSetZ(eType);
    8487         786 :     if (bHasM)
    8488         101 :         eType = wkbSetM(eType);
    8489             : 
    8490         786 :     return eType;
    8491             : }
    8492             : 
    8493             : /************************************************************************/
    8494             : /*                           OGR_GT_IsCurve()                           */
    8495             : /************************************************************************/
    8496             : 
    8497             : /**
    8498             :  * \brief Return if a geometry type is an instance of Curve
    8499             :  *
    8500             :  * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
    8501             :  * and their Z/M/ZM variant.
    8502             :  *
    8503             :  * @param eGeomType the geometry type
    8504             :  * @return TRUE if the geometry type is an instance of Curve
    8505             :  *
    8506             :  */
    8507             : 
    8508       23320 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
    8509             : {
    8510       23320 :     return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
    8511             : }
    8512             : 
    8513             : /************************************************************************/
    8514             : /*                          OGR_GT_IsSurface()                          */
    8515             : /************************************************************************/
    8516             : 
    8517             : /**
    8518             :  * \brief Return if a geometry type is an instance of Surface
    8519             :  *
    8520             :  * Such geometry type are wkbCurvePolygon and wkbPolygon
    8521             :  * and their Z/M/ZM variant.
    8522             :  *
    8523             :  * @param eGeomType the geometry type
    8524             :  * @return TRUE if the geometry type is an instance of Surface
    8525             :  *
    8526             :  */
    8527             : 
    8528        3693 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
    8529             : {
    8530        3693 :     return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
    8531             : }
    8532             : 
    8533             : /************************************************************************/
    8534             : /*                         OGR_GT_IsNonLinear()                         */
    8535             : /************************************************************************/
    8536             : 
    8537             : /**
    8538             :  * \brief Return if a geometry type is a non-linear geometry type.
    8539             :  *
    8540             :  * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
    8541             :  * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
    8542             :  * Z/M variants.
    8543             :  *
    8544             :  * @param eGeomType the geometry type
    8545             :  * @return TRUE if the geometry type is a non-linear geometry type.
    8546             :  *
    8547             :  */
    8548             : 
    8549      120233 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
    8550             : {
    8551      120233 :     OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
    8552      120225 :     return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
    8553      120152 :            eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
    8554      240458 :            eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
    8555      120233 :            eFGeomType == wkbMultiSurface;
    8556             : }
    8557             : 
    8558             : /************************************************************************/
    8559             : /*                            CastToError()                             */
    8560             : /************************************************************************/
    8561             : 
    8562             : //! @cond Doxygen_Suppress
    8563           0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
    8564             : {
    8565           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
    8566           0 :              poGeom->getGeometryName());
    8567           0 :     delete poGeom;
    8568           0 :     return nullptr;
    8569             : }
    8570             : 
    8571             : //! @endcond
    8572             : 
    8573             : /************************************************************************/
    8574             : /*                         OGRexportToSFCGAL()                          */
    8575             : /************************************************************************/
    8576             : 
    8577             : //! @cond Doxygen_Suppress
    8578             : sfcgal_geometry_t *
    8579           0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
    8580             : {
    8581             : #ifdef HAVE_SFCGAL
    8582             : 
    8583             :     sfcgal_init();
    8584             : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8585             : 
    8586             :     const auto exportToSFCGALViaWKB =
    8587             :         [](const OGRGeometry *geom) -> sfcgal_geometry_t *
    8588             :     {
    8589             :         if (!geom)
    8590             :             return nullptr;
    8591             : 
    8592             :         // Get WKB size and allocate buffer
    8593             :         size_t nSize = geom->WkbSize();
    8594             :         unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
    8595             : 
    8596             :         // Set export options with NDR byte order
    8597             :         OGRwkbExportOptions oOptions;
    8598             :         oOptions.eByteOrder = wkbNDR;
    8599             :         // and ISO to avoid wkb25DBit for Z geometries
    8600             :         oOptions.eWkbVariant = wkbVariantIso;
    8601             : 
    8602             :         // Export to WKB
    8603             :         sfcgal_geometry_t *sfcgalGeom = nullptr;
    8604             :         if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
    8605             :         {
    8606             :             sfcgalGeom = sfcgal_io_read_wkb(
    8607             :                 reinterpret_cast<const char *>(pabyWkb), nSize);
    8608             :         }
    8609             : 
    8610             :         CPLFree(pabyWkb);
    8611             :         return sfcgalGeom;
    8612             :     };
    8613             : 
    8614             :     // Handle special cases
    8615             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8616             :     {
    8617             :         std::unique_ptr<OGRLineString> poLS(
    8618             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8619             :         return exportToSFCGALViaWKB(poLS.get());
    8620             :     }
    8621             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8622             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8623             :     {
    8624             :         std::unique_ptr<OGRLineString> poLS(
    8625             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8626             :                 ->toLineString());
    8627             :         return exportToSFCGALViaWKB(poLS.get());
    8628             :     }
    8629             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8630             :     {
    8631             :         std::unique_ptr<OGRPolygon> poPolygon(
    8632             :             OGRGeometryFactory::forceToPolygon(
    8633             :                 poGeom->clone()->toCurvePolygon())
    8634             :                 ->toPolygon());
    8635             :         return exportToSFCGALViaWKB(poPolygon.get());
    8636             :     }
    8637             :     else
    8638             :     {
    8639             :         // Default case - direct export
    8640             :         return exportToSFCGALViaWKB(poGeom);
    8641             :     }
    8642             : #else
    8643             :     char *buffer = nullptr;
    8644             : 
    8645             :     // special cases - LinearRing, Circular String, Compound Curve, Curve
    8646             :     // Polygon
    8647             : 
    8648             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8649             :     {
    8650             :         // cast it to LineString and get the WKT
    8651             :         std::unique_ptr<OGRLineString> poLS(
    8652             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8653             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8654             :         {
    8655             :             sfcgal_geometry_t *_geometry =
    8656             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8657             :             CPLFree(buffer);
    8658             :             return _geometry;
    8659             :         }
    8660             :         else
    8661             :         {
    8662             :             CPLFree(buffer);
    8663             :             return nullptr;
    8664             :         }
    8665             :     }
    8666             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8667             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8668             :     {
    8669             :         // convert it to LineString and get the WKT
    8670             :         std::unique_ptr<OGRLineString> poLS(
    8671             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8672             :                 ->toLineString());
    8673             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8674             :         {
    8675             :             sfcgal_geometry_t *_geometry =
    8676             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8677             :             CPLFree(buffer);
    8678             :             return _geometry;
    8679             :         }
    8680             :         else
    8681             :         {
    8682             :             CPLFree(buffer);
    8683             :             return nullptr;
    8684             :         }
    8685             :     }
    8686             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8687             :     {
    8688             :         // convert it to Polygon and get the WKT
    8689             :         std::unique_ptr<OGRPolygon> poPolygon(
    8690             :             OGRGeometryFactory::forceToPolygon(
    8691             :                 poGeom->clone()->toCurvePolygon())
    8692             :                 ->toPolygon());
    8693             :         if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
    8694             :         {
    8695             :             sfcgal_geometry_t *_geometry =
    8696             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8697             :             CPLFree(buffer);
    8698             :             return _geometry;
    8699             :         }
    8700             :         else
    8701             :         {
    8702             :             CPLFree(buffer);
    8703             :             return nullptr;
    8704             :         }
    8705             :     }
    8706             :     else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
    8707             :     {
    8708             :         sfcgal_geometry_t *_geometry =
    8709             :             sfcgal_io_read_wkt(buffer, strlen(buffer));
    8710             :         CPLFree(buffer);
    8711             :         return _geometry;
    8712             :     }
    8713             :     else
    8714             :     {
    8715             :         CPLFree(buffer);
    8716             :         return nullptr;
    8717             :     }
    8718             : #endif
    8719             : #else
    8720           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8721           0 :     return nullptr;
    8722             : #endif
    8723             : }
    8724             : 
    8725             : //! @endcond
    8726             : 
    8727             : /************************************************************************/
    8728             : /*                         SFCGALexportToOGR()                          */
    8729             : /************************************************************************/
    8730             : 
    8731             : //! @cond Doxygen_Suppress
    8732           0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
    8733             :     UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
    8734             : {
    8735             : #ifdef HAVE_SFCGAL
    8736             :     if (geometry == nullptr)
    8737             :         return nullptr;
    8738             : 
    8739             :     sfcgal_init();
    8740             :     char *pabySFCGAL = nullptr;
    8741             :     size_t nLength = 0;
    8742             : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8743             : 
    8744             :     sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
    8745             : 
    8746             :     if (pabySFCGAL == nullptr || nLength == 0)
    8747             :         return nullptr;
    8748             : 
    8749             :     OGRGeometry *poGeom = nullptr;
    8750             :     OGRErr eErr = OGRGeometryFactory::createFromWkb(
    8751             :         reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
    8752             :         nLength);
    8753             : 
    8754             :     free(pabySFCGAL);
    8755             : 
    8756             :     if (eErr == OGRERR_NONE)
    8757             :     {
    8758             :         return poGeom;
    8759             :     }
    8760             :     else
    8761             :     {
    8762             :         return nullptr;
    8763             :     }
    8764             : #else
    8765             :     sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
    8766             :     char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
    8767             :     memcpy(pszWKT, pabySFCGAL, nLength);
    8768             :     pszWKT[nLength] = 0;
    8769             :     free(pabySFCGAL);
    8770             : 
    8771             :     sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
    8772             : 
    8773             :     OGRGeometry *poGeom = nullptr;
    8774             :     if (geom_type == SFCGAL_TYPE_POINT)
    8775             :     {
    8776             :         poGeom = new OGRPoint();
    8777             :     }
    8778             :     else if (geom_type == SFCGAL_TYPE_LINESTRING)
    8779             :     {
    8780             :         poGeom = new OGRLineString();
    8781             :     }
    8782             :     else if (geom_type == SFCGAL_TYPE_POLYGON)
    8783             :     {
    8784             :         poGeom = new OGRPolygon();
    8785             :     }
    8786             :     else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
    8787             :     {
    8788             :         poGeom = new OGRMultiPoint();
    8789             :     }
    8790             :     else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
    8791             :     {
    8792             :         poGeom = new OGRMultiLineString();
    8793             :     }
    8794             :     else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
    8795             :     {
    8796             :         poGeom = new OGRMultiPolygon();
    8797             :     }
    8798             :     else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
    8799             :     {
    8800             :         poGeom = new OGRGeometryCollection();
    8801             :     }
    8802             :     else if (geom_type == SFCGAL_TYPE_TRIANGLE)
    8803             :     {
    8804             :         poGeom = new OGRTriangle();
    8805             :     }
    8806             :     else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
    8807             :     {
    8808             :         poGeom = new OGRPolyhedralSurface();
    8809             :     }
    8810             :     else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
    8811             :     {
    8812             :         poGeom = new OGRTriangulatedSurface();
    8813             :     }
    8814             :     else
    8815             :     {
    8816             :         CPLFree(pszWKT);
    8817             :         return nullptr;
    8818             :     }
    8819             : 
    8820             :     const char *pszWKTTmp = pszWKT;
    8821             :     if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
    8822             :     {
    8823             :         CPLFree(pszWKT);
    8824             :         return poGeom;
    8825             :     }
    8826             :     else
    8827             :     {
    8828             :         delete poGeom;
    8829             :         CPLFree(pszWKT);
    8830             :         return nullptr;
    8831             :     }
    8832             : #endif
    8833             : #else
    8834           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8835           0 :     return nullptr;
    8836             : #endif
    8837             : }
    8838             : 
    8839             : //! @endcond
    8840             : 
    8841             : //! @cond Doxygen_Suppress
    8842       11066 : bool OGRGeometry::IsSFCGALCompatible() const
    8843             : {
    8844       11066 :     const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
    8845       11066 :     if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
    8846             :         eGType == wkbTIN)
    8847             :     {
    8848           2 :         return TRUE;
    8849             :     }
    8850       11064 :     if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
    8851             :     {
    8852          14 :         const OGRGeometryCollection *poGC = toGeometryCollection();
    8853          14 :         bool bIsSFCGALCompatible = false;
    8854          14 :         for (auto &&poSubGeom : *poGC)
    8855             :         {
    8856             :             OGRwkbGeometryType eSubGeomType =
    8857          14 :                 wkbFlatten(poSubGeom->getGeometryType());
    8858          14 :             if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
    8859             :             {
    8860           0 :                 bIsSFCGALCompatible = true;
    8861             :             }
    8862          14 :             else if (eSubGeomType != wkbMultiPolygon)
    8863             :             {
    8864          14 :                 bIsSFCGALCompatible = false;
    8865          14 :                 break;
    8866             :             }
    8867             :         }
    8868          14 :         return bIsSFCGALCompatible;
    8869             :     }
    8870       11050 :     return FALSE;
    8871             : }
    8872             : 
    8873             : //! @endcond
    8874             : 
    8875             : /************************************************************************/
    8876             : /*                      roundCoordinatesIEEE754()                       */
    8877             : /************************************************************************/
    8878             : 
    8879             : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
    8880             :  * double-precision binary representation.
    8881             :  *
    8882             :  * Determines the number of bits (N) required to represent a coordinate value
    8883             :  * with a specified number of digits after the decimal point, and then sets all
    8884             :  * but the N most significant bits to zero. The resulting coordinate value will
    8885             :  * still round to the original value (e.g. after roundCoordinates()), but will
    8886             :  * have improved compressiblity.
    8887             :  *
    8888             :  * @param options Contains the precision requirements.
    8889             :  * @since GDAL 3.9
    8890             :  */
    8891           1 : void OGRGeometry::roundCoordinatesIEEE754(
    8892             :     const OGRGeomCoordinateBinaryPrecision &options)
    8893             : {
    8894             :     struct Quantizer : public OGRDefaultGeometryVisitor
    8895             :     {
    8896             :         const OGRGeomCoordinateBinaryPrecision &m_options;
    8897             : 
    8898           1 :         explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
    8899           1 :             : m_options(optionsIn)
    8900             :         {
    8901           1 :         }
    8902             : 
    8903             :         using OGRDefaultGeometryVisitor::visit;
    8904             : 
    8905           3 :         void visit(OGRPoint *poPoint) override
    8906             :         {
    8907           3 :             if (m_options.nXYBitPrecision != INT_MIN)
    8908             :             {
    8909             :                 uint64_t i;
    8910             :                 double d;
    8911           3 :                 d = poPoint->getX();
    8912           3 :                 memcpy(&i, &d, sizeof(i));
    8913           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8914           3 :                 memcpy(&d, &i, sizeof(i));
    8915           3 :                 poPoint->setX(d);
    8916           3 :                 d = poPoint->getY();
    8917           3 :                 memcpy(&i, &d, sizeof(i));
    8918           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8919           3 :                 memcpy(&d, &i, sizeof(i));
    8920           3 :                 poPoint->setY(d);
    8921             :             }
    8922           3 :             if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
    8923             :             {
    8924             :                 uint64_t i;
    8925             :                 double d;
    8926           3 :                 d = poPoint->getZ();
    8927           3 :                 memcpy(&i, &d, sizeof(i));
    8928           3 :                 i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
    8929           3 :                 memcpy(&d, &i, sizeof(i));
    8930           3 :                 poPoint->setZ(d);
    8931             :             }
    8932           3 :             if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
    8933             :             {
    8934             :                 uint64_t i;
    8935             :                 double d;
    8936           3 :                 d = poPoint->getM();
    8937           3 :                 memcpy(&i, &d, sizeof(i));
    8938           3 :                 i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
    8939           3 :                 memcpy(&d, &i, sizeof(i));
    8940           3 :                 poPoint->setM(d);
    8941             :             }
    8942           3 :         }
    8943             :     };
    8944             : 
    8945           2 :     Quantizer quantizer(options);
    8946           1 :     accept(&quantizer);
    8947           1 : }
    8948             : 
    8949             : /************************************************************************/
    8950             : /*                               visit()                                */
    8951             : /************************************************************************/
    8952             : 
    8953         105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
    8954             : {
    8955        1248 :     for (auto &&oPoint : *poGeom)
    8956             :     {
    8957        1143 :         oPoint.accept(this);
    8958             :     }
    8959         105 : }
    8960             : 
    8961         104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
    8962             : {
    8963         104 :     _visit(poGeom);
    8964         104 : }
    8965             : 
    8966          80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
    8967             : {
    8968          80 :     visit(poGeom->toUpperClass());
    8969          80 : }
    8970             : 
    8971           1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
    8972             : {
    8973           1 :     _visit(poGeom);
    8974           1 : }
    8975             : 
    8976          78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
    8977             : {
    8978         159 :     for (auto &&poSubGeom : *poGeom)
    8979          81 :         poSubGeom->accept(this);
    8980          78 : }
    8981             : 
    8982          77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
    8983             : {
    8984          77 :     visit(poGeom->toUpperClass());
    8985          77 : }
    8986             : 
    8987           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
    8988             : {
    8989           1 :     visit(poGeom->toUpperClass());
    8990           1 : }
    8991             : 
    8992           8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
    8993             : {
    8994           8 :     visit(poGeom->toUpperClass());
    8995           8 : }
    8996             : 
    8997          44 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
    8998             : {
    8999          44 :     visit(poGeom->toUpperClass());
    9000          44 : }
    9001             : 
    9002          56 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
    9003             : {
    9004         135 :     for (auto &&poSubGeom : *poGeom)
    9005          79 :         poSubGeom->accept(this);
    9006          56 : }
    9007             : 
    9008           1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
    9009             : {
    9010           2 :     for (auto &&poSubGeom : *poGeom)
    9011           1 :         poSubGeom->accept(this);
    9012           1 : }
    9013             : 
    9014           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
    9015             : {
    9016           1 :     visit(poGeom->toUpperClass());
    9017           1 : }
    9018             : 
    9019           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
    9020             : {
    9021           1 :     visit(poGeom->toUpperClass());
    9022           1 : }
    9023             : 
    9024           2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
    9025             : {
    9026           2 :     visit(poGeom->toUpperClass());
    9027           2 : }
    9028             : 
    9029           2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
    9030             : {
    9031           4 :     for (auto &&poSubGeom : *poGeom)
    9032           2 :         poSubGeom->accept(this);
    9033           2 : }
    9034             : 
    9035           1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
    9036             : {
    9037           1 :     visit(poGeom->toUpperClass());
    9038           1 : }
    9039             : 
    9040         127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
    9041             : {
    9042        2988 :     for (auto &&oPoint : *poGeom)
    9043             :     {
    9044        2861 :         oPoint.accept(this);
    9045             :     }
    9046         127 : }
    9047             : 
    9048         121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
    9049             : {
    9050         121 :     _visit(poGeom);
    9051         121 : }
    9052             : 
    9053         110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
    9054             : {
    9055         110 :     visit(poGeom->toUpperClass());
    9056         110 : }
    9057             : 
    9058           6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
    9059             : {
    9060           6 :     _visit(poGeom);
    9061           6 : }
    9062             : 
    9063         112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
    9064             : {
    9065         225 :     for (auto &&poSubGeom : *poGeom)
    9066         113 :         poSubGeom->accept(this);
    9067         112 : }
    9068             : 
    9069         109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
    9070             : {
    9071         109 :     visit(poGeom->toUpperClass());
    9072         109 : }
    9073             : 
    9074          64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
    9075             : {
    9076          64 :     visit(poGeom->toUpperClass());
    9077          64 : }
    9078             : 
    9079           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
    9080             : {
    9081           1 :     visit(poGeom->toUpperClass());
    9082           1 : }
    9083             : 
    9084          83 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
    9085             : {
    9086          83 :     visit(poGeom->toUpperClass());
    9087          83 : }
    9088             : 
    9089         151 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
    9090             : {
    9091         489 :     for (auto &&poSubGeom : *poGeom)
    9092         338 :         poSubGeom->accept(this);
    9093         151 : }
    9094             : 
    9095           3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
    9096             : {
    9097          14 :     for (auto &&poSubGeom : *poGeom)
    9098          11 :         poSubGeom->accept(this);
    9099           3 : }
    9100             : 
    9101           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
    9102             : {
    9103           1 :     visit(poGeom->toUpperClass());
    9104           1 : }
    9105             : 
    9106           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
    9107             : {
    9108           1 :     visit(poGeom->toUpperClass());
    9109           1 : }
    9110             : 
    9111           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
    9112             : {
    9113           2 :     visit(poGeom->toUpperClass());
    9114           2 : }
    9115             : 
    9116           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
    9117             : {
    9118           4 :     for (auto &&poSubGeom : *poGeom)
    9119           2 :         poSubGeom->accept(this);
    9120           2 : }
    9121             : 
    9122           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
    9123             : {
    9124           1 :     visit(poGeom->toUpperClass());
    9125           1 : }
    9126             : 
    9127             : /************************************************************************/
    9128             : /*                     OGRGeometryUniquePtrDeleter                      */
    9129             : /************************************************************************/
    9130             : 
    9131             : //! @cond Doxygen_Suppress
    9132        1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
    9133             : {
    9134        1333 :     delete poGeom;
    9135        1333 : }
    9136             : 
    9137             : //! @endcond
    9138             : 
    9139             : /************************************************************************/
    9140             : /*                 OGRPreparedGeometryUniquePtrDeleter                  */
    9141             : /************************************************************************/
    9142             : 
    9143             : //! @cond Doxygen_Suppress
    9144         150 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
    9145             :     OGRPreparedGeometry *poPreparedGeom) const
    9146             : {
    9147         150 :     OGRDestroyPreparedGeometry(poPreparedGeom);
    9148         150 : }
    9149             : 
    9150             : //! @endcond
    9151             : 
    9152             : /************************************************************************/
    9153             : /*                    HomogenizeDimensionalityWith()                    */
    9154             : /************************************************************************/
    9155             : 
    9156             : //! @cond Doxygen_Suppress
    9157     3350380 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
    9158             : {
    9159     3350380 :     if (poOtherGeom->Is3D() && !Is3D())
    9160     1331020 :         set3D(TRUE);
    9161             : 
    9162     3350380 :     if (poOtherGeom->IsMeasured() && !IsMeasured())
    9163         854 :         setMeasured(TRUE);
    9164             : 
    9165     3350380 :     if (!poOtherGeom->Is3D() && Is3D())
    9166         298 :         poOtherGeom->set3D(TRUE);
    9167             : 
    9168     3350380 :     if (!poOtherGeom->IsMeasured() && IsMeasured())
    9169          41 :         poOtherGeom->setMeasured(TRUE);
    9170     3350380 : }
    9171             : 
    9172             : //! @endcond
    9173             : 
    9174             : /************************************************************************/
    9175             : /*             OGRGeomCoordinateBinaryPrecision::SetFrom()              */
    9176             : /************************************************************************/
    9177             : 
    9178             : /** Set binary precision options from resolution.
    9179             :  *
    9180             :  * @since GDAL 3.9
    9181             :  */
    9182          16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
    9183             :     const OGRGeomCoordinatePrecision &prec)
    9184             : {
    9185          16 :     if (prec.dfXYResolution != 0)
    9186             :     {
    9187          16 :         nXYBitPrecision =
    9188          16 :             static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
    9189             :     }
    9190          16 :     if (prec.dfZResolution != 0)
    9191             :     {
    9192          12 :         nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
    9193             :     }
    9194          16 :     if (prec.dfMResolution != 0)
    9195             :     {
    9196          12 :         nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
    9197             :     }
    9198          16 : }
    9199             : 
    9200             : /************************************************************************/
    9201             : /*                     OGRwkbExportOptionsCreate()                      */
    9202             : /************************************************************************/
    9203             : 
    9204             : /**
    9205             :  * \brief Create geometry WKB export options.
    9206             :  *
    9207             :  * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
    9208             :  *
    9209             :  * @return object to be freed with OGRwkbExportOptionsDestroy().
    9210             :  * @since GDAL 3.9
    9211             :  */
    9212           2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
    9213             : {
    9214           2 :     return new OGRwkbExportOptions;
    9215             : }
    9216             : 
    9217             : /************************************************************************/
    9218             : /*                     OGRwkbExportOptionsDestroy()                     */
    9219             : /************************************************************************/
    9220             : 
    9221             : /**
    9222             :  * \brief Destroy object returned by OGRwkbExportOptionsCreate()
    9223             :  *
    9224             :  * @param psOptions WKB export options
    9225             :  * @since GDAL 3.9
    9226             :  */
    9227             : 
    9228           2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
    9229             : {
    9230           2 :     delete psOptions;
    9231           2 : }
    9232             : 
    9233             : /************************************************************************/
    9234             : /*                  OGRwkbExportOptionsSetByteOrder()                   */
    9235             : /************************************************************************/
    9236             : 
    9237             : /**
    9238             :  * \brief Set the WKB byte order.
    9239             :  *
    9240             :  * @param psOptions WKB export options
    9241             :  * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
    9242             :  * Intel)
    9243             :  * @since GDAL 3.9
    9244             :  */
    9245             : 
    9246           1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
    9247             :                                      OGRwkbByteOrder eByteOrder)
    9248             : {
    9249           1 :     psOptions->eByteOrder = eByteOrder;
    9250           1 : }
    9251             : 
    9252             : /************************************************************************/
    9253             : /*                   OGRwkbExportOptionsSetVariant()                    */
    9254             : /************************************************************************/
    9255             : 
    9256             : /**
    9257             :  * \brief Set the WKB variant
    9258             :  *
    9259             :  * @param psOptions WKB export options
    9260             :  * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
    9261             :  * wkbVariantPostGIS1
    9262             :  * @since GDAL 3.9
    9263             :  */
    9264             : 
    9265           1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
    9266             :                                    OGRwkbVariant eWkbVariant)
    9267             : {
    9268           1 :     psOptions->eWkbVariant = eWkbVariant;
    9269           1 : }
    9270             : 
    9271             : /************************************************************************/
    9272             : /*                  OGRwkbExportOptionsSetPrecision()                   */
    9273             : /************************************************************************/
    9274             : 
    9275             : /**
    9276             :  * \brief Set precision options
    9277             :  *
    9278             :  * @param psOptions WKB export options
    9279             :  * @param hPrecisionOptions Precision options (might be null to reset them)
    9280             :  * @since GDAL 3.9
    9281             :  */
    9282             : 
    9283           1 : void OGRwkbExportOptionsSetPrecision(
    9284             :     OGRwkbExportOptions *psOptions,
    9285             :     OGRGeomCoordinatePrecisionH hPrecisionOptions)
    9286             : {
    9287           1 :     psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
    9288           1 :     if (hPrecisionOptions)
    9289           1 :         psOptions->sPrecision.SetFrom(*hPrecisionOptions);
    9290           1 : }
    9291             : 
    9292             : /************************************************************************/
    9293             : /*                            IsRectangle()                             */
    9294             : /************************************************************************/
    9295             : 
    9296             : /**
    9297             :  * \brief Returns whether the geometry is a polygon with 4 corners forming
    9298             :  * a rectangle.
    9299             :  *
    9300             :  * @since GDAL 3.10
    9301             :  */
    9302       53073 : bool OGRGeometry::IsRectangle() const
    9303             : {
    9304       53073 :     if (wkbFlatten(getGeometryType()) != wkbPolygon)
    9305         572 :         return false;
    9306             : 
    9307       52501 :     const OGRPolygon *poPoly = toPolygon();
    9308             : 
    9309       52501 :     if (poPoly->getNumInteriorRings() != 0)
    9310          27 :         return false;
    9311             : 
    9312       52474 :     const OGRLinearRing *poRing = poPoly->getExteriorRing();
    9313       52474 :     if (!poRing)
    9314           4 :         return false;
    9315             : 
    9316       52470 :     if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
    9317          33 :         return false;
    9318             : 
    9319             :     // If the ring has 5 points, the last should be the first.
    9320      104817 :     if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
    9321       52380 :                                         poRing->getY(0) != poRing->getY(4)))
    9322           1 :         return false;
    9323             : 
    9324             :     // Polygon with first segment in "y" direction.
    9325      104161 :     if (poRing->getX(0) == poRing->getX(1) &&
    9326      103449 :         poRing->getY(1) == poRing->getY(2) &&
    9327      155885 :         poRing->getX(2) == poRing->getX(3) &&
    9328       51724 :         poRing->getY(3) == poRing->getY(0))
    9329       51724 :         return true;
    9330             : 
    9331             :     // Polygon with first segment in "x" direction.
    9332        1363 :     if (poRing->getY(0) == poRing->getY(1) &&
    9333        1302 :         poRing->getX(1) == poRing->getX(2) &&
    9334        2014 :         poRing->getY(2) == poRing->getY(3) &&
    9335         651 :         poRing->getX(3) == poRing->getX(0))
    9336         651 :         return true;
    9337             : 
    9338          61 :     return false;
    9339             : }
    9340             : 
    9341             : /************************************************************************/
    9342             : /*                           hasEmptyParts()                            */
    9343             : /************************************************************************/
    9344             : 
    9345             : /**
    9346             :  * \brief Returns whether a geometry has empty parts/rings.
    9347             :  *
    9348             :  * Returns true if removeEmptyParts() will modify the geometry.
    9349             :  *
    9350             :  * This is different from IsEmpty().
    9351             :  *
    9352             :  * @since GDAL 3.10
    9353             :  */
    9354         103 : bool OGRGeometry::hasEmptyParts() const
    9355             : {
    9356         103 :     return false;
    9357             : }
    9358             : 
    9359             : /************************************************************************/
    9360             : /*                          removeEmptyParts()                          */
    9361             : /************************************************************************/
    9362             : 
    9363             : /**
    9364             :  * \brief Remove empty parts/rings from this geometry.
    9365             :  *
    9366             :  * @since GDAL 3.10
    9367             :  */
    9368          17 : void OGRGeometry::removeEmptyParts()
    9369             : {
    9370          17 : }
    9371             : 
    9372             : /************************************************************************/
    9373             : /*                        ~IOGRGeometryVisitor()                        */
    9374             : /************************************************************************/
    9375             : 
    9376             : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
    9377             : 
    9378             : /************************************************************************/
    9379             : /*                     ~IOGRConstGeometryVisitor()                      */
    9380             : /************************************************************************/
    9381             : 
    9382             : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;

Generated by: LCOV version 1.14