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-05-17 16:12:46 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             : #define SFCGAL_MAKE_VERSION(major, minor, patch)                               \
      45             :     ((major) * 10000 + (minor) * 100 + (patch))
      46             : #define SFCGAL_VERSION                                                         \
      47             :     SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR,            \
      48             :                         SFCGAL_VERSION_PATCH)
      49             : 
      50             : //! @cond Doxygen_Suppress
      51             : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
      52             : //! @endcond
      53             : 
      54             : #ifdef HAVE_GEOS
      55         103 : static void OGRGEOSErrorHandler(const char *fmt, ...)
      56             : {
      57             :     va_list args;
      58             : 
      59         103 :     va_start(args, fmt);
      60         103 :     CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
      61         103 :     va_end(args);
      62         103 : }
      63             : 
      64         111 : static void OGRGEOSWarningHandler(const char *fmt, ...)
      65             : {
      66             :     va_list args;
      67             : 
      68         111 :     va_start(args, fmt);
      69         111 :     CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
      70         111 :     va_end(args);
      71         111 : }
      72             : #endif
      73             : 
      74             : /************************************************************************/
      75             : /*                           OGRWktOptions()                            */
      76             : /************************************************************************/
      77             : 
      78       11220 : int OGRWktOptions::getDefaultPrecision()
      79             : {
      80       11220 :     return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
      81             : }
      82             : 
      83       11316 : bool OGRWktOptions::getDefaultRound()
      84             : {
      85       11316 :     return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
      86             : }
      87             : 
      88             : /************************************************************************/
      89             : /*                            OGRGeometry()                             */
      90             : /************************************************************************/
      91             : 
      92             : OGRGeometry::OGRGeometry() = default;
      93             : 
      94             : /************************************************************************/
      95             : /*                  OGRGeometry( const OGRGeometry& )                   */
      96             : /************************************************************************/
      97             : 
      98             : /**
      99             :  * \brief Copy constructor.
     100             :  */
     101             : 
     102     1784260 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
     103     1784260 :     : poSRS(other.poSRS), flags(other.flags)
     104             : {
     105     1784260 :     if (poSRS != nullptr)
     106       75344 :         const_cast<OGRSpatialReference *>(poSRS)->Reference();
     107     1784260 : }
     108             : 
     109             : /************************************************************************/
     110             : /*                     OGRGeometry( OGRGeometry&& )                     */
     111             : /************************************************************************/
     112             : 
     113             : /**
     114             :  * \brief Move constructor.
     115             :  *
     116             :  * @since GDAL 3.11
     117             :  */
     118             : 
     119      156278 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
     120      156278 :     : poSRS(other.poSRS), flags(other.flags)
     121             : {
     122      156278 :     other.poSRS = nullptr;
     123      156278 : }
     124             : 
     125             : /************************************************************************/
     126             : /*                            ~OGRGeometry()                            */
     127             : /************************************************************************/
     128             : 
     129    25577100 : OGRGeometry::~OGRGeometry()
     130             : 
     131             : {
     132    12788500 :     if (poSRS != nullptr)
     133     3625420 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     134    12788500 : }
     135             : 
     136             : /************************************************************************/
     137             : /*                    operator=( const OGRGeometry&)                    */
     138             : /************************************************************************/
     139             : 
     140             : /**
     141             :  * \brief Assignment operator.
     142             :  */
     143             : 
     144        1211 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
     145             : {
     146        1211 :     if (this != &other)
     147             :     {
     148        1211 :         empty();
     149        1211 :         assignSpatialReference(other.getSpatialReference());
     150        1211 :         flags = other.flags;
     151             :     }
     152        1211 :     return *this;
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                      operator=( OGRGeometry&&)                       */
     157             : /************************************************************************/
     158             : 
     159             : /**
     160             :  * \brief Move assignment operator.
     161             :  *
     162             :  * @since GDAL 3.11
     163             :  */
     164             : 
     165      103910 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
     166             : {
     167      103910 :     if (this != &other)
     168             :     {
     169      103910 :         poSRS = other.poSRS;
     170      103910 :         other.poSRS = nullptr;
     171      103910 :         flags = other.flags;
     172             :     }
     173      103910 :     return *this;
     174             : }
     175             : 
     176             : /************************************************************************/
     177             : /*                            dumpReadable()                            */
     178             : /************************************************************************/
     179             : 
     180             : /**
     181             :  * \brief Dump geometry in well known text format to indicated output file.
     182             :  *
     183             :  * A few options can be defined to change the default dump :
     184             :  * <ul>
     185             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     186             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     187             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     188             :  * </ul>
     189             :  *
     190             :  * This method is the same as the C function OGR_G_DumpReadable().
     191             :  *
     192             :  * @param fp the text file to write the geometry to.
     193             :  * @param pszPrefix the prefix to put on each line of output.
     194             :  * @param papszOptions NULL terminated list of options (may be NULL)
     195             :  */
     196             : 
     197           0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
     198             :                                CSLConstList papszOptions) const
     199             : 
     200             : {
     201           0 :     if (fp == nullptr)
     202           0 :         fp = stdout;
     203             : 
     204           0 :     const auto osStr = dumpReadable(pszPrefix, papszOptions);
     205           0 :     fprintf(fp, "%s", osStr.c_str());
     206           0 : }
     207             : 
     208             : /************************************************************************/
     209             : /*                            dumpReadable()                            */
     210             : /************************************************************************/
     211             : 
     212             : /**
     213             :  * \brief Dump geometry in well known text format to indicated output file.
     214             :  *
     215             :  * A few options can be defined to change the default dump :
     216             :  * <ul>
     217             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     218             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     219             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     220             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
     221             :  * in WKT (added in GDAL 3.9)</li>
     222             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
     223             :  * WKT (added in GDAL 3.9)</li>
     224             :  * </ul>
     225             :  *
     226             :  * @param pszPrefix the prefix to put on each line of output.
     227             :  * @param papszOptions NULL terminated list of options (may be NULL)
     228             :  * @return a string with the geometry representation.
     229             :  * @since GDAL 3.7
     230             :  */
     231             : 
     232         307 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
     233             :                                       CSLConstList papszOptions) const
     234             : 
     235             : {
     236         307 :     if (pszPrefix == nullptr)
     237         306 :         pszPrefix = "";
     238             : 
     239         307 :     std::string osRet;
     240             : 
     241             :     const auto exportToWktWithOpts =
     242        2044 :         [this, pszPrefix, papszOptions, &osRet](bool bIso)
     243             :     {
     244         292 :         OGRErr err(OGRERR_NONE);
     245         292 :         OGRWktOptions opts;
     246         292 :         if (const char *pszXYPrecision =
     247         292 :                 CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
     248             :         {
     249           1 :             opts.format = OGRWktFormat::F;
     250           1 :             opts.xyPrecision = atoi(pszXYPrecision);
     251             :         }
     252         292 :         if (const char *pszZPrecision =
     253         292 :                 CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
     254             :         {
     255           1 :             opts.format = OGRWktFormat::F;
     256           1 :             opts.zPrecision = atoi(pszZPrecision);
     257             :         }
     258         292 :         if (bIso)
     259         292 :             opts.variant = wkbVariantIso;
     260         584 :         std::string wkt = exportToWkt(opts, &err);
     261         292 :         if (err == OGRERR_NONE)
     262             :         {
     263         292 :             osRet = pszPrefix;
     264         292 :             osRet += wkt.data();
     265         292 :             osRet += '\n';
     266             :         }
     267         292 :     };
     268             : 
     269             :     const char *pszDisplayGeometry =
     270         307 :         CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
     271         307 :     if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
     272             :     {
     273          15 :         osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
     274          15 :         switch (getGeometryType())
     275             :         {
     276           1 :             case wkbUnknown:
     277             :             case wkbNone:
     278             :             case wkbPoint:
     279             :             case wkbPoint25D:
     280             :             case wkbPointM:
     281             :             case wkbPointZM:
     282           1 :                 break;
     283           0 :             case wkbPolyhedralSurface:
     284             :             case wkbTIN:
     285             :             case wkbPolyhedralSurfaceZ:
     286             :             case wkbTINZ:
     287             :             case wkbPolyhedralSurfaceM:
     288             :             case wkbTINM:
     289             :             case wkbPolyhedralSurfaceZM:
     290             :             case wkbTINZM:
     291             :             {
     292           0 :                 const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
     293             :                 osRet +=
     294           0 :                     CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
     295           0 :                 for (auto &&poSubGeom : *poPS)
     296             :                 {
     297           0 :                     osRet += pszPrefix;
     298           0 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     299             :                 }
     300           0 :                 break;
     301             :             }
     302           0 :             case wkbLineString:
     303             :             case wkbLineString25D:
     304             :             case wkbLineStringM:
     305             :             case wkbLineStringZM:
     306             :             case wkbCircularString:
     307             :             case wkbCircularStringZ:
     308             :             case wkbCircularStringM:
     309             :             case wkbCircularStringZM:
     310             :             {
     311           0 :                 const OGRSimpleCurve *poSC = toSimpleCurve();
     312           0 :                 osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
     313           0 :                 break;
     314             :             }
     315          11 :             case wkbPolygon:
     316             :             case wkbTriangle:
     317             :             case wkbTriangleZ:
     318             :             case wkbTriangleM:
     319             :             case wkbTriangleZM:
     320             :             case wkbPolygon25D:
     321             :             case wkbPolygonM:
     322             :             case wkbPolygonZM:
     323             :             case wkbCurvePolygon:
     324             :             case wkbCurvePolygonZ:
     325             :             case wkbCurvePolygonM:
     326             :             case wkbCurvePolygonZM:
     327             :             {
     328          11 :                 const OGRCurvePolygon *poPoly = toCurvePolygon();
     329          11 :                 const OGRCurve *poRing = poPoly->getExteriorRingCurve();
     330          11 :                 const int nRings = poPoly->getNumInteriorRings();
     331          11 :                 if (poRing == nullptr)
     332             :                 {
     333           0 :                     osRet += "empty";
     334             :                 }
     335             :                 else
     336             :                 {
     337          11 :                     osRet += CPLOPrintf("%d points", poRing->getNumPoints());
     338          11 :                     if (wkbFlatten(poRing->getGeometryType()) ==
     339             :                         wkbCompoundCurve)
     340             :                     {
     341           0 :                         osRet += " (";
     342           0 :                         osRet += poRing->dumpReadable(nullptr, papszOptions);
     343           0 :                         osRet += ")";
     344             :                     }
     345          11 :                     if (nRings)
     346             :                     {
     347           1 :                         osRet += CPLOPrintf(", %d inner rings (", nRings);
     348           8 :                         for (int ir = 0; ir < nRings; ir++)
     349             :                         {
     350           7 :                             poRing = poPoly->getInteriorRingCurve(ir);
     351           7 :                             if (ir)
     352           6 :                                 osRet += ", ";
     353             :                             osRet +=
     354           7 :                                 CPLOPrintf("%d points", poRing->getNumPoints());
     355           7 :                             if (wkbFlatten(poRing->getGeometryType()) ==
     356             :                                 wkbCompoundCurve)
     357             :                             {
     358           2 :                                 osRet += " (";
     359             :                                 osRet +=
     360           2 :                                     poRing->dumpReadable(nullptr, papszOptions);
     361           2 :                                 osRet += ")";
     362             :                             }
     363             :                         }
     364           1 :                         osRet += ")";
     365             :                     }
     366             :                 }
     367          11 :                 osRet += "\n";
     368          11 :                 break;
     369             :             }
     370           2 :             case wkbCompoundCurve:
     371             :             case wkbCompoundCurveZ:
     372             :             case wkbCompoundCurveM:
     373             :             case wkbCompoundCurveZM:
     374             :             {
     375           2 :                 const OGRCompoundCurve *poCC = toCompoundCurve();
     376           2 :                 if (poCC->getNumCurves() == 0)
     377             :                 {
     378           0 :                     osRet += "empty";
     379             :                 }
     380             :                 else
     381             :                 {
     382           6 :                     for (int i = 0; i < poCC->getNumCurves(); i++)
     383             :                     {
     384           4 :                         if (i)
     385           2 :                             osRet += ", ";
     386             :                         osRet +=
     387           8 :                             CPLOPrintf("%s (%d points)",
     388           4 :                                        poCC->getCurve(i)->getGeometryName(),
     389           8 :                                        poCC->getCurve(i)->getNumPoints());
     390             :                     }
     391             :                 }
     392           2 :                 break;
     393             :             }
     394             : 
     395           1 :             case wkbMultiPoint:
     396             :             case wkbMultiLineString:
     397             :             case wkbMultiPolygon:
     398             :             case wkbMultiCurve:
     399             :             case wkbMultiSurface:
     400             :             case wkbGeometryCollection:
     401             :             case wkbMultiPoint25D:
     402             :             case wkbMultiLineString25D:
     403             :             case wkbMultiPolygon25D:
     404             :             case wkbMultiCurveZ:
     405             :             case wkbMultiSurfaceZ:
     406             :             case wkbGeometryCollection25D:
     407             :             case wkbMultiPointM:
     408             :             case wkbMultiLineStringM:
     409             :             case wkbMultiPolygonM:
     410             :             case wkbMultiCurveM:
     411             :             case wkbMultiSurfaceM:
     412             :             case wkbGeometryCollectionM:
     413             :             case wkbMultiPointZM:
     414             :             case wkbMultiLineStringZM:
     415             :             case wkbMultiPolygonZM:
     416             :             case wkbMultiCurveZM:
     417             :             case wkbMultiSurfaceZM:
     418             :             case wkbGeometryCollectionZM:
     419             :             {
     420           1 :                 const OGRGeometryCollection *poColl = toGeometryCollection();
     421             :                 osRet +=
     422           1 :                     CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
     423           2 :                 for (auto &&poSubGeom : *poColl)
     424             :                 {
     425           1 :                     osRet += pszPrefix;
     426           1 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     427             :                 }
     428           1 :                 break;
     429             :             }
     430           0 :             case wkbLinearRing:
     431             :             case wkbCurve:
     432             :             case wkbSurface:
     433             :             case wkbCurveZ:
     434             :             case wkbSurfaceZ:
     435             :             case wkbCurveM:
     436             :             case wkbSurfaceM:
     437             :             case wkbCurveZM:
     438             :             case wkbSurfaceZM:
     439           0 :                 break;
     440          15 :         }
     441             :     }
     442         292 :     else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
     443             :     {
     444           0 :         exportToWktWithOpts(/* bIso=*/false);
     445             :     }
     446         292 :     else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
     447           0 :              EQUAL(pszDisplayGeometry, "ISO_WKT"))
     448             :     {
     449         292 :         exportToWktWithOpts(/* bIso=*/true);
     450             :     }
     451             : 
     452         614 :     return osRet;
     453             : }
     454             : 
     455             : /************************************************************************/
     456             : /*                         OGR_G_DumpReadable()                         */
     457             : /************************************************************************/
     458             : /**
     459             :  * \brief Dump geometry in well known text format to indicated output file.
     460             :  *
     461             :  * This method is the same as the CPP method OGRGeometry::dumpReadable.
     462             :  *
     463             :  * @param hGeom handle on the geometry to dump.
     464             :  * @param fp the text file to write the geometry to.
     465             :  * @param pszPrefix the prefix to put on each line of output.
     466             :  */
     467             : 
     468           0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
     469             : 
     470             : {
     471           0 :     VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
     472             : 
     473           0 :     OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
     474             : }
     475             : 
     476             : /************************************************************************/
     477             : /*                       assignSpatialReference()                       */
     478             : /************************************************************************/
     479             : 
     480             : /**
     481             :  * \brief Assign spatial reference to this object.
     482             :  *
     483             :  * Any existing spatial reference
     484             :  * is replaced, but under no circumstances does this result in the object
     485             :  * being reprojected.  It is just changing the interpretation of the existing
     486             :  * geometry.  Note that assigning a spatial reference increments the
     487             :  * reference count on the OGRSpatialReference, but does not copy it.
     488             :  *
     489             :  * This will also assign the spatial reference to
     490             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     491             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     492             :  * derived classes).
     493             :  *
     494             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     495             :  *
     496             :  * This method is the same as the C function OGR_G_AssignSpatialReference().
     497             :  *
     498             :  * @param poSR new spatial reference system to apply.
     499             :  */
     500             : 
     501     5593890 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
     502             : 
     503             : {
     504             :     // Do in that order to properly handle poSR == poSRS
     505     5593890 :     if (poSR != nullptr)
     506     3586140 :         const_cast<OGRSpatialReference *>(poSR)->Reference();
     507     5593890 :     if (poSRS != nullptr)
     508       36059 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     509             : 
     510     5593890 :     poSRS = poSR;
     511     5593890 : }
     512             : 
     513             : /************************************************************************/
     514             : /*                    OGR_G_AssignSpatialReference()                    */
     515             : /************************************************************************/
     516             : /**
     517             :  * \brief Assign spatial reference to this object.
     518             :  *
     519             :  * Any existing spatial reference
     520             :  * is replaced, but under no circumstances does this result in the object
     521             :  * being reprojected.  It is just changing the interpretation of the existing
     522             :  * geometry.  Note that assigning a spatial reference increments the
     523             :  * reference count on the OGRSpatialReference, but does not copy it.
     524             :  *
     525             :  * This will also assign the spatial reference to
     526             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     527             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     528             :  * derived classes).
     529             :  *
     530             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     531             :  *
     532             :  * This function is the same as the CPP method
     533             :  * OGRGeometry::assignSpatialReference.
     534             :  *
     535             :  * @param hGeom handle on the geometry to apply the new spatial reference
     536             :  * system.
     537             :  * @param hSRS handle on the new spatial reference system to apply.
     538             :  */
     539             : 
     540          80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     541             : 
     542             : {
     543          80 :     VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
     544             : 
     545         160 :     OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
     546          80 :         OGRSpatialReference::FromHandle(hSRS));
     547             : }
     548             : 
     549             : /************************************************************************/
     550             : /*                             Intersects()                             */
     551             : /************************************************************************/
     552             : 
     553             : /**
     554             :  * \brief Do these features intersect?
     555             :  *
     556             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     557             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     558             :  * envelopes (bounding boxes) of the two geometries overlap.
     559             :  *
     560             :  * The poOtherGeom argument may be safely NULL, but in this case the method
     561             :  * will always return TRUE.   That is, a NULL geometry is treated as being
     562             :  * everywhere.
     563             :  *
     564             :  * This method is the same as the C function OGR_G_Intersects().
     565             :  *
     566             :  * @param poOtherGeom the other geometry to test against.
     567             :  *
     568             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     569             :  */
     570             : 
     571          44 : bool OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
     572             : 
     573             : {
     574          44 :     if (poOtherGeom == nullptr)
     575           0 :         return TRUE;
     576             : 
     577          44 :     OGREnvelope oEnv1;
     578          44 :     getEnvelope(&oEnv1);
     579             : 
     580          44 :     OGREnvelope oEnv2;
     581          44 :     poOtherGeom->getEnvelope(&oEnv2);
     582             : 
     583          44 :     if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
     584          26 :         oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
     585          18 :         return FALSE;
     586             : 
     587             : #ifndef HAVE_GEOS
     588             :     // Without GEOS we assume that envelope overlap is equivalent to
     589             :     // actual intersection.
     590             :     return TRUE;
     591             : #else
     592             : 
     593          26 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
     594          26 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
     595          26 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
     596             : 
     597          26 :     bool bResult = false;
     598          26 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
     599             :     {
     600          26 :         bResult =
     601          26 :             GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
     602             :     }
     603             : 
     604          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
     605          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
     606          26 :     freeGEOSContext(hGEOSCtxt);
     607             : 
     608          26 :     return bResult;
     609             : #endif  // HAVE_GEOS
     610             : }
     611             : 
     612             : // Old API compatibility function.
     613             : 
     614             : //! @cond Doxygen_Suppress
     615           0 : bool OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
     616             : 
     617             : {
     618           0 :     return Intersects(poOtherGeom);
     619             : }
     620             : 
     621             : //! @endcond
     622             : 
     623             : /************************************************************************/
     624             : /*                          OGR_G_Intersects()                          */
     625             : /************************************************************************/
     626             : /**
     627             :  * \brief Do these features intersect?
     628             :  *
     629             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     630             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     631             :  * envelopes (bounding boxes) of the two geometries overlap.
     632             :  *
     633             :  * This function is the same as the CPP method OGRGeometry::Intersects.
     634             :  *
     635             :  * @param hGeom handle on the first geometry.
     636             :  * @param hOtherGeom handle on the other geometry to test against.
     637             :  *
     638             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     639             :  */
     640             : 
     641          11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     642             : 
     643             : {
     644          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
     645          11 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
     646             : 
     647          22 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     648          22 :         OGRGeometry::FromHandle(hOtherGeom));
     649             : }
     650             : 
     651             : //! @cond Doxygen_Suppress
     652           0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     653             : 
     654             : {
     655           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
     656           0 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
     657             : 
     658           0 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     659           0 :         OGRGeometry::FromHandle(hOtherGeom));
     660             : }
     661             : 
     662             : //! @endcond
     663             : 
     664             : /************************************************************************/
     665             : /*                            transformTo()                             */
     666             : /************************************************************************/
     667             : 
     668             : /**
     669             :  * \brief Transform geometry to new spatial reference system.
     670             :  *
     671             :  * This method will transform the coordinates of a geometry from
     672             :  * their current spatial reference system to a new target spatial
     673             :  * reference system.  Normally this means reprojecting the vectors,
     674             :  * but it could include datum shifts, and changes of units.
     675             :  *
     676             :  * This method will only work if the geometry already has an assigned
     677             :  * spatial reference system, and if it is transformable to the target
     678             :  * coordinate system.
     679             :  *
     680             :  * Because this method requires internal creation and initialization of an
     681             :  * OGRCoordinateTransformation object it is significantly more expensive to
     682             :  * use this method to transform many geometries than it is to create the
     683             :  * OGRCoordinateTransformation in advance, and call transform() with that
     684             :  * transformation.  This method exists primarily for convenience when only
     685             :  * transforming a single geometry.
     686             :  *
     687             :  * This method is the same as the C function OGR_G_TransformTo().
     688             :  *
     689             :  * @param poSR spatial reference system to transform to.
     690             :  *
     691             :  * @return OGRERR_NONE on success, or an error code.
     692             :  */
     693             : 
     694          27 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
     695             : 
     696             : {
     697          27 :     if (getSpatialReference() == nullptr)
     698             :     {
     699           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
     700           1 :         return OGRERR_FAILURE;
     701             :     }
     702             : 
     703          26 :     if (poSR == nullptr)
     704             :     {
     705           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
     706           0 :         return OGRERR_FAILURE;
     707             :     }
     708             : 
     709             :     OGRCoordinateTransformation *poCT =
     710          26 :         OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
     711          26 :     if (poCT == nullptr)
     712           0 :         return OGRERR_FAILURE;
     713             : 
     714          26 :     const OGRErr eErr = transform(poCT);
     715             : 
     716          26 :     delete poCT;
     717             : 
     718          26 :     return eErr;
     719             : }
     720             : 
     721             : /************************************************************************/
     722             : /*                         OGR_G_TransformTo()                          */
     723             : /************************************************************************/
     724             : /**
     725             :  * \brief Transform geometry to new spatial reference system.
     726             :  *
     727             :  * This function will transform the coordinates of a geometry from
     728             :  * their current spatial reference system to a new target spatial
     729             :  * reference system.  Normally this means reprojecting the vectors,
     730             :  * but it could include datum shifts, and changes of units.
     731             :  *
     732             :  * This function will only work if the geometry already has an assigned
     733             :  * spatial reference system, and if it is transformable to the target
     734             :  * coordinate system.
     735             :  *
     736             :  * Because this function requires internal creation and initialization of an
     737             :  * OGRCoordinateTransformation object it is significantly more expensive to
     738             :  * use this function to transform many geometries than it is to create the
     739             :  * OGRCoordinateTransformation in advance, and call transform() with that
     740             :  * transformation.  This function exists primarily for convenience when only
     741             :  * transforming a single geometry.
     742             :  *
     743             :  * This function is the same as the CPP method OGRGeometry::transformTo.
     744             :  *
     745             :  * @param hGeom handle on the geometry to apply the transform to.
     746             :  * @param hSRS handle on the spatial reference system to apply.
     747             :  *
     748             :  * @return OGRERR_NONE on success, or an error code.
     749             :  */
     750             : 
     751           9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     752             : 
     753             : {
     754           9 :     VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
     755             : 
     756          18 :     return OGRGeometry::FromHandle(hGeom)->transformTo(
     757          18 :         OGRSpatialReference::FromHandle(hSRS));
     758             : }
     759             : 
     760             : /**
     761             :  * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
     762             :  *
     763             :  * \brief Apply arbitrary coordinate transformation to geometry.
     764             :  *
     765             :  * This method will transform the coordinates of a geometry from
     766             :  * their current spatial reference system to a new target spatial
     767             :  * reference system.  Normally this means reprojecting the vectors,
     768             :  * but it could include datum shifts, and changes of units.
     769             :  *
     770             :  * Note that this method does not require that the geometry already
     771             :  * have a spatial reference system.  It will be assumed that they can
     772             :  * be treated as having the source spatial reference system of the
     773             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     774             :  * will be ignored.  On successful completion the output OGRSpatialReference
     775             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     776             :  *
     777             :  * This method only does reprojection on a point-by-point basis. It does not
     778             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     779             :  * For that, use the OGRGeometryFactory::transformWithOptions() method.
     780             :  *
     781             :  * This method is the same as the C function OGR_G_Transform().
     782             :  *
     783             :  * @param poCT the transformation to apply.
     784             :  *
     785             :  * @return OGRERR_NONE on success or an error code.
     786             :  */
     787             : 
     788             : /************************************************************************/
     789             : /*                          OGR_G_Transform()                           */
     790             : /************************************************************************/
     791             : /**
     792             :  * \brief Apply arbitrary coordinate transformation to geometry.
     793             :  *
     794             :  * This function will transform the coordinates of a geometry from
     795             :  * their current spatial reference system to a new target spatial
     796             :  * reference system.  Normally this means reprojecting the vectors,
     797             :  * but it could include datum shifts, and changes of units.
     798             :  *
     799             :  * Note that this function does not require that the geometry already
     800             :  * have a spatial reference system.  It will be assumed that they can
     801             :  * be treated as having the source spatial reference system of the
     802             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     803             :  * will be ignored.  On successful completion the output OGRSpatialReference
     804             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     805             :  *
     806             :  * This function only does reprojection on a point-by-point basis. It does not
     807             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     808             :  * For that, use the OGR_GeomTransformer_Create() and
     809             :  * OGR_GeomTransformer_Transform() functions.
     810             :  *
     811             :  * This function is the same as the CPP method OGRGeometry::transform.
     812             :  *
     813             :  * @param hGeom handle on the geometry to apply the transform to.
     814             :  * @param hTransform handle on the transformation to apply.
     815             :  *
     816             :  * @return OGRERR_NONE on success or an error code.
     817             :  */
     818             : 
     819          11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
     820             :                        OGRCoordinateTransformationH hTransform)
     821             : 
     822             : {
     823          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
     824             : 
     825          22 :     return OGRGeometry::FromHandle(hGeom)->transform(
     826          11 :         OGRCoordinateTransformation::FromHandle(hTransform));
     827             : }
     828             : 
     829             : /**
     830             :  * \fn int OGRGeometry::getDimension() const;
     831             :  *
     832             :  * \brief Get the dimension of this object.
     833             :  *
     834             :  * This method corresponds to the SFCOM IGeometry::GetDimension() method.
     835             :  * It indicates the dimension of the object, but does not indicate the
     836             :  * dimension of the underlying space (as indicated by
     837             :  * OGRGeometry::getCoordinateDimension()).
     838             :  *
     839             :  * This method is the same as the C function OGR_G_GetDimension().
     840             :  *
     841             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     842             :  */
     843             : 
     844             : /**
     845             :  * \brief Get the geometry type that conforms with ISO SQL/MM Part3
     846             :  *
     847             :  * @return the geometry type that conforms with ISO SQL/MM Part3
     848             :  */
     849      716953 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
     850             : {
     851      716953 :     OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
     852             : 
     853      716953 :     if (flags & OGR_G_3D)
     854      214269 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
     855      716953 :     if (flags & OGR_G_MEASURED)
     856       26025 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
     857             : 
     858      716953 :     return nGType;
     859             : }
     860             : 
     861             : /************************************************************************/
     862             : /*                      OGRGeometry::segmentize()                       */
     863             : /************************************************************************/
     864             : /**
     865             :  *
     866             :  * \brief Modify the geometry such it has no segment longer then the
     867             :  * given distance.
     868             :  *
     869             :  * This method modifies the geometry to add intermediate vertices if necessary
     870             :  * so that the maximum length between 2 consecutive vertices is lower than
     871             :  * dfMaxLength.
     872             :  *
     873             :  * Interpolated points will have Z and M values (if needed) set to 0.
     874             :  * Distance computation is performed in 2d only
     875             :  *
     876             :  * This function is the same as the C function OGR_G_Segmentize()
     877             :  *
     878             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     879             :  * @return (since 3.10) true in case of success, false in case of error.
     880             :  */
     881             : 
     882           0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
     883             : {
     884             :     // Do nothing.
     885           0 :     return true;
     886             : }
     887             : 
     888             : /************************************************************************/
     889             : /*                          OGR_G_Segmentize()                          */
     890             : /************************************************************************/
     891             : 
     892             : /**
     893             :  *
     894             :  * \brief Modify the geometry such it has no segment longer then the given
     895             :  * distance.
     896             :  *
     897             :  * Interpolated points will have Z and M values (if needed) set to 0.
     898             :  * Distance computation is performed in 2d only.
     899             :  *
     900             :  * This function is the same as the CPP method OGRGeometry::segmentize().
     901             :  *
     902             :  * @param hGeom handle on the geometry to segmentize
     903             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     904             :  */
     905             : 
     906          24 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
     907             : {
     908          24 :     VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
     909             : 
     910          24 :     if (dfMaxLength <= 0)
     911             :     {
     912           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     913             :                  "dfMaxLength must be strictly positive");
     914           0 :         return;
     915             :     }
     916          24 :     OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
     917             : }
     918             : 
     919             : /************************************************************************/
     920             : /*                         OGR_G_GetDimension()                         */
     921             : /************************************************************************/
     922             : /**
     923             :  *
     924             :  * \brief Get the dimension of this geometry.
     925             :  *
     926             :  * This function corresponds to the SFCOM IGeometry::GetDimension() method.
     927             :  * It indicates the dimension of the geometry, but does not indicate the
     928             :  * dimension of the underlying space (as indicated by
     929             :  * OGR_G_GetCoordinateDimension() function).
     930             :  *
     931             :  * This function is the same as the CPP method OGRGeometry::getDimension().
     932             :  *
     933             :  * @param hGeom handle on the geometry to get the dimension from.
     934             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     935             :  */
     936             : 
     937          21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
     938             : 
     939             : {
     940          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
     941             : 
     942          21 :     return OGRGeometry::FromHandle(hGeom)->getDimension();
     943             : }
     944             : 
     945             : /************************************************************************/
     946             : /*                       getCoordinateDimension()                       */
     947             : /************************************************************************/
     948             : /**
     949             :  * \brief Get the dimension of the coordinates in this object.
     950             :  *
     951             :  * This method is the same as the C function OGR_G_GetCoordinateDimension().
     952             :  *
     953             :  * @deprecated use CoordinateDimension().
     954             :  *
     955             :  * @return this will return 2 or 3.
     956             :  */
     957             : 
     958      571478 : int OGRGeometry::getCoordinateDimension() const
     959             : 
     960             : {
     961      571478 :     return (flags & OGR_G_3D) ? 3 : 2;
     962             : }
     963             : 
     964             : /************************************************************************/
     965             : /*                        CoordinateDimension()                         */
     966             : /************************************************************************/
     967             : /**
     968             :  * \brief Get the dimension of the coordinates in this object.
     969             :  *
     970             :  * This method is the same as the C function OGR_G_CoordinateDimension().
     971             :  *
     972             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
     973             :  *
     974             :  */
     975             : 
     976       30428 : int OGRGeometry::CoordinateDimension() const
     977             : 
     978             : {
     979       30428 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     980        7375 :         return 4;
     981       23053 :     else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
     982        6863 :         return 3;
     983             :     else
     984       16190 :         return 2;
     985             : }
     986             : 
     987             : /************************************************************************/
     988             : /*                    OGR_G_GetCoordinateDimension()                    */
     989             : /************************************************************************/
     990             : /**
     991             :  *
     992             :  * \brief Get the dimension of the coordinates in this geometry.
     993             :  *
     994             :  * This function is the same as the CPP method
     995             :  * OGRGeometry::getCoordinateDimension().
     996             :  *
     997             :  * @param hGeom handle on the geometry to get the dimension of the
     998             :  * coordinates from.
     999             :  *
    1000             :  * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
    1001             :  * OGR_G_IsMeasured().
    1002             :  *
    1003             :  * @return this will return 2 or 3.
    1004             :  */
    1005             : 
    1006         724 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
    1007             : 
    1008             : {
    1009         724 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
    1010             : 
    1011         724 :     return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
    1012             : }
    1013             : 
    1014             : /************************************************************************/
    1015             : /*                     OGR_G_CoordinateDimension()                      */
    1016             : /************************************************************************/
    1017             : /**
    1018             :  *
    1019             :  * \brief Get the dimension of the coordinates in this geometry.
    1020             :  *
    1021             :  * This function is the same as the CPP method
    1022             :  * OGRGeometry::CoordinateDimension().
    1023             :  *
    1024             :  * @param hGeom handle on the geometry to get the dimension of the
    1025             :  * coordinates from.
    1026             :  *
    1027             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
    1028             :  *
    1029             :  */
    1030             : 
    1031           4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
    1032             : 
    1033             : {
    1034           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
    1035             : 
    1036           4 :     return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
    1037             : }
    1038             : 
    1039             : /**
    1040             :  *
    1041             :  * \brief See whether this geometry has Z coordinates.
    1042             :  *
    1043             :  * This function is the same as the CPP method
    1044             :  * OGRGeometry::Is3D().
    1045             :  *
    1046             :  * @param hGeom handle on the geometry to check whether it has Z coordinates.
    1047             :  *
    1048             :  * @return TRUE if the geometry has Z coordinates.
    1049             :  */
    1050             : 
    1051       37776 : int OGR_G_Is3D(OGRGeometryH hGeom)
    1052             : 
    1053             : {
    1054       37776 :     VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
    1055             : 
    1056       37776 :     return OGRGeometry::FromHandle(hGeom)->Is3D();
    1057             : }
    1058             : 
    1059             : /**
    1060             :  *
    1061             :  * \brief See whether this geometry is measured.
    1062             :  *
    1063             :  * This function is the same as the CPP method
    1064             :  * OGRGeometry::IsMeasured().
    1065             :  *
    1066             :  * @param hGeom handle on the geometry to check whether it is measured.
    1067             :  *
    1068             :  * @return TRUE if the geometry has M coordinates.
    1069             :  */
    1070             : 
    1071       40185 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
    1072             : 
    1073             : {
    1074       40185 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
    1075             : 
    1076       40185 :     return OGRGeometry::FromHandle(hGeom)->IsMeasured();
    1077             : }
    1078             : 
    1079             : /************************************************************************/
    1080             : /*                       setCoordinateDimension()                       */
    1081             : /************************************************************************/
    1082             : 
    1083             : /**
    1084             :  * \brief Set the coordinate dimension.
    1085             :  *
    1086             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1087             :  * dimension of a geometry to 2 should zero out any existing Z values.  Setting
    1088             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1089             :  * will affect the children geometries.
    1090             :  * This will also remove the M dimension if present before this call.
    1091             :  *
    1092             :  * @deprecated use set3D() or setMeasured().
    1093             :  *
    1094             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1095             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1096             :  */
    1097             : 
    1098       66665 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
    1099             : 
    1100             : {
    1101       66665 :     if (nNewDimension == 2)
    1102       66173 :         flags &= ~OGR_G_3D;
    1103             :     else
    1104         492 :         flags |= OGR_G_3D;
    1105       66665 :     return setMeasured(FALSE);
    1106             : }
    1107             : 
    1108             : /**
    1109             :  * \brief Add or remove the Z coordinate dimension.
    1110             :  *
    1111             :  * This method adds or removes the explicit Z coordinate dimension.
    1112             :  * Removing the Z coordinate dimension of a geometry will remove any
    1113             :  * existing Z values.  Adding the Z dimension to a geometry
    1114             :  * collection, a compound curve, a polygon, etc.  will affect the
    1115             :  * children geometries.
    1116             :  *
    1117             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1118             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1119             :  */
    1120             : 
    1121     1618090 : bool OGRGeometry::set3D(bool bIs3D)
    1122             : 
    1123             : {
    1124     1618090 :     if (bIs3D)
    1125     1612740 :         flags |= OGR_G_3D;
    1126             :     else
    1127        5350 :         flags &= ~OGR_G_3D;
    1128     1618090 :     return true;
    1129             : }
    1130             : 
    1131             : /**
    1132             :  * \brief Add or remove the M coordinate dimension.
    1133             :  *
    1134             :  * This method adds or removes the explicit M coordinate dimension.
    1135             :  * Removing the M coordinate dimension of a geometry will remove any
    1136             :  * existing M values.  Adding the M dimension to a geometry
    1137             :  * collection, a compound curve, a polygon, etc.  will affect the
    1138             :  * children geometries.
    1139             :  *
    1140             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1141             :  * TRUE or FALSE.
    1142             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1143             :  */
    1144             : 
    1145      413283 : bool OGRGeometry::setMeasured(bool bIsMeasured)
    1146             : 
    1147             : {
    1148      413283 :     if (bIsMeasured)
    1149      137729 :         flags |= OGR_G_MEASURED;
    1150             :     else
    1151      275554 :         flags &= ~OGR_G_MEASURED;
    1152      413283 :     return true;
    1153             : }
    1154             : 
    1155             : /************************************************************************/
    1156             : /*                    OGR_G_SetCoordinateDimension()                    */
    1157             : /************************************************************************/
    1158             : 
    1159             : /**
    1160             :  * \brief Set the coordinate dimension.
    1161             :  *
    1162             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1163             :  * dimension of a geometry to 2 should zero out any existing Z values. Setting
    1164             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1165             :  * will affect the children geometries.
    1166             :  * This will also remove the M dimension if present before this call.
    1167             :  *
    1168             :  * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
    1169             :  *
    1170             :  * @param hGeom handle on the geometry to set the dimension of the
    1171             :  * coordinates.
    1172             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1173             :  */
    1174             : 
    1175          56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
    1176             : 
    1177             : {
    1178          56 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
    1179             : 
    1180          56 :     OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
    1181             : }
    1182             : 
    1183             : /************************************************************************/
    1184             : /*                            OGR_G_Set3D()                             */
    1185             : /************************************************************************/
    1186             : 
    1187             : /**
    1188             :  * \brief Add or remove the Z coordinate dimension.
    1189             :  *
    1190             :  * This method adds or removes the explicit Z coordinate dimension.
    1191             :  * Removing the Z coordinate dimension of a geometry will remove any
    1192             :  * existing Z values.  Adding the Z dimension to a geometry
    1193             :  * collection, a compound curve, a polygon, etc.  will affect the
    1194             :  * children geometries.
    1195             :  *
    1196             :  * @param hGeom handle on the geometry to set or unset the Z dimension.
    1197             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1198             :  */
    1199             : 
    1200         154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
    1201             : 
    1202             : {
    1203         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
    1204             : 
    1205         154 :     OGRGeometry::FromHandle(hGeom)->set3D(CPL_TO_BOOL(bIs3D));
    1206             : }
    1207             : 
    1208             : /************************************************************************/
    1209             : /*                         OGR_G_SetMeasured()                          */
    1210             : /************************************************************************/
    1211             : 
    1212             : /**
    1213             :  * \brief Add or remove the M coordinate dimension.
    1214             :  *
    1215             :  * This method adds or removes the explicit M coordinate dimension.
    1216             :  * Removing the M coordinate dimension of a geometry will remove any
    1217             :  * existing M values.  Adding the M dimension to a geometry
    1218             :  * collection, a compound curve, a polygon, etc.  will affect the
    1219             :  * children geometries.
    1220             :  *
    1221             :  * @param hGeom handle on the geometry to set or unset the M dimension.
    1222             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1223             :  * TRUE or FALSE.
    1224             :  */
    1225             : 
    1226         154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
    1227             : 
    1228             : {
    1229         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
    1230             : 
    1231         154 :     OGRGeometry::FromHandle(hGeom)->setMeasured(CPL_TO_BOOL(bIsMeasured));
    1232             : }
    1233             : 
    1234             : /**
    1235             :  * \fn bool OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
    1236             :  *
    1237             :  * \brief Returns TRUE if two geometries are equivalent.
    1238             :  *
    1239             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1240             :  *
    1241             :  * The comparison is done in a structural way, that is to say that the geometry
    1242             :  * types must be identical, as well as the number and ordering of sub-geometries
    1243             :  * and vertices.
    1244             :  * Or equivalently, two geometries are considered equal by this method if their
    1245             :  * WKT/WKB representation is equal.
    1246             :  * Note: this must be distinguished for equality in a spatial way (which is
    1247             :  * the purpose of the ST_Equals() operation).
    1248             :  *
    1249             :  * This method is the same as the C function OGR_G_Equals().
    1250             :  *
    1251             :  * @return TRUE if equivalent or FALSE otherwise.
    1252             :  */
    1253             : 
    1254             : // Backward compatibility method.
    1255             : 
    1256             : //! @cond Doxygen_Suppress
    1257           0 : bool OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
    1258             : {
    1259           0 :     return Equals(poOtherGeom);
    1260             : }
    1261             : 
    1262             : //! @endcond
    1263             : 
    1264             : /************************************************************************/
    1265             : /*                            OGR_G_Equals()                            */
    1266             : /************************************************************************/
    1267             : 
    1268             : /**
    1269             :  * \brief Returns TRUE if two geometries are equivalent.
    1270             :  *
    1271             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1272             :  *
    1273             :  * The comparison is done in a structural way, that is to say that the geometry
    1274             :  * types must be identical, as well as the number and ordering of sub-geometries
    1275             :  * and vertices.
    1276             :  * Or equivalently, two geometries are considered equal by this method if their
    1277             :  * WKT/WKB representation is equal.
    1278             :  * Note: this must be distinguished for equality in a spatial way (which is
    1279             :  * the purpose of the ST_Equals() operation).
    1280             :  *
    1281             :  * This function is the same as the CPP method OGRGeometry::Equals() method.
    1282             :  *
    1283             :  * @param hGeom handle on the first geometry.
    1284             :  * @param hOther handle on the other geometry to test against.
    1285             :  * @return TRUE if equivalent or FALSE otherwise.
    1286             :  */
    1287             : 
    1288       28137 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
    1289             : 
    1290             : {
    1291       28137 :     VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
    1292             : 
    1293       28137 :     if (hOther == nullptr)
    1294             :     {
    1295           0 :         CPLError(CE_Failure, CPLE_ObjectNull,
    1296             :                  "hOther was NULL in OGR_G_Equals");
    1297           0 :         return 0;
    1298             :     }
    1299             : 
    1300       56274 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1301       56274 :         OGRGeometry::FromHandle(hOther));
    1302             : }
    1303             : 
    1304             : //! @cond Doxygen_Suppress
    1305           0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
    1306             : 
    1307             : {
    1308           0 :     if (hGeom == nullptr)
    1309             :     {
    1310           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
    1311           0 :         return 0;
    1312             :     }
    1313             : 
    1314           0 :     if (hOther == nullptr)
    1315             :     {
    1316           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
    1317           0 :         return 0;
    1318             :     }
    1319             : 
    1320           0 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1321           0 :         OGRGeometry::FromHandle(hOther));
    1322             : }
    1323             : 
    1324             : //! @endcond
    1325             : 
    1326             : /**
    1327             :  * \fn int OGRGeometry::WkbSize() const;
    1328             :  *
    1329             :  * \brief Returns size of related binary representation.
    1330             :  *
    1331             :  * This method returns the exact number of bytes required to hold the
    1332             :  * well known binary representation of this geometry object.  Its computation
    1333             :  * may be slightly expensive for complex geometries.
    1334             :  *
    1335             :  * This method relates to the SFCOM IWks::WkbSize() method.
    1336             :  *
    1337             :  * This method is the same as the C function OGR_G_WkbSize().
    1338             :  *
    1339             :  * @return size of binary representation in bytes.
    1340             :  */
    1341             : 
    1342             : /************************************************************************/
    1343             : /*                           OGR_G_WkbSize()                            */
    1344             : /************************************************************************/
    1345             : /**
    1346             :  * \brief Returns size of related binary representation.
    1347             :  *
    1348             :  * This function returns the exact number of bytes required to hold the
    1349             :  * well known binary representation of this geometry object.  Its computation
    1350             :  * may be slightly expensive for complex geometries.
    1351             :  *
    1352             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1353             :  *
    1354             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1355             :  *
    1356             :  * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
    1357             :  *
    1358             :  * @param hGeom handle on the geometry to get the binary size from.
    1359             :  * @return size of binary representation in bytes.
    1360             :  */
    1361             : 
    1362           1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
    1363             : 
    1364             : {
    1365           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
    1366             : 
    1367           1 :     const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
    1368           1 :     if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
    1369             :     {
    1370           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1371             :                  "OGR_G_WkbSize() would return a value beyond int range. "
    1372             :                  "Use OGR_G_WkbSizeEx() instead");
    1373           0 :         return 0;
    1374             :     }
    1375           1 :     return static_cast<int>(nSize);
    1376             : }
    1377             : 
    1378             : /************************************************************************/
    1379             : /*                          OGR_G_WkbSizeEx()                           */
    1380             : /************************************************************************/
    1381             : /**
    1382             :  * \brief Returns size of related binary representation.
    1383             :  *
    1384             :  * This function returns the exact number of bytes required to hold the
    1385             :  * well known binary representation of this geometry object.  Its computation
    1386             :  * may be slightly expensive for complex geometries.
    1387             :  *
    1388             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1389             :  *
    1390             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1391             :  *
    1392             :  * @param hGeom handle on the geometry to get the binary size from.
    1393             :  * @return size of binary representation in bytes.
    1394             :  * @since GDAL 3.3
    1395             :  */
    1396             : 
    1397       10679 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
    1398             : 
    1399             : {
    1400       10679 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
    1401             : 
    1402       10679 :     return OGRGeometry::FromHandle(hGeom)->WkbSize();
    1403             : }
    1404             : 
    1405             : /**
    1406             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
    1407             :  *
    1408             :  * \brief Computes and returns the bounding envelope for this geometry
    1409             :  * in the passed psEnvelope structure.
    1410             :  *
    1411             :  * This method is the same as the C function OGR_G_GetEnvelope().
    1412             :  *
    1413             :  * @param psEnvelope the structure in which to place the results.
    1414             :  */
    1415             : 
    1416             : /************************************************************************/
    1417             : /*                         OGR_G_GetEnvelope()                          */
    1418             : /************************************************************************/
    1419             : /**
    1420             :  * \brief Computes and returns the bounding envelope for this geometry
    1421             :  * in the passed psEnvelope structure.
    1422             :  *
    1423             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1424             :  *
    1425             :  * @param hGeom handle of the geometry to get envelope from.
    1426             :  * @param psEnvelope the structure in which to place the results.
    1427             :  */
    1428             : 
    1429       13358 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
    1430             : 
    1431             : {
    1432       13358 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
    1433             : 
    1434       13358 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1435             : }
    1436             : 
    1437             : /**
    1438             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
    1439             :  *
    1440             :  * \brief Computes and returns the bounding envelope (3D) for this
    1441             :  * geometry in the passed psEnvelope structure.
    1442             :  *
    1443             :  * This method is the same as the C function OGR_G_GetEnvelope3D().
    1444             :  *
    1445             :  * @param psEnvelope the structure in which to place the results.
    1446             :  *
    1447             :  */
    1448             : 
    1449             : /************************************************************************/
    1450             : /*                        OGR_G_GetEnvelope3D()                         */
    1451             : /************************************************************************/
    1452             : /**
    1453             :  * \brief Computes and returns the bounding envelope (3D) for this
    1454             :  * geometry in the passed psEnvelope structure.
    1455             :  *
    1456             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1457             :  *
    1458             :  * @param hGeom handle of the geometry to get envelope from.
    1459             :  * @param psEnvelope the structure in which to place the results.
    1460             :  *
    1461             :  */
    1462             : 
    1463          10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
    1464             : 
    1465             : {
    1466          10 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
    1467             : 
    1468          10 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1469             : }
    1470             : 
    1471             : /************************************************************************/
    1472             : /*                           importFromWkb()                            */
    1473             : /************************************************************************/
    1474             : 
    1475             : /**
    1476             :  * \brief Assign geometry from well known binary data.
    1477             :  *
    1478             :  * The object must have already been instantiated as the correct derived
    1479             :  * type of geometry object to match the binaries type.  This method is used
    1480             :  * by the OGRGeometryFactory class, but not normally called by application
    1481             :  * code.
    1482             :  *
    1483             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1484             :  *
    1485             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1486             :  *
    1487             :  * @param pabyData the binary input data.
    1488             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1489             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1490             :  * done for curve geometries code
    1491             :  *
    1492             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1493             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1494             :  * OGRERR_CORRUPT_DATA may be returned.
    1495             :  */
    1496             : 
    1497         492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
    1498             :                                   OGRwkbVariant eWkbVariant)
    1499             : {
    1500         492 :     size_t nBytesConsumedOutIgnored = 0;
    1501         492 :     return importFromWkb(pabyData, nSize, eWkbVariant,
    1502         984 :                          nBytesConsumedOutIgnored);
    1503             : }
    1504             : 
    1505             : /**
    1506             :  * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
    1507             :  * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
    1508             :  *
    1509             :  * \brief Assign geometry from well known binary data.
    1510             :  *
    1511             :  * The object must have already been instantiated as the correct derived
    1512             :  * type of geometry object to match the binaries type.  This method is used
    1513             :  * by the OGRGeometryFactory class, but not normally called by application
    1514             :  * code.
    1515             :  *
    1516             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1517             :  *
    1518             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1519             :  *
    1520             :  * @param pabyData the binary input data.
    1521             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1522             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1523             :  * done for curve geometries code
    1524             :  * @param nBytesConsumedOut output parameter. Number of bytes consumed.
    1525             :  *
    1526             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1527             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1528             :  * OGRERR_CORRUPT_DATA may be returned.
    1529             :  *
    1530             :  */
    1531             : 
    1532             : /************************************************************************/
    1533             : /*                        OGR_G_ImportFromWkb()                         */
    1534             : /************************************************************************/
    1535             : /**
    1536             :  * \brief Assign geometry from well known binary data.
    1537             :  *
    1538             :  * The object must have already been instantiated as the correct derived
    1539             :  * type of geometry object to match the binaries type.
    1540             :  *
    1541             :  * This function relates to the SFCOM IWks::ImportFromWKB() method.
    1542             :  *
    1543             :  * This function is the same as the CPP method OGRGeometry::importFromWkb().
    1544             :  *
    1545             :  * @param hGeom handle on the geometry to assign the well know binary data to.
    1546             :  * @param pabyData the binary input data.
    1547             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1548             :  *
    1549             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1550             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1551             :  * OGRERR_CORRUPT_DATA may be returned.
    1552             :  */
    1553             : 
    1554           0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
    1555             : 
    1556             : {
    1557           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
    1558             : 
    1559           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkb(
    1560           0 :         static_cast<const GByte *>(pabyData), nSize);
    1561             : }
    1562             : 
    1563             : /************************************************************************/
    1564             : /*                      OGRGeometry::exportToWkb()                      */
    1565             : /************************************************************************/
    1566             : 
    1567             : /* clang-format off */
    1568             : /**
    1569             :  * \brief Convert a geometry into well known binary format.
    1570             :  *
    1571             :  * This method relates to the SFCOM IWks::ExportToWKB() method.
    1572             :  *
    1573             :  * This method is the same as the C function OGR_G_ExportToWkb() or
    1574             :  * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
    1575             :  *
    1576             :  * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1577             :  *               respectively.
    1578             :  * @param pabyData a buffer into which the binary representation is
    1579             :  *                      written.  This buffer must be at least
    1580             :  *                      OGRGeometry::WkbSize() byte in size.
    1581             :  * @param eWkbVariant What standard to use when exporting geometries
    1582             :  *                      with three dimensions (or more). The default
    1583             :  *                      wkbVariantOldOgc is the historical OGR
    1584             :  *                      variant. wkbVariantIso is the variant defined
    1585             :  *                      in ISO SQL/MM and adopted by OGC for SFSQL
    1586             :  *                      1.2.
    1587             :  *
    1588             :  * @return Currently OGRERR_NONE is always returned.
    1589             :  */
    1590             : /* clang-format on */
    1591             : 
    1592      275936 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
    1593             :                                 unsigned char *pabyData,
    1594             :                                 OGRwkbVariant eWkbVariant) const
    1595             : {
    1596      275936 :     OGRwkbExportOptions sOptions;
    1597      275936 :     sOptions.eByteOrder = eByteOrder;
    1598      275936 :     sOptions.eWkbVariant = eWkbVariant;
    1599      551872 :     return exportToWkb(pabyData, &sOptions);
    1600             : }
    1601             : 
    1602             : /************************************************************************/
    1603             : /*                         OGR_G_ExportToWkb()                          */
    1604             : /************************************************************************/
    1605             : /**
    1606             :  * \brief Convert a geometry well known binary format
    1607             :  *
    1608             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1609             :  *
    1610             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1611             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1612             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1613             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
    1614             :  *
    1615             :  * This function is the same as the CPP method
    1616             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
    1617             :  * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
    1618             :  *
    1619             :  * @param hGeom handle on the geometry to convert to a well know binary
    1620             :  * data from.
    1621             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1622             :  *               respectively.
    1623             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1624             :  *                      written.  This buffer must be at least
    1625             :  *                      OGR_G_WkbSize() byte in size.
    1626             :  *
    1627             :  * @return Currently OGRERR_NONE is always returned.
    1628             :  */
    1629             : 
    1630         109 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1631             :                          unsigned char *pabyDstBuffer)
    1632             : 
    1633             : {
    1634         109 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
    1635             : 
    1636         109 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
    1637             : }
    1638             : 
    1639             : /************************************************************************/
    1640             : /*                        OGR_G_ExportToIsoWkb()                        */
    1641             : /************************************************************************/
    1642             : /**
    1643             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
    1644             :  * binary format
    1645             :  *
    1646             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1647             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
    1648             :  * types.
    1649             :  *
    1650             :  * This function is the same as the CPP method
    1651             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
    1652             :  * with eWkbVariant = wkbVariantIso.
    1653             :  *
    1654             :  * @param hGeom handle on the geometry to convert to a well know binary
    1655             :  * data from.
    1656             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1657             :  *               respectively.
    1658             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1659             :  *                      written.  This buffer must be at least
    1660             :  *                      OGR_G_WkbSize() byte in size.
    1661             :  *
    1662             :  * @return Currently OGRERR_NONE is always returned.
    1663             :  *
    1664             :  */
    1665             : 
    1666       10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1667             :                             unsigned char *pabyDstBuffer)
    1668             : 
    1669             : {
    1670       10571 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
    1671             : 
    1672       10571 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
    1673       10571 :                                                        wkbVariantIso);
    1674             : }
    1675             : 
    1676             : /************************************************************************/
    1677             : /*                        OGR_G_ExportToWkbEx()                         */
    1678             : /************************************************************************/
    1679             : 
    1680             : /* clang-format off */
    1681             : /**
    1682             :  * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
    1683             :  *
    1684             :  * \brief Convert a geometry into well known binary format
    1685             :  *
    1686             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1687             :  *
    1688             :  * This function is the same as the C function OGR_G_ExportToWkbEx().
    1689             :  *
    1690             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1691             :  *                      written.  This buffer must be at least
    1692             :  *                      OGR_G_WkbSize() byte in size.
    1693             :  * @param psOptions WKB export options.
    1694             : 
    1695             :  * @return Currently OGRERR_NONE is always returned.
    1696             :  *
    1697             :  * @since GDAL 3.9
    1698             :  */
    1699             : /* clang-format on */
    1700             : 
    1701             : /**
    1702             :  * \brief Convert a geometry into well known binary format
    1703             :  *
    1704             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1705             :  *
    1706             :  * This function is the same as the CPP method
    1707             :  * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
    1708             :  *
    1709             :  * @param hGeom handle on the geometry to convert to a well know binary
    1710             :  * data from.
    1711             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1712             :  *                      written.  This buffer must be at least
    1713             :  *                      OGR_G_WkbSize() byte in size.
    1714             :  * @param psOptions WKB export options.
    1715             : 
    1716             :  * @return Currently OGRERR_NONE is always returned.
    1717             :  *
    1718             :  * @since GDAL 3.9
    1719             :  */
    1720             : 
    1721           2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
    1722             :                            const OGRwkbExportOptions *psOptions)
    1723             : {
    1724           2 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
    1725             : 
    1726           4 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
    1727           2 :                                                        psOptions);
    1728             : }
    1729             : 
    1730             : /**
    1731             :  * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
    1732             :  *
    1733             :  * \brief Assign geometry from well known text data.
    1734             :  *
    1735             :  * The object must have already been instantiated as the correct derived
    1736             :  * type of geometry object to match the text type.  This method is used
    1737             :  * by the OGRGeometryFactory class, but not normally called by application
    1738             :  * code.
    1739             :  *
    1740             :  * This method relates to the SFCOM IWks::ImportFromWKT() method.
    1741             :  *
    1742             :  * This method is the same as the C function OGR_G_ImportFromWkt().
    1743             :  *
    1744             :  * @param ppszInput pointer to a pointer to the source text.  The pointer is
    1745             :  *                    updated to pointer after the consumed text.
    1746             :  *
    1747             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1748             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1749             :  * OGRERR_CORRUPT_DATA may be returned.
    1750             :  */
    1751             : 
    1752             : /************************************************************************/
    1753             : /*                        OGR_G_ImportFromWkt()                         */
    1754             : /************************************************************************/
    1755             : /**
    1756             :  * \brief Assign geometry from well known text data.
    1757             :  *
    1758             :  * The object must have already been instantiated as the correct derived
    1759             :  * type of geometry object to match the text type.
    1760             :  *
    1761             :  * This function relates to the SFCOM IWks::ImportFromWKT() method.
    1762             :  *
    1763             :  * This function is the same as the CPP method OGRGeometry::importFromWkt().
    1764             :  *
    1765             :  * @param hGeom handle on the geometry to assign well know text data to.
    1766             :  * @param ppszSrcText pointer to a pointer to the source text.  The pointer is
    1767             :  *                    updated to pointer after the consumed text.
    1768             :  *
    1769             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1770             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1771             :  * OGRERR_CORRUPT_DATA may be returned.
    1772             :  */
    1773             : 
    1774           0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
    1775             : 
    1776             : {
    1777           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
    1778             : 
    1779           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkt(
    1780           0 :         const_cast<const char **>(ppszSrcText));
    1781             : }
    1782             : 
    1783             : /************************************************************************/
    1784             : /*                       importPreambleFromWkt()                        */
    1785             : /************************************************************************/
    1786             : 
    1787             : // Returns -1 if processing must continue.
    1788             : //! @cond Doxygen_Suppress
    1789      123642 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
    1790             :                                           int *pbHasM, bool *pbIsEmpty)
    1791             : {
    1792      123642 :     const char *pszInput = *ppszInput;
    1793             : 
    1794             :     /* -------------------------------------------------------------------- */
    1795             :     /*      Clear existing Geoms.                                           */
    1796             :     /* -------------------------------------------------------------------- */
    1797      123642 :     empty();
    1798      123642 :     *pbIsEmpty = false;
    1799             : 
    1800             :     /* -------------------------------------------------------------------- */
    1801             :     /*      Read and verify the type keyword, and ensure it matches the     */
    1802             :     /*      actual type of this container.                                  */
    1803             :     /* -------------------------------------------------------------------- */
    1804      123642 :     bool bHasM = false;
    1805      123642 :     bool bHasZ = false;
    1806      123642 :     bool bAlreadyGotDimension = false;
    1807             : 
    1808      123642 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
    1809      123642 :     pszInput = OGRWktReadToken(pszInput, szToken);
    1810      123642 :     if (szToken[0] != '\0')
    1811             :     {
    1812             :         // Postgis EWKT: POINTM instead of POINT M.
    1813             :         // Current QGIS versions (at least <= 3.38) also export POINTZ.
    1814      123642 :         const size_t nTokenLen = strlen(szToken);
    1815      123642 :         if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
    1816             :         {
    1817          11 :             szToken[nTokenLen - 1] = '\0';
    1818          11 :             bHasM = true;
    1819          11 :             bAlreadyGotDimension = true;
    1820             : 
    1821          11 :             if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
    1822           9 :                                   szToken[nTokenLen - 2] == 'z'))
    1823             :             {
    1824           4 :                 bHasZ = true;
    1825           4 :                 szToken[nTokenLen - 2] = '\0';
    1826             :             }
    1827             :         }
    1828      123631 :         else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
    1829             :         {
    1830           6 :             szToken[nTokenLen - 1] = '\0';
    1831           6 :             bHasZ = true;
    1832           6 :             bAlreadyGotDimension = true;
    1833             :         }
    1834             :     }
    1835             : 
    1836      123642 :     if (!EQUAL(szToken, getGeometryName()))
    1837           0 :         return OGRERR_CORRUPT_DATA;
    1838             : 
    1839             :     /* -------------------------------------------------------------------- */
    1840             :     /*      Check for Z, M or ZM                                            */
    1841             :     /* -------------------------------------------------------------------- */
    1842      123642 :     if (!bAlreadyGotDimension)
    1843             :     {
    1844      123625 :         const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1845      123625 :         if (EQUAL(szToken, "Z"))
    1846             :         {
    1847        1418 :             pszInput = pszNewInput;
    1848        1418 :             bHasZ = true;
    1849             :         }
    1850      122207 :         else if (EQUAL(szToken, "M"))
    1851             :         {
    1852         353 :             pszInput = pszNewInput;
    1853         353 :             bHasM = true;
    1854             :         }
    1855      121854 :         else if (EQUAL(szToken, "ZM"))
    1856             :         {
    1857         494 :             pszInput = pszNewInput;
    1858         494 :             bHasZ = true;
    1859         494 :             bHasM = true;
    1860             :         }
    1861             :     }
    1862      123642 :     *pbHasZ = bHasZ;
    1863      123642 :     *pbHasM = bHasM;
    1864             : 
    1865             :     /* -------------------------------------------------------------------- */
    1866             :     /*      Check for EMPTY ...                                             */
    1867             :     /* -------------------------------------------------------------------- */
    1868      123642 :     const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1869      123642 :     if (EQUAL(szToken, "EMPTY"))
    1870             :     {
    1871        1578 :         *ppszInput = pszNewInput;
    1872        1578 :         *pbIsEmpty = true;
    1873        1578 :         if (bHasZ)
    1874         137 :             set3D(TRUE);
    1875        1578 :         if (bHasM)
    1876          84 :             setMeasured(TRUE);
    1877        1578 :         return OGRERR_NONE;
    1878             :     }
    1879             : 
    1880      122064 :     if (!EQUAL(szToken, "("))
    1881          35 :         return OGRERR_CORRUPT_DATA;
    1882             : 
    1883      122029 :     if (!bHasZ && !bHasM)
    1884             :     {
    1885             :         // Test for old-style XXXXXXXXX(EMPTY).
    1886      119927 :         pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1887      119927 :         if (EQUAL(szToken, "EMPTY"))
    1888             :         {
    1889          69 :             pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1890             : 
    1891          69 :             if (EQUAL(szToken, ","))
    1892             :             {
    1893             :                 // This is OK according to SFSQL SPEC.
    1894             :             }
    1895          44 :             else if (!EQUAL(szToken, ")"))
    1896             :             {
    1897           9 :                 return OGRERR_CORRUPT_DATA;
    1898             :             }
    1899             :             else
    1900             :             {
    1901          35 :                 *ppszInput = pszNewInput;
    1902          35 :                 empty();
    1903          35 :                 *pbIsEmpty = true;
    1904          35 :                 return OGRERR_NONE;
    1905             :             }
    1906             :         }
    1907             :     }
    1908             : 
    1909      121985 :     *ppszInput = pszInput;
    1910             : 
    1911      121985 :     return OGRERR_NONE;
    1912             : }
    1913             : 
    1914             : //! @endcond
    1915             : 
    1916             : /************************************************************************/
    1917             : /*                           wktTypeString()                            */
    1918             : /************************************************************************/
    1919             : 
    1920             : //! @cond Doxygen_Suppress
    1921             : /** Get a type string for WKT, padded with a space at the end.
    1922             :  *
    1923             :  * @param variant  OGR type variant
    1924             :  * @return  "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
    1925             :  */
    1926       14243 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
    1927             : {
    1928       14243 :     std::string s(" ");
    1929             : 
    1930       14243 :     if (variant == wkbVariantIso)
    1931             :     {
    1932        9352 :         if (flags & OGR_G_3D)
    1933        1957 :             s += "Z";
    1934        9352 :         if (flags & OGR_G_MEASURED)
    1935        1200 :             s += "M";
    1936             :     }
    1937       14243 :     if (s.size() > 1)
    1938        2518 :         s += " ";
    1939       14243 :     return s;
    1940             : }
    1941             : 
    1942             : //! @endcond
    1943             : 
    1944             : /**
    1945             :  * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
    1946             :  * OGRwkbVariant variant = wkbVariantOldOgc ) const;
    1947             :  *
    1948             :  * \brief Convert a geometry into well known text format.
    1949             :  *
    1950             :  * This method relates to the SFCOM IWks::ExportToWKT() method.
    1951             :  *
    1952             :  * This method is the same as the C function OGR_G_ExportToWkt().
    1953             :  *
    1954             :  * @param ppszDstText a text buffer is allocated by the program, and assigned
    1955             :  *                    to the passed pointer. After use, *ppszDstText should be
    1956             :  *                    freed with CPLFree().
    1957             :  * @param variant the specification that must be conformed too :
    1958             :  *                    - wkbVariantOgc for old-style 99-402 extended
    1959             :  *                      dimension (Z) WKB types
    1960             :  *                    - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
    1961             :  *
    1962             :  * @return Currently OGRERR_NONE is always returned.
    1963             :  */
    1964        8879 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
    1965             : {
    1966        8879 :     OGRWktOptions opts;
    1967        8879 :     opts.variant = variant;
    1968        8879 :     OGRErr err(OGRERR_NONE);
    1969             : 
    1970        8879 :     std::string wkt = exportToWkt(opts, &err);
    1971        8879 :     *ppszDstText = CPLStrdup(wkt.data());
    1972       17758 :     return err;
    1973             : }
    1974             : 
    1975             : /************************************************************************/
    1976             : /*                         OGR_G_ExportToWkt()                          */
    1977             : /************************************************************************/
    1978             : 
    1979             : /**
    1980             :  * \brief Convert a geometry into well known text format.
    1981             :  *
    1982             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    1983             :  *
    1984             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1985             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1986             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1987             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
    1988             :  *
    1989             :  * This function is the same as the CPP method OGRGeometry::exportToWkt().
    1990             :  *
    1991             :  * @param hGeom handle on the geometry to convert to a text format from.
    1992             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    1993             :  *                    to the passed pointer. After use, *ppszDstText should be
    1994             :  *                    freed with CPLFree().
    1995             :  *
    1996             :  * @return Currently OGRERR_NONE is always returned.
    1997             :  */
    1998             : 
    1999        2551 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2000             : 
    2001             : {
    2002        2551 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
    2003             : 
    2004        2551 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
    2005             : }
    2006             : 
    2007             : /************************************************************************/
    2008             : /*                        OGR_G_ExportToIsoWkt()                        */
    2009             : /************************************************************************/
    2010             : 
    2011             : /**
    2012             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
    2013             :  * known text format.
    2014             :  *
    2015             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    2016             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
    2017             :  * (Z&M) WKB types.
    2018             :  *
    2019             :  * This function is the same as the CPP method
    2020             :  * OGRGeometry::exportToWkt(wkbVariantIso).
    2021             :  *
    2022             :  * @param hGeom handle on the geometry to convert to a text format from.
    2023             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    2024             :  *                    to the passed pointer. After use, *ppszDstText should be
    2025             :  *                    freed with CPLFree().
    2026             :  *
    2027             :  * @return Currently OGRERR_NONE is always returned.
    2028             :  *
    2029             :  */
    2030             : 
    2031        5500 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2032             : 
    2033             : {
    2034        5500 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
    2035             : 
    2036        5500 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
    2037        5500 :                                                        wkbVariantIso);
    2038             : }
    2039             : 
    2040             : /**
    2041             :  * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
    2042             :  *
    2043             :  * \brief Fetch geometry type.
    2044             :  *
    2045             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2046             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2047             :  * to the return result.
    2048             :  *
    2049             :  * This method is the same as the C function OGR_G_GetGeometryType().
    2050             :  *
    2051             :  * @return the geometry type code.
    2052             :  */
    2053             : 
    2054             : /************************************************************************/
    2055             : /*                       OGR_G_GetGeometryType()                        */
    2056             : /************************************************************************/
    2057             : /**
    2058             :  * \brief Fetch geometry type.
    2059             :  *
    2060             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2061             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2062             :  * to the return result.
    2063             :  *
    2064             :  * This function is the same as the CPP method OGRGeometry::getGeometryType().
    2065             :  *
    2066             :  * @param hGeom handle on the geometry to get type from.
    2067             :  * @return the geometry type code.
    2068             :  */
    2069             : 
    2070        5789 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
    2071             : 
    2072             : {
    2073        5789 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
    2074             : 
    2075        5789 :     return OGRGeometry::FromHandle(hGeom)->getGeometryType();
    2076             : }
    2077             : 
    2078             : /**
    2079             :  * \fn const char * OGRGeometry::getGeometryName() const;
    2080             :  *
    2081             :  * \brief Fetch WKT name for geometry type.
    2082             :  *
    2083             :  * There is no SFCOM analog to this method.
    2084             :  *
    2085             :  * This method is the same as the C function OGR_G_GetGeometryName().
    2086             :  *
    2087             :  * @return name used for this geometry type in well known text format.  The
    2088             :  * returned pointer is to a static internal string and should not be modified
    2089             :  * or freed.
    2090             :  */
    2091             : 
    2092             : /************************************************************************/
    2093             : /*                       OGR_G_GetGeometryName()                        */
    2094             : /************************************************************************/
    2095             : /**
    2096             :  * \brief Fetch WKT name for geometry type.
    2097             :  *
    2098             :  * There is no SFCOM analog to this function.
    2099             :  *
    2100             :  * This function is the same as the CPP method OGRGeometry::getGeometryName().
    2101             :  *
    2102             :  * @param hGeom handle on the geometry to get name from.
    2103             :  * @return name used for this geometry type in well known text format.
    2104             :  */
    2105             : 
    2106       18986 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
    2107             : 
    2108             : {
    2109       18986 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
    2110             : 
    2111       18986 :     return OGRGeometry::FromHandle(hGeom)->getGeometryName();
    2112             : }
    2113             : 
    2114             : /**
    2115             :  * \fn OGRGeometry *OGRGeometry::clone() const;
    2116             :  *
    2117             :  * \brief Make a copy of this object.
    2118             :  *
    2119             :  * This method relates to the SFCOM IGeometry::clone() method.
    2120             :  *
    2121             :  * This method is the same as the C function OGR_G_Clone().
    2122             :  *
    2123             :  * @return a new object instance with the same geometry, and spatial
    2124             :  * reference system as the original.
    2125             :  */
    2126             : 
    2127             : /************************************************************************/
    2128             : /*                            OGR_G_Clone()                             */
    2129             : /************************************************************************/
    2130             : /**
    2131             :  * \brief Make a copy of this object.
    2132             :  *
    2133             :  * This function relates to the SFCOM IGeometry::clone() method.
    2134             :  *
    2135             :  * This function is the same as the CPP method OGRGeometry::clone().
    2136             :  *
    2137             :  * @param hGeom handle on the geometry to clone from.
    2138             :  * @return a handle on the copy of the geometry with the spatial
    2139             :  * reference system as the original.
    2140             :  */
    2141             : 
    2142       13574 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
    2143             : 
    2144             : {
    2145       13574 :     VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
    2146             : 
    2147       13574 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
    2148             : }
    2149             : 
    2150             : /**
    2151             :  * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
    2152             :  *
    2153             :  * \brief Returns spatial reference system for object.
    2154             :  *
    2155             :  * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
    2156             :  *
    2157             :  * This method is the same as the C function OGR_G_GetSpatialReference().
    2158             :  *
    2159             :  * @return a reference to the spatial reference object.  The object may be
    2160             :  * shared with many geometry objects, and should not be modified.
    2161             :  */
    2162             : 
    2163             : /************************************************************************/
    2164             : /*                     OGR_G_GetSpatialReference()                      */
    2165             : /************************************************************************/
    2166             : /**
    2167             :  * \brief Returns spatial reference system for geometry.
    2168             :  *
    2169             :  * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
    2170             :  *
    2171             :  * This function is the same as the CPP method
    2172             :  * OGRGeometry::getSpatialReference().
    2173             :  *
    2174             :  * @param hGeom handle on the geometry to get spatial reference from.
    2175             :  * @return a reference to the spatial reference geometry, which should not be
    2176             :  * modified.
    2177             :  */
    2178             : 
    2179         126 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
    2180             : 
    2181             : {
    2182         126 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
    2183             : 
    2184         126 :     return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
    2185         252 :         OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
    2186             : }
    2187             : 
    2188             : /**
    2189             :  * \fn void OGRGeometry::empty();
    2190             :  *
    2191             :  * \brief Clear geometry information.
    2192             :  * This restores the geometry to its initial
    2193             :  * state after construction, and before assignment of actual geometry.
    2194             :  *
    2195             :  * This method relates to the SFCOM IGeometry::Empty() method.
    2196             :  *
    2197             :  * This method is the same as the C function OGR_G_Empty().
    2198             :  */
    2199             : 
    2200             : /************************************************************************/
    2201             : /*                            OGR_G_Empty()                             */
    2202             : /************************************************************************/
    2203             : /**
    2204             :  * \brief Clear geometry information.
    2205             :  * This restores the geometry to its initial
    2206             :  * state after construction, and before assignment of actual geometry.
    2207             :  *
    2208             :  * This function relates to the SFCOM IGeometry::Empty() method.
    2209             :  *
    2210             :  * This function is the same as the CPP method OGRGeometry::empty().
    2211             :  *
    2212             :  * @param hGeom handle on the geometry to empty.
    2213             :  */
    2214             : 
    2215           4 : void OGR_G_Empty(OGRGeometryH hGeom)
    2216             : 
    2217             : {
    2218           4 :     VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
    2219             : 
    2220           4 :     OGRGeometry::FromHandle(hGeom)->empty();
    2221             : }
    2222             : 
    2223             : /**
    2224             :  * \fn bool OGRGeometry::IsEmpty() const;
    2225             :  *
    2226             :  * \brief Returns TRUE (non-zero) if the object has no points.
    2227             :  *
    2228             :  * Normally this
    2229             :  * returns FALSE except between when an object is instantiated and points
    2230             :  * have been assigned.
    2231             :  *
    2232             :  * This method relates to the SFCOM IGeometry::IsEmpty() method.
    2233             :  *
    2234             :  * @return TRUE if object is empty, otherwise FALSE.
    2235             :  */
    2236             : 
    2237             : /************************************************************************/
    2238             : /*                           OGR_G_IsEmpty()                            */
    2239             : /************************************************************************/
    2240             : 
    2241             : /**
    2242             :  * \brief Test if the geometry is empty.
    2243             :  *
    2244             :  * This method is the same as the CPP method OGRGeometry::IsEmpty().
    2245             :  *
    2246             :  * @param hGeom The Geometry to test.
    2247             :  *
    2248             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2249             :  */
    2250             : 
    2251        2391 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
    2252             : 
    2253             : {
    2254        2391 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
    2255             : 
    2256        2391 :     return OGRGeometry::FromHandle(hGeom)->IsEmpty();
    2257             : }
    2258             : 
    2259             : /************************************************************************/
    2260             : /*                              IsValid()                               */
    2261             : /************************************************************************/
    2262             : 
    2263             : /**
    2264             :  * \brief Test if the geometry is valid.
    2265             :  *
    2266             :  * This method is the same as the C functions OGR_G_IsValid() and
    2267             :  * OGR_G_GetInvalidityReason().
    2268             :  *
    2269             :  * This method is built on the GEOS library, check it for the definition
    2270             :  * of the geometry operation.
    2271             :  * If OGR is built without the GEOS library, this method will always return
    2272             :  * FALSE.
    2273             :  *
    2274             :  * @param[out] posReason (since 3.13) Pointer to a string to receive the reason
    2275             :  *                       for invalidity, or nullptr. When nullptr, invalidity
    2276             :  *                       reasons are emitted as CPL warnings.
    2277             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2278             :  */
    2279             : 
    2280        2606 : bool OGRGeometry::IsValid(std::string *posReason) const
    2281             : 
    2282             : {
    2283        2606 :     if (posReason)
    2284         833 :         posReason->clear();
    2285             : 
    2286        2606 :     if (IsSFCGALCompatible())
    2287             :     {
    2288             : #ifndef HAVE_SFCGAL
    2289             : 
    2290             : #ifdef HAVE_GEOS
    2291           3 :         if (wkbFlatten(getGeometryType()) == wkbTriangle)
    2292             :         {
    2293             :             // go on
    2294             :         }
    2295             :         else
    2296             : #endif
    2297             :         {
    2298           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2299             :                      "SFCGAL support not enabled.");
    2300           1 :             return FALSE;
    2301             :         }
    2302             : #else
    2303             :         sfcgal_init();
    2304             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    2305             :         if (poThis == nullptr)
    2306             :         {
    2307             :             CPLError(CE_Failure, CPLE_IllegalArg,
    2308             :                      "SFCGAL geometry returned is NULL");
    2309             :             return FALSE;
    2310             :         }
    2311             : 
    2312             :         const int res = sfcgal_geometry_is_valid(poThis);
    2313             :         if (res != 1 && posReason)
    2314             :         {
    2315             :             char *pszReason = nullptr;
    2316             :             sfcgal_geometry_is_valid_detail(poThis, &pszReason, nullptr);
    2317             :             if (pszReason)
    2318             :             {
    2319             :                 *posReason = pszReason;
    2320             :                 free(pszReason);
    2321             :             }
    2322             :             else
    2323             :                 *posReason = "unknown reason";
    2324             :         }
    2325             :         sfcgal_geometry_delete(poThis);
    2326             :         return res == 1;
    2327             : #endif
    2328             :     }
    2329             : 
    2330             :     {
    2331             : #ifndef HAVE_GEOS
    2332             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2333             :         return FALSE;
    2334             : 
    2335             : #else
    2336        2605 :         bool bResult = false;
    2337             : 
    2338             :         // Some invalid geometries, such as lines with one point, or
    2339             :         // rings that do not close, cannot be converted to GEOS.
    2340             :         // For validity checking we initialize the GEOS context with
    2341             :         // the warning handler as the error handler to avoid emitting
    2342             :         // CE_Failure when a geometry cannot be converted to GEOS.
    2343             :         GEOSContextHandle_t hGEOSCtxt =
    2344        2605 :             initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
    2345             : 
    2346             :         GEOSGeom hThisGeosGeom;
    2347        2605 :         if (posReason)
    2348             :         {
    2349        1666 :             CPLErrorAccumulator oAccumulator;
    2350             :             {
    2351         833 :                 auto oContext = oAccumulator.InstallForCurrentScope();
    2352         833 :                 CPL_IGNORE_RET_VAL(oContext);
    2353         833 :                 hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2354             :             }
    2355         833 :             if (!hThisGeosGeom && oAccumulator.GetErrors().size() == 1)
    2356             :             {
    2357           2 :                 std::string msg = oAccumulator.GetErrors()[0].msg;
    2358             : 
    2359             :                 // Trim GEOS exception name
    2360           1 :                 const auto subMsgPos = msg.find(": ");
    2361           1 :                 if (subMsgPos != std::string::npos)
    2362             :                 {
    2363           1 :                     msg = msg.substr(subMsgPos + strlen(": "));
    2364             :                 }
    2365             : 
    2366             :                 // Trim newline from end of GEOS exception message
    2367           1 :                 if (!msg.empty() && msg.back() == '\n')
    2368             :                 {
    2369           1 :                     msg.pop_back();
    2370             :                 }
    2371             : 
    2372           1 :                 *posReason = std::move(msg);
    2373             :             }
    2374             :         }
    2375             :         else
    2376             :         {
    2377        1772 :             hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2378             :         }
    2379             : 
    2380        2605 :         if (hThisGeosGeom != nullptr)
    2381             :         {
    2382        2604 :             if (posReason)
    2383             :             {
    2384        1664 :                 CPLErrorAccumulator oAccumulator;
    2385             :                 {
    2386         832 :                     auto oContext = oAccumulator.InstallForCurrentScope();
    2387         832 :                     CPL_IGNORE_RET_VAL(oContext);
    2388         832 :                     bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2389             :                 }
    2390         832 :                 if (!bResult && oAccumulator.GetErrors().size() == 1)
    2391             :                 {
    2392          27 :                     *posReason = oAccumulator.GetErrors()[0].msg;
    2393             :                 }
    2394             :             }
    2395             :             else
    2396             :             {
    2397        1772 :                 bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2398             :             }
    2399             : #ifdef DEBUG_VERBOSE
    2400             :             if (!bResult && !posReason)
    2401             :             {
    2402             :                 char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
    2403             :                 CPLDebug("OGR", "%s", pszReason);
    2404             :                 GEOSFree_r(hGEOSCtxt, pszReason);
    2405             :             }
    2406             : #endif
    2407        2604 :             GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2408             :         }
    2409        2605 :         freeGEOSContext(hGEOSCtxt);
    2410             : 
    2411        2605 :         return bResult;
    2412             : 
    2413             : #endif  // HAVE_GEOS
    2414             :     }
    2415             : }
    2416             : 
    2417             : /************************************************************************/
    2418             : /*                           OGR_G_IsValid()                            */
    2419             : /************************************************************************/
    2420             : 
    2421             : /**
    2422             :  * \brief Test if the geometry is valid.
    2423             :  *
    2424             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2425             :  *
    2426             :  * This function is built on the GEOS library, check it for the definition
    2427             :  * of the geometry operation.
    2428             :  * If OGR is built without the GEOS library, this function will always return
    2429             :  * FALSE.
    2430             :  *
    2431             :  * If the geometry is invalid, the reason for its invalidity is emitted as a
    2432             :  * CPL warning. To get it in a string instead, use OGR_G_GetInvalidityReason()
    2433             :  *
    2434             :  * @param hGeom The Geometry to test.
    2435             :  *
    2436             :  * @return TRUE if the geometry is valid, otherwise FALSE.
    2437             :  */
    2438             : 
    2439          22 : int OGR_G_IsValid(OGRGeometryH hGeom)
    2440             : 
    2441             : {
    2442          22 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
    2443             : 
    2444          22 :     return OGRGeometry::FromHandle(hGeom)->IsValid();
    2445             : }
    2446             : 
    2447             : /************************************************************************/
    2448             : /*                     OGR_G_GetInvalidityReason()                      */
    2449             : /************************************************************************/
    2450             : 
    2451             : /**
    2452             :  * \brief Test if the geometry is valid and, if not, return the invalidity reason.
    2453             :  *
    2454             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2455             :  *
    2456             :  * This function is built on the GEOS library, check it for the definition
    2457             :  * of the geometry operation.
    2458             :  * If OGR is built without the GEOS library, this function will always return
    2459             :  * FALSE.
    2460             :  *
    2461             :  * @param hGeom The Geometry to test.
    2462             :  * @return a string with the invalidity reason, to free with CPLFree(),
    2463             :  * if the geometry is invalid, or nullptr if the geometry is valid.
    2464             :  *
    2465             :  * @since 3.13
    2466             :  */
    2467             : 
    2468           3 : char *OGR_G_GetInvalidityReason(OGRGeometryH hGeom)
    2469             : 
    2470             : {
    2471           3 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetInvalidityReason", nullptr);
    2472             : 
    2473           6 :     std::string osReason;
    2474           3 :     const int nRet = OGRGeometry::FromHandle(hGeom)->IsValid(&osReason);
    2475           3 :     if (osReason.empty())
    2476             :     {
    2477           1 :         if (!nRet)
    2478             :         {
    2479             :             // not sure if that can happen
    2480           0 :             return CPLStrdup("unknown reason");
    2481             :         }
    2482             :         else
    2483           1 :             return nullptr;
    2484             :     }
    2485             :     else
    2486             :     {
    2487           2 :         return CPLStrdup(osReason.c_str());
    2488             :     }
    2489             : }
    2490             : 
    2491             : /************************************************************************/
    2492             : /*                              IsSimple()                              */
    2493             : /************************************************************************/
    2494             : 
    2495             : /**
    2496             :  * \brief Test if the geometry is simple.
    2497             :  *
    2498             :  * This method is the same as the C function OGR_G_IsSimple().
    2499             :  *
    2500             :  * This method is built on the GEOS library, check it for the definition
    2501             :  * of the geometry operation.
    2502             :  * If OGR is built without the GEOS library, this method will always return
    2503             :  * FALSE.
    2504             :  *
    2505             :  *
    2506             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2507             :  */
    2508             : 
    2509           5 : bool OGRGeometry::IsSimple() const
    2510             : 
    2511             : {
    2512             : #ifndef HAVE_GEOS
    2513             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2514             :     return FALSE;
    2515             : 
    2516             : #else
    2517             : 
    2518           5 :     bool bResult = false;
    2519             : 
    2520           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2521           5 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2522             : 
    2523           5 :     if (hThisGeosGeom != nullptr)
    2524             :     {
    2525           5 :         bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2526           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2527             :     }
    2528           5 :     freeGEOSContext(hGEOSCtxt);
    2529             : 
    2530           5 :     return bResult;
    2531             : 
    2532             : #endif  // HAVE_GEOS
    2533             : }
    2534             : 
    2535             : /**
    2536             :  * \brief Returns TRUE if the geometry is simple.
    2537             :  *
    2538             :  * Returns TRUE if the geometry has no anomalous geometric points, such
    2539             :  * as self intersection or self tangency. The description of each
    2540             :  * instantiable geometric class will include the specific conditions that
    2541             :  * cause an instance of that class to be classified as not simple.
    2542             :  *
    2543             :  * This function is the same as the C++ method OGRGeometry::IsSimple() method.
    2544             :  *
    2545             :  * If OGR is built without the GEOS library, this function will always return
    2546             :  * FALSE.
    2547             :  *
    2548             :  * @param hGeom The Geometry to test.
    2549             :  *
    2550             :  * @return TRUE if object is simple, otherwise FALSE.
    2551             :  */
    2552             : 
    2553           5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
    2554             : 
    2555             : {
    2556           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
    2557             : 
    2558           5 :     return OGRGeometry::FromHandle(hGeom)->IsSimple();
    2559             : }
    2560             : 
    2561             : /************************************************************************/
    2562             : /*                               IsRing()                               */
    2563             : /************************************************************************/
    2564             : 
    2565             : /**
    2566             :  * \brief Test if the geometry is a ring
    2567             :  *
    2568             :  * This method is the same as the C function OGR_G_IsRing().
    2569             :  *
    2570             :  * This method is built on the GEOS library, check it for the definition
    2571             :  * of the geometry operation.
    2572             :  * If OGR is built without the GEOS library, this method will always return
    2573             :  * FALSE.
    2574             :  *
    2575             :  *
    2576             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2577             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2578             :  */
    2579             : 
    2580           1 : bool OGRGeometry::IsRing() const
    2581             : 
    2582             : {
    2583             : #ifndef HAVE_GEOS
    2584             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2585             :     return FALSE;
    2586             : 
    2587             : #else
    2588             : 
    2589           1 :     bool bResult = false;
    2590             : 
    2591           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2592           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2593             : 
    2594           1 :     if (hThisGeosGeom != nullptr)
    2595             :     {
    2596           1 :         bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2597           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2598             :     }
    2599           1 :     freeGEOSContext(hGEOSCtxt);
    2600             : 
    2601           1 :     return bResult;
    2602             : 
    2603             : #endif  // HAVE_GEOS
    2604             : }
    2605             : 
    2606             : /************************************************************************/
    2607             : /*                            OGR_G_IsRing()                            */
    2608             : /************************************************************************/
    2609             : 
    2610             : /**
    2611             :  * \brief Test if the geometry is a ring
    2612             :  *
    2613             :  * This function is the same as the C++ method OGRGeometry::IsRing().
    2614             :  *
    2615             :  * This function is built on the GEOS library, check it for the definition
    2616             :  * of the geometry operation.
    2617             :  * If OGR is built without the GEOS library, this function will always return
    2618             :  * FALSE.
    2619             :  *
    2620             :  * @param hGeom The Geometry to test.
    2621             :  *
    2622             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2623             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2624             :  */
    2625             : 
    2626           1 : int OGR_G_IsRing(OGRGeometryH hGeom)
    2627             : 
    2628             : {
    2629           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
    2630             : 
    2631           1 :     return OGRGeometry::FromHandle(hGeom)->IsRing();
    2632             : }
    2633             : 
    2634             : /************************************************************************/
    2635             : /*                         OGRFromOGCGeomType()                         */
    2636             : /************************************************************************/
    2637             : 
    2638             : /** Map OGC geometry format type to corresponding OGR constants.
    2639             :  * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
    2640             :  * @return OGR constant.
    2641             :  */
    2642        3219 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
    2643             : {
    2644        3219 :     OGRwkbGeometryType eType = wkbUnknown;
    2645        3219 :     bool bConvertTo3D = false;
    2646        3219 :     bool bIsMeasured = false;
    2647        3219 :     if (*pszGeomType != '\0')
    2648             :     {
    2649        3213 :         char ch = pszGeomType[strlen(pszGeomType) - 1];
    2650        3213 :         if (ch == 'm' || ch == 'M')
    2651             :         {
    2652           2 :             bIsMeasured = true;
    2653           2 :             if (strlen(pszGeomType) > 1)
    2654           2 :                 ch = pszGeomType[strlen(pszGeomType) - 2];
    2655             :         }
    2656        3213 :         if (ch == 'z' || ch == 'Z')
    2657             :         {
    2658          34 :             bConvertTo3D = true;
    2659             :         }
    2660             :     }
    2661             : 
    2662        3219 :     if (STARTS_WITH_CI(pszGeomType, "POINT"))
    2663         971 :         eType = wkbPoint;
    2664        2248 :     else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
    2665         184 :         eType = wkbLineString;
    2666        2064 :     else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
    2667         895 :         eType = wkbPolygon;
    2668        1169 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
    2669          24 :         eType = wkbMultiPoint;
    2670        1145 :     else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
    2671          13 :         eType = wkbMultiLineString;
    2672        1132 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
    2673          82 :         eType = wkbMultiPolygon;
    2674        1050 :     else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
    2675           4 :         eType = wkbGeometryCollection;
    2676        1046 :     else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
    2677         356 :         eType = wkbCircularString;
    2678         690 :     else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
    2679           0 :         eType = wkbCompoundCurve;
    2680         690 :     else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
    2681          16 :         eType = wkbCurvePolygon;
    2682         674 :     else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
    2683           2 :         eType = wkbMultiCurve;
    2684         672 :     else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
    2685           0 :         eType = wkbMultiSurface;
    2686         672 :     else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
    2687           0 :         eType = wkbTriangle;
    2688         672 :     else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
    2689           1 :         eType = wkbPolyhedralSurface;
    2690         671 :     else if (STARTS_WITH_CI(pszGeomType, "TIN"))
    2691           5 :         eType = wkbTIN;
    2692         666 :     else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
    2693           3 :         eType = wkbCurve;
    2694         663 :     else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
    2695           3 :         eType = wkbSurface;
    2696             :     else
    2697         660 :         eType = wkbUnknown;
    2698             : 
    2699        3219 :     if (bConvertTo3D)
    2700          34 :         eType = wkbSetZ(eType);
    2701        3219 :     if (bIsMeasured)
    2702           2 :         eType = wkbSetM(eType);
    2703             : 
    2704        3219 :     return eType;
    2705             : }
    2706             : 
    2707             : /************************************************************************/
    2708             : /*                          OGRToOGCGeomType()                          */
    2709             : /************************************************************************/
    2710             : 
    2711             : /** Map OGR geometry format constants to corresponding OGC geometry type.
    2712             :  * @param eGeomType OGR geometry type
    2713             :  * @param bCamelCase Whether the return should be like "MultiPoint"
    2714             :  *        (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
    2715             :  * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
    2716             :  *               Default is false.
    2717             :  * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
    2718             :  *                       suffix. Default is false.
    2719             :  * @return string with OGC geometry type (without dimensionality)
    2720             :  */
    2721        3119 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
    2722             :                              bool bAddZM, bool bSpaceBeforeZM)
    2723             : {
    2724        3119 :     const char *pszRet = "";
    2725        3119 :     switch (wkbFlatten(eGeomType))
    2726             :     {
    2727        1699 :         case wkbUnknown:
    2728        1699 :             pszRet = "Geometry";
    2729        1699 :             break;
    2730         560 :         case wkbPoint:
    2731         560 :             pszRet = "Point";
    2732         560 :             break;
    2733         153 :         case wkbLineString:
    2734         153 :             pszRet = "LineString";
    2735         153 :             break;
    2736         372 :         case wkbPolygon:
    2737         372 :             pszRet = "Polygon";
    2738         372 :             break;
    2739          48 :         case wkbMultiPoint:
    2740          48 :             pszRet = "MultiPoint";
    2741          48 :             break;
    2742          56 :         case wkbMultiLineString:
    2743          56 :             pszRet = "MultiLineString";
    2744          56 :             break;
    2745          74 :         case wkbMultiPolygon:
    2746          74 :             pszRet = "MultiPolygon";
    2747          74 :             break;
    2748          54 :         case wkbGeometryCollection:
    2749          54 :             pszRet = "GeometryCollection";
    2750          54 :             break;
    2751           9 :         case wkbCircularString:
    2752           9 :             pszRet = "CircularString";
    2753           9 :             break;
    2754           3 :         case wkbCompoundCurve:
    2755           3 :             pszRet = "CompoundCurve";
    2756           3 :             break;
    2757          12 :         case wkbCurvePolygon:
    2758          12 :             pszRet = "CurvePolygon";
    2759          12 :             break;
    2760           2 :         case wkbMultiCurve:
    2761           2 :             pszRet = "MultiCurve";
    2762           2 :             break;
    2763           3 :         case wkbMultiSurface:
    2764           3 :             pszRet = "MultiSurface";
    2765           3 :             break;
    2766           3 :         case wkbTriangle:
    2767           3 :             pszRet = "Triangle";
    2768           3 :             break;
    2769           5 :         case wkbPolyhedralSurface:
    2770           5 :             pszRet = "PolyhedralSurface";
    2771           5 :             break;
    2772           1 :         case wkbTIN:
    2773           1 :             pszRet = "Tin";
    2774           1 :             break;
    2775           3 :         case wkbCurve:
    2776           3 :             pszRet = "Curve";
    2777           3 :             break;
    2778           3 :         case wkbSurface:
    2779           3 :             pszRet = "Surface";
    2780           3 :             break;
    2781          59 :         default:
    2782          59 :             break;
    2783             :     }
    2784        3119 :     if (bAddZM)
    2785             :     {
    2786          67 :         const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    2787          67 :         const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    2788          67 :         if (bHasZ || bHasM)
    2789             :         {
    2790          10 :             if (bSpaceBeforeZM)
    2791           1 :                 pszRet = CPLSPrintf("%s ", pszRet);
    2792          10 :             if (bHasZ)
    2793           9 :                 pszRet = CPLSPrintf("%sZ", pszRet);
    2794          10 :             if (bHasM)
    2795           5 :                 pszRet = CPLSPrintf("%sM", pszRet);
    2796             :         }
    2797             :     }
    2798        3119 :     if (!bCamelCase)
    2799        3051 :         pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
    2800        3119 :     return pszRet;
    2801             : }
    2802             : 
    2803             : /************************************************************************/
    2804             : /*                       OGRGeometryTypeToName()                        */
    2805             : /************************************************************************/
    2806             : 
    2807             : /**
    2808             :  * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
    2809             :  * value.  The returned value should not be modified, or freed by the
    2810             :  * application.
    2811             :  *
    2812             :  * This function is C callable.
    2813             :  *
    2814             :  * @param eType the geometry type.
    2815             :  *
    2816             :  * @return internal human readable string, or NULL on failure.
    2817             :  */
    2818             : 
    2819         393 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
    2820             : 
    2821             : {
    2822         393 :     bool b3D = wkbHasZ(eType);
    2823         393 :     bool bMeasured = wkbHasM(eType);
    2824             : 
    2825         393 :     switch (wkbFlatten(eType))
    2826             :     {
    2827          34 :         case wkbUnknown:
    2828          34 :             if (b3D && bMeasured)
    2829           0 :                 return "3D Measured Unknown (any)";
    2830          34 :             else if (b3D)
    2831           1 :                 return "3D Unknown (any)";
    2832          33 :             else if (bMeasured)
    2833           0 :                 return "Measured Unknown (any)";
    2834             :             else
    2835          33 :                 return "Unknown (any)";
    2836             : 
    2837          59 :         case wkbPoint:
    2838          59 :             if (b3D && bMeasured)
    2839           3 :                 return "3D Measured Point";
    2840          56 :             else if (b3D)
    2841          12 :                 return "3D Point";
    2842          44 :             else if (bMeasured)
    2843           5 :                 return "Measured Point";
    2844             :             else
    2845          39 :                 return "Point";
    2846             : 
    2847          37 :         case wkbLineString:
    2848          37 :             if (b3D && bMeasured)
    2849           0 :                 return "3D Measured Line String";
    2850          37 :             else if (b3D)
    2851           9 :                 return "3D Line String";
    2852          28 :             else if (bMeasured)
    2853           0 :                 return "Measured Line String";
    2854             :             else
    2855          28 :                 return "Line String";
    2856             : 
    2857          62 :         case wkbPolygon:
    2858          62 :             if (b3D && bMeasured)
    2859           0 :                 return "3D Measured Polygon";
    2860          62 :             else if (b3D)
    2861           8 :                 return "3D Polygon";
    2862          54 :             else if (bMeasured)
    2863           0 :                 return "Measured Polygon";
    2864             :             else
    2865          54 :                 return "Polygon";
    2866             : 
    2867          19 :         case wkbMultiPoint:
    2868          19 :             if (b3D && bMeasured)
    2869           0 :                 return "3D Measured Multi Point";
    2870          19 :             else if (b3D)
    2871           9 :                 return "3D Multi Point";
    2872          10 :             else if (bMeasured)
    2873           0 :                 return "Measured Multi Point";
    2874             :             else
    2875          10 :                 return "Multi Point";
    2876             : 
    2877          14 :         case wkbMultiLineString:
    2878          14 :             if (b3D && bMeasured)
    2879           0 :                 return "3D Measured Multi Line String";
    2880          14 :             else if (b3D)
    2881           6 :                 return "3D Multi Line String";
    2882           8 :             else if (bMeasured)
    2883           0 :                 return "Measured Multi Line String";
    2884             :             else
    2885           8 :                 return "Multi Line String";
    2886             : 
    2887          19 :         case wkbMultiPolygon:
    2888          19 :             if (b3D && bMeasured)
    2889           0 :                 return "3D Measured Multi Polygon";
    2890          19 :             else if (b3D)
    2891           8 :                 return "3D Multi Polygon";
    2892          11 :             else if (bMeasured)
    2893           0 :                 return "Measured Multi Polygon";
    2894             :             else
    2895          11 :                 return "Multi Polygon";
    2896             : 
    2897          25 :         case wkbGeometryCollection:
    2898          25 :             if (b3D && bMeasured)
    2899           0 :                 return "3D Measured Geometry Collection";
    2900          25 :             else if (b3D)
    2901          10 :                 return "3D Geometry Collection";
    2902          15 :             else if (bMeasured)
    2903           0 :                 return "Measured Geometry Collection";
    2904             :             else
    2905          15 :                 return "Geometry Collection";
    2906             : 
    2907           0 :         case wkbCircularString:
    2908           0 :             if (b3D && bMeasured)
    2909           0 :                 return "3D Measured Circular String";
    2910           0 :             else if (b3D)
    2911           0 :                 return "3D Circular String";
    2912           0 :             else if (bMeasured)
    2913           0 :                 return "Measured Circular String";
    2914             :             else
    2915           0 :                 return "Circular String";
    2916             : 
    2917           1 :         case wkbCompoundCurve:
    2918           1 :             if (b3D && bMeasured)
    2919           0 :                 return "3D Measured Compound Curve";
    2920           1 :             else if (b3D)
    2921           0 :                 return "3D Compound Curve";
    2922           1 :             else if (bMeasured)
    2923           0 :                 return "Measured Compound Curve";
    2924             :             else
    2925           1 :                 return "Compound Curve";
    2926             : 
    2927           0 :         case wkbCurvePolygon:
    2928           0 :             if (b3D && bMeasured)
    2929           0 :                 return "3D Measured Curve Polygon";
    2930           0 :             else if (b3D)
    2931           0 :                 return "3D Curve Polygon";
    2932           0 :             else if (bMeasured)
    2933           0 :                 return "Measured Curve Polygon";
    2934             :             else
    2935           0 :                 return "Curve Polygon";
    2936             : 
    2937           0 :         case wkbMultiCurve:
    2938           0 :             if (b3D && bMeasured)
    2939           0 :                 return "3D Measured Multi Curve";
    2940           0 :             else if (b3D)
    2941           0 :                 return "3D Multi Curve";
    2942           0 :             else if (bMeasured)
    2943           0 :                 return "Measured Multi Curve";
    2944             :             else
    2945           0 :                 return "Multi Curve";
    2946             : 
    2947           0 :         case wkbMultiSurface:
    2948           0 :             if (b3D && bMeasured)
    2949           0 :                 return "3D Measured Multi Surface";
    2950           0 :             else if (b3D)
    2951           0 :                 return "3D Multi Surface";
    2952           0 :             else if (bMeasured)
    2953           0 :                 return "Measured Multi Surface";
    2954             :             else
    2955           0 :                 return "Multi Surface";
    2956             : 
    2957           4 :         case wkbCurve:
    2958           4 :             if (b3D && bMeasured)
    2959           1 :                 return "3D Measured Curve";
    2960           3 :             else if (b3D)
    2961           1 :                 return "3D Curve";
    2962           2 :             else if (bMeasured)
    2963           1 :                 return "Measured Curve";
    2964             :             else
    2965           1 :                 return "Curve";
    2966             : 
    2967           4 :         case wkbSurface:
    2968           4 :             if (b3D && bMeasured)
    2969           1 :                 return "3D Measured Surface";
    2970           3 :             else if (b3D)
    2971           1 :                 return "3D Surface";
    2972           2 :             else if (bMeasured)
    2973           1 :                 return "Measured Surface";
    2974             :             else
    2975           1 :                 return "Surface";
    2976             : 
    2977           0 :         case wkbTriangle:
    2978           0 :             if (b3D && bMeasured)
    2979           0 :                 return "3D Measured Triangle";
    2980           0 :             else if (b3D)
    2981           0 :                 return "3D Triangle";
    2982           0 :             else if (bMeasured)
    2983           0 :                 return "Measured Triangle";
    2984             :             else
    2985           0 :                 return "Triangle";
    2986             : 
    2987           0 :         case wkbPolyhedralSurface:
    2988           0 :             if (b3D && bMeasured)
    2989           0 :                 return "3D Measured PolyhedralSurface";
    2990           0 :             else if (b3D)
    2991           0 :                 return "3D PolyhedralSurface";
    2992           0 :             else if (bMeasured)
    2993           0 :                 return "Measured PolyhedralSurface";
    2994             :             else
    2995           0 :                 return "PolyhedralSurface";
    2996             : 
    2997           2 :         case wkbTIN:
    2998           2 :             if (b3D && bMeasured)
    2999           0 :                 return "3D Measured TIN";
    3000           2 :             else if (b3D)
    3001           0 :                 return "3D TIN";
    3002           2 :             else if (bMeasured)
    3003           0 :                 return "Measured TIN";
    3004             :             else
    3005           2 :                 return "TIN";
    3006             : 
    3007         112 :         case wkbNone:
    3008         112 :             return "None";
    3009             : 
    3010           1 :         default:
    3011             :         {
    3012           1 :             return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
    3013             :         }
    3014             :     }
    3015             : }
    3016             : 
    3017             : /************************************************************************/
    3018             : /*                       OGRMergeGeometryTypes()                        */
    3019             : /************************************************************************/
    3020             : 
    3021             : /**
    3022             :  * \brief Find common geometry type.
    3023             :  *
    3024             :  * Given two geometry types, find the most specific common
    3025             :  * type.  Normally used repeatedly with the geometries in a
    3026             :  * layer to try and establish the most specific geometry type
    3027             :  * that can be reported for the layer.
    3028             :  *
    3029             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    3030             :  * geometry types with nothing in common but the base geometry
    3031             :  * type.  wkbNone should be used to indicate that no geometries
    3032             :  * have been encountered yet, and means the first geometry
    3033             :  * encountered will establish the preliminary type.
    3034             :  *
    3035             :  * @param eMain the first input geometry type.
    3036             :  * @param eExtra the second input geometry type.
    3037             :  *
    3038             :  * @return the merged geometry type.
    3039             :  */
    3040             : 
    3041           0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
    3042             :                                          OGRwkbGeometryType eExtra)
    3043             : 
    3044             : {
    3045           0 :     return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
    3046             : }
    3047             : 
    3048             : /**
    3049             :  * \brief Find common geometry type.
    3050             :  *
    3051             :  * Given two geometry types, find the most specific common
    3052             :  * type.  Normally used repeatedly with the geometries in a
    3053             :  * layer to try and establish the most specific geometry type
    3054             :  * that can be reported for the layer.
    3055             :  *
    3056             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    3057             :  * geometry types with nothing in common but the base geometry
    3058             :  * type.  wkbNone should be used to indicate that no geometries
    3059             :  * have been encountered yet, and means the first geometry
    3060             :  * encountered will establish the preliminary type.
    3061             :  *
    3062             :  * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
    3063             :  * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
    3064             :  * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
    3065             :  * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
    3066             :  *
    3067             :  * @param eMain the first input geometry type.
    3068             :  * @param eExtra the second input geometry type.
    3069             :  * @param bAllowPromotingToCurves determine if promotion to curve type
    3070             :  * must be done.
    3071             :  *
    3072             :  * @return the merged geometry type.
    3073             :  *
    3074             :  */
    3075             : 
    3076         575 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
    3077             :                                            OGRwkbGeometryType eExtra,
    3078             :                                            int bAllowPromotingToCurves)
    3079             : 
    3080             : {
    3081         575 :     OGRwkbGeometryType eFMain = wkbFlatten(eMain);
    3082         575 :     OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
    3083             : 
    3084         575 :     const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
    3085         575 :     const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
    3086             : 
    3087         575 :     if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
    3088          17 :         return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3089             : 
    3090         558 :     if (eFMain == wkbNone)
    3091           2 :         return eExtra;
    3092             : 
    3093         556 :     if (eFExtra == wkbNone)
    3094           0 :         return eMain;
    3095             : 
    3096         556 :     if (eFMain == eFExtra)
    3097             :     {
    3098         539 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3099             :     }
    3100             : 
    3101          17 :     if (bAllowPromotingToCurves)
    3102             :     {
    3103          17 :         if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
    3104           4 :             return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
    3105             : 
    3106          13 :         if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3107           3 :             return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3108             : 
    3109          10 :         if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3110           6 :             return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3111             :     }
    3112             : 
    3113             :     // One is subclass of the other one
    3114           4 :     if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3115             :     {
    3116           0 :         return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3117             :     }
    3118           4 :     else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3119             :     {
    3120           0 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3121             :     }
    3122             : 
    3123             :     // Nothing apparently in common.
    3124           4 :     return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3125             : }
    3126             : 
    3127             : /**
    3128             :  * \fn void OGRGeometry::flattenTo2D();
    3129             :  *
    3130             :  * \brief Convert geometry to strictly 2D.
    3131             :  * In a sense this converts all Z coordinates
    3132             :  * to 0.0.
    3133             :  *
    3134             :  * This method is the same as the C function OGR_G_FlattenTo2D().
    3135             :  */
    3136             : 
    3137             : /************************************************************************/
    3138             : /*                         OGR_G_FlattenTo2D()                          */
    3139             : /************************************************************************/
    3140             : /**
    3141             :  * \brief Convert geometry to strictly 2D.
    3142             :  * In a sense this converts all Z coordinates
    3143             :  * to 0.0.
    3144             :  *
    3145             :  * This function is the same as the CPP method OGRGeometry::flattenTo2D().
    3146             :  *
    3147             :  * @param hGeom handle on the geometry to convert.
    3148             :  */
    3149             : 
    3150          31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
    3151             : 
    3152             : {
    3153          31 :     OGRGeometry::FromHandle(hGeom)->flattenTo2D();
    3154          31 : }
    3155             : 
    3156             : /************************************************************************/
    3157             : /*                            exportToGML()                             */
    3158             : /************************************************************************/
    3159             : 
    3160             : /**
    3161             :  * \fn char *OGRGeometry::exportToGML( const char* const *
    3162             :  * papszOptions = NULL ) const;
    3163             :  *
    3164             :  * \brief Convert a geometry into GML format.
    3165             :  *
    3166             :  * The GML geometry is expressed directly in terms of GML basic data
    3167             :  * types assuming the this is available in the gml namespace.  The returned
    3168             :  * string should be freed with CPLFree() when no longer required.
    3169             :  *
    3170             :  * The supported options are :
    3171             :  * <ul>
    3172             :  * <li> FORMAT=GML2/GML3/GML32.
    3173             :  *      If not set, it will default to GML 2.1.2 output.
    3174             :  * </li>
    3175             :  * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
    3176             :  *      To use gml:Curve element for linestrings.
    3177             :  *      Otherwise gml:LineString will be used .
    3178             :  * </li>
    3179             :  * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
    3180             :  *      SRSNAME_FORMAT in GDAL &gt;=2.2). Defaults to YES.
    3181             :  *      If YES, SRS with EPSG authority will be written with the
    3182             :  *      "urn:ogc:def:crs:EPSG::" prefix.
    3183             :  *      In the case the SRS should be treated as lat/long or
    3184             :  *      northing/easting, then the function will take care of coordinate order
    3185             :  *      swapping if the data axis to CRS axis mapping indicates it.
    3186             :  *      If set to NO, SRS with EPSG authority will be written with the "EPSG:"
    3187             :  *      prefix, even if they are in lat/long order.
    3188             :  * </li>
    3189             :  * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
    3190             :  *      Defaults to OGC_URN.  If SHORT, then srsName will be in
    3191             :  *      the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
    3192             :  *      in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
    3193             :  *      then srsName will be in the form
    3194             :  *      http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
    3195             :  *      OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
    3196             :  *      or northing/easting, then the function will take care of coordinate
    3197             :  *      order swapping if the data axis to CRS axis mapping indicates it.
    3198             :  * </li>
    3199             :  * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
    3200             :  *      top-level geometry element with the provided value.
    3201             :  *      Required for GML 3.2 compatibility.
    3202             :  * </li>
    3203             :  * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
    3204             :  *      FORMAT=GML3/GML32) Default to POSLIST.
    3205             :  *      For 2.5D geometries, define the location where to attach the
    3206             :  *      srsDimension attribute.
    3207             :  *      There are diverging implementations. Some put in on the
    3208             :  *      &lt;gml:posList&gt; element, other on the top geometry element.
    3209             :  * </li>
    3210             :  * <li> NAMESPACE_DECL=YES/NO. If set to YES,
    3211             :  *      xmlns:gml="http://www.opengis.net/gml" will be added to the root node
    3212             :  *      for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
    3213             :  * </li>
    3214             :  * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
    3215             :  *      Resolution for the coordinate precision of the X and Y coordinates.
    3216             :  *      Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
    3217             :  *      to 5 decimal digits. 0 for the default behavior.
    3218             :  * </li>
    3219             :  * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
    3220             :  *      Resolution for the coordinate precision of the Z coordinates.
    3221             :  *      Expressed in the units of the Z axis of the SRS.
    3222             :  *      0 for the default behavior.
    3223             :  * </li>
    3224             :  * </ul>
    3225             :  *
    3226             :  * This method is the same as the C function OGR_G_ExportToGMLEx().
    3227             :  *
    3228             :  * @param papszOptions NULL-terminated list of options.
    3229             :  * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
    3230             :  */
    3231             : 
    3232         251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
    3233             : {
    3234         251 :     return OGR_G_ExportToGMLEx(
    3235             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
    3236         251 :         const_cast<char **>(papszOptions));
    3237             : }
    3238             : 
    3239             : /************************************************************************/
    3240             : /*                            exportToKML()                             */
    3241             : /************************************************************************/
    3242             : 
    3243             : /**
    3244             :  * \fn char *OGRGeometry::exportToKML() const;
    3245             :  *
    3246             :  * \brief Convert a geometry into KML format.
    3247             :  *
    3248             :  * The returned string should be freed with CPLFree() when no longer required.
    3249             :  *
    3250             :  * This method is the same as the C function OGR_G_ExportToKML().
    3251             :  *
    3252             :  * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
    3253             :  */
    3254             : 
    3255           0 : char *OGRGeometry::exportToKML() const
    3256             : {
    3257           0 :     return OGR_G_ExportToKML(
    3258           0 :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
    3259             : }
    3260             : 
    3261             : /************************************************************************/
    3262             : /*                            exportToJson()                            */
    3263             : /************************************************************************/
    3264             : 
    3265             : /**
    3266             :  * \fn char *OGRGeometry::exportToJson() const;
    3267             :  *
    3268             :  * \brief Convert a geometry into GeoJSON format.
    3269             :  *
    3270             :  * The returned string should be freed with CPLFree() when no longer required.
    3271             :  *
    3272             :  * The following options are supported :
    3273             :  * <ul>
    3274             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
    3275             :  * (added in GDAL 3.9)</li>
    3276             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
    3277             :  * (added in GDAL 3.9)</li>
    3278             :  * </ul>
    3279             :  *
    3280             :  * This method is the same as the C function OGR_G_ExportToJson().
    3281             :  *
    3282             :  * @param papszOptions Null terminated list of options, or null (added in 3.9)
    3283             :  * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
    3284             :  */
    3285             : 
    3286          43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
    3287             : {
    3288          43 :     OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
    3289          43 :     return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
    3290          43 :                                 const_cast<char **>(papszOptions));
    3291             : }
    3292             : 
    3293             : /************************************************************************/
    3294             : /*                 OGRSetGenerate_DB2_V72_BYTE_ORDER()                  */
    3295             : /************************************************************************/
    3296             : 
    3297             : /**
    3298             :  * \brief Special entry point to enable the hack for generating DB2 V7.2 style
    3299             :  * WKB.
    3300             :  *
    3301             :  * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
    3302             :  * order in WKB.  This entry point is used to turn on or off the generation of
    3303             :  * such WKB.
    3304             :  */
    3305           4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
    3306             : 
    3307             : {
    3308             : #if defined(HACK_FOR_IBM_DB2_V72)
    3309           4 :     OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
    3310           4 :     return OGRERR_NONE;
    3311             : #else
    3312             :     if (bGenerate_DB2_V72_BYTE_ORDER)
    3313             :         return OGRERR_FAILURE;
    3314             :     else
    3315             :         return OGRERR_NONE;
    3316             : #endif
    3317             : }
    3318             : 
    3319             : /************************************************************************/
    3320             : /*                 OGRGetGenerate_DB2_V72_BYTE_ORDER()                  */
    3321             : /*                                                                      */
    3322             : /*      This is a special entry point to get the value of static flag   */
    3323             : /*      OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER.                      */
    3324             : /************************************************************************/
    3325           0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
    3326             : {
    3327           0 :     return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
    3328             : }
    3329             : 
    3330             : /************************************************************************/
    3331             : /*                         createGEOSContext()                          */
    3332             : /************************************************************************/
    3333             : 
    3334             : /** Create a new GEOS context.
    3335             :  * @return a new GEOS context (to be freed with freeGEOSContext())
    3336             :  */
    3337       85525 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
    3338             : {
    3339             : #ifndef HAVE_GEOS
    3340             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3341             :     return nullptr;
    3342             : #else
    3343       85525 :     return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
    3344             : #endif
    3345             : }
    3346             : 
    3347             : /************************************************************************/
    3348             : /*                          freeGEOSContext()                           */
    3349             : /************************************************************************/
    3350             : 
    3351             : /** Destroy a GEOS context.
    3352             :  * @param hGEOSCtxt GEOS context
    3353             :  */
    3354       82481 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
    3355             : {
    3356             :     (void)hGEOSCtxt;
    3357             : #ifdef HAVE_GEOS
    3358       82481 :     if (hGEOSCtxt != nullptr)
    3359             :     {
    3360       82481 :         finishGEOS_r(hGEOSCtxt);
    3361             :     }
    3362             : #endif
    3363       82481 : }
    3364             : #ifdef HAVE_GEOS
    3365             : 
    3366             : /************************************************************************/
    3367             : /*                      canConvertToMultiPolygon()                      */
    3368             : /************************************************************************/
    3369             : 
    3370         148 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
    3371             : {
    3372         338 :     for (const auto *poSubGeom : *poGC)
    3373             :     {
    3374             :         const OGRwkbGeometryType eSubGeomType =
    3375         272 :             wkbFlatten(poSubGeom->getGeometryType());
    3376         272 :         if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
    3377         164 :             eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
    3378             :         {
    3379          82 :             return false;
    3380             :         }
    3381             :     }
    3382             : 
    3383          66 :     return true;
    3384             : }
    3385             : 
    3386             : /************************************************************************/
    3387             : /*                         GEOSWarningSilencer                          */
    3388             : /************************************************************************/
    3389             : 
    3390             : /** Class that can be used to silence GEOS messages while in-scope. */
    3391             : class GEOSWarningSilencer
    3392             : {
    3393             :   public:
    3394         152 :     explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
    3395         152 :         : m_poContext(poContext)
    3396             :     {
    3397         152 :         GEOSContext_setErrorHandler_r(m_poContext, nullptr);
    3398         152 :         GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
    3399         152 :     }
    3400             : 
    3401         152 :     ~GEOSWarningSilencer()
    3402         152 :     {
    3403         152 :         GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
    3404         152 :         GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
    3405         152 :     }
    3406             : 
    3407             :     CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
    3408             : 
    3409             :   private:
    3410             :     GEOSContextHandle_t m_poContext{nullptr};
    3411             : };
    3412             : 
    3413             : /************************************************************************/
    3414             : /*                           repairForGEOS()                            */
    3415             : /************************************************************************/
    3416             : 
    3417             : /** Modify an OGRGeometry so that it can be converted into GEOS.
    3418             :  *  Modifications include closing unclosed rings and adding redundant vertices
    3419             :  *  to reach minimum point limits in GEOS.
    3420             :  *
    3421             :  *  It is assumed that the input is a non-curved type that can be
    3422             :  *  represented in GEOS.
    3423             :  *
    3424             :  * @param poGeom the geometry to modify
    3425             :  * @return an OGRGeometry that can be converted to GEOS using WKB
    3426             :  */
    3427          22 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
    3428             : {
    3429             : #if GEOS_VERSION_MAJOR >= 3 ||                                                 \
    3430             :     (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
    3431             :     static constexpr int MIN_RING_POINTS = 3;
    3432             : #else
    3433             :     static constexpr int MIN_RING_POINTS = 4;
    3434             : #endif
    3435             : 
    3436          22 :     const auto eType = wkbFlatten(poGeom->getGeometryType());
    3437             : 
    3438          22 :     if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    3439             :     {
    3440           4 :         std::unique_ptr<OGRGeometryCollection> poRet;
    3441           4 :         if (eType == wkbGeometryCollection)
    3442             :         {
    3443           2 :             poRet = std::make_unique<OGRGeometryCollection>();
    3444             :         }
    3445           2 :         else if (eType == wkbMultiPolygon)
    3446             :         {
    3447           2 :             poRet = std::make_unique<OGRMultiPolygon>();
    3448             :         }
    3449           0 :         else if (eType == wkbMultiLineString)
    3450             :         {
    3451           0 :             poRet = std::make_unique<OGRMultiLineString>();
    3452             :         }
    3453           0 :         else if (eType == wkbMultiPoint)
    3454             :         {
    3455           0 :             poRet = std::make_unique<OGRMultiPoint>();
    3456             :         }
    3457             :         else
    3458             :         {
    3459           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3460             :                      "Unexpected geometry type: %s",
    3461             :                      OGRGeometryTypeToName(eType));
    3462           0 :             return nullptr;
    3463             :         }
    3464             : 
    3465           4 :         const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
    3466          12 :         for (const auto *poSubGeomIn : *poColl)
    3467             :         {
    3468           8 :             std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
    3469           8 :             poRet->addGeometry(std::move(poSubGeom));
    3470             :         }
    3471             : 
    3472           4 :         return poRet;
    3473             :     }
    3474             : 
    3475          18 :     if (eType == wkbPoint)
    3476             :     {
    3477           0 :         return std::unique_ptr<OGRGeometry>(poGeom->clone());
    3478             :     }
    3479          18 :     if (eType == wkbLineString)
    3480             :     {
    3481             :         std::unique_ptr<OGRLineString> poLineString(
    3482           4 :             poGeom->toLineString()->clone());
    3483           2 :         if (poLineString->getNumPoints() == 1)
    3484             :         {
    3485           4 :             OGRPoint oPoint;
    3486           2 :             poLineString->getPoint(0, &oPoint);
    3487           2 :             poLineString->addPoint(&oPoint);
    3488             :         }
    3489           2 :         return poLineString;
    3490             :     }
    3491          16 :     if (eType == wkbPolygon)
    3492             :     {
    3493          32 :         std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
    3494          16 :         poPolygon->closeRings();
    3495             : 
    3496             :         // make sure rings have enough points
    3497          34 :         for (auto *poRing : *poPolygon)
    3498             :         {
    3499          22 :             while (poRing->getNumPoints() < MIN_RING_POINTS)
    3500             :             {
    3501           8 :                 OGRPoint oPoint;
    3502           4 :                 poRing->getPoint(0, &oPoint);
    3503           4 :                 poRing->addPoint(&oPoint);
    3504             :             }
    3505             :         }
    3506             : 
    3507          16 :         return poPolygon;
    3508             :     }
    3509             : 
    3510           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
    3511             :              OGRGeometryTypeToName(eType));
    3512           0 :     return nullptr;
    3513             : }
    3514             : 
    3515             : /************************************************************************/
    3516             : /*                         convertToGEOSGeom()                          */
    3517             : /************************************************************************/
    3518             : 
    3519      240733 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
    3520             :                                   const OGRGeometry *poGeom)
    3521             : {
    3522      240733 :     GEOSGeom hGeom = nullptr;
    3523      240733 :     const size_t nDataSize = poGeom->WkbSize();
    3524             :     unsigned char *pabyData =
    3525      240733 :         static_cast<unsigned char *>(CPLMalloc(nDataSize));
    3526             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    3527             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
    3528      240733 :     OGRwkbVariant eWkbVariant = wkbVariantIso;
    3529             : #else
    3530             :     OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
    3531             : #endif
    3532      240733 :     if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
    3533             :     {
    3534      239713 :         hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
    3535             :     }
    3536      240733 :     CPLFree(pabyData);
    3537             : 
    3538      240733 :     return hGeom;
    3539             : }
    3540             : #endif
    3541             : 
    3542             : /************************************************************************/
    3543             : /*                            exportToGEOS()                            */
    3544             : /************************************************************************/
    3545             : 
    3546             : /** Returns a GEOSGeom object corresponding to the geometry.
    3547             :  *
    3548             :  * @param hGEOSCtxt GEOS context
    3549             :  * @param bRemoveEmptyParts Whether empty parts of the geometry should be
    3550             :  * removed before exporting to GEOS (GDAL >= 3.10)
    3551             :  * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
    3552             :  * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
    3553             :  * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
    3554             :  * @return a GEOSGeom object corresponding to the geometry (to be freed with
    3555             :  * GEOSGeom_destroy_r()), or NULL in case of error
    3556             :  */
    3557      240719 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
    3558             :                                    bool bRemoveEmptyParts,
    3559             :                                    bool bAddPointsIfNeeded) const
    3560             : {
    3561             :     (void)hGEOSCtxt;
    3562             :     (void)bRemoveEmptyParts;
    3563             :     (void)bAddPointsIfNeeded;
    3564             : 
    3565             : #ifndef HAVE_GEOS
    3566             : 
    3567             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3568             :     return nullptr;
    3569             : 
    3570             : #else
    3571             : 
    3572      240719 :     if (hGEOSCtxt == nullptr)
    3573           0 :         return nullptr;
    3574             : 
    3575      240719 :     const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
    3576             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3577             :     // POINT EMPTY is exported to WKB as if it were POINT(0 0),
    3578             :     // so that particular case is necessary.
    3579             :     if (eType == wkbPoint && IsEmpty())
    3580             :     {
    3581             :         return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
    3582             :     }
    3583             : #endif
    3584             : 
    3585      240719 :     GEOSGeom hGeom = nullptr;
    3586             : 
    3587      240719 :     std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
    3588      240719 :     const OGRGeometry *poGeosInput = this;
    3589             : 
    3590      240719 :     const bool bHasZ = poGeosInput->Is3D();
    3591      240719 :     bool bHasM = poGeosInput->IsMeasured();
    3592             : 
    3593      240719 :     if (poGeosInput->hasCurveGeometry())
    3594             :     {
    3595         864 :         poModifiedInput.reset(poGeosInput->getLinearGeometry());
    3596         864 :         poGeosInput = poModifiedInput.get();
    3597             :     }
    3598             : 
    3599      240719 :     if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
    3600             :     {
    3601           1 :         if (!poModifiedInput)
    3602             :         {
    3603           1 :             poModifiedInput.reset(poGeosInput->clone());
    3604           1 :             poGeosInput = poModifiedInput.get();
    3605             :         }
    3606           1 :         poModifiedInput->removeEmptyParts();
    3607             :     }
    3608             : 
    3609             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3610             :     // GEOS < 3.12 doesn't support M dimension
    3611             :     if (bHasM)
    3612             :     {
    3613             :         if (!poModifiedInput)
    3614             :         {
    3615             :             poModifiedInput.reset(poGeosInput->clone());
    3616             :             poGeosInput = poModifiedInput.get();
    3617             :         }
    3618             :         poModifiedInput->setMeasured(false);
    3619             :         bHasM = false;
    3620             :     }
    3621             : #endif
    3622             : 
    3623      240719 :     if (eType == wkbTriangle)
    3624             :     {
    3625             :         poModifiedInput =
    3626          98 :             std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
    3627          98 :         poGeosInput = poModifiedInput.get();
    3628             :     }
    3629      240621 :     else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
    3630             :     {
    3631         844 :         if (!poModifiedInput)
    3632             :         {
    3633         844 :             poModifiedInput.reset(poGeosInput->clone());
    3634             :         }
    3635             : 
    3636        2532 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3637         844 :             std::move(poModifiedInput),
    3638         844 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
    3639         844 :         poGeosInput = poModifiedInput.get();
    3640             :     }
    3641      239925 :     else if (eType == wkbGeometryCollection &&
    3642         148 :              CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
    3643             :     {
    3644          66 :         if (!poModifiedInput)
    3645             :         {
    3646          64 :             poModifiedInput.reset(poGeosInput->clone());
    3647             :         }
    3648             : 
    3649             :         // Force into a MultiPolygon, then back to a GeometryCollection.
    3650             :         // This gets rid of fancy types like TIN and PolyhedralSurface that
    3651             :         // GEOS doesn't understand and flattens nested collections.
    3652         198 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3653          66 :             std::move(poModifiedInput),
    3654          66 :             OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
    3655         198 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3656          66 :             std::move(poModifiedInput),
    3657          66 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
    3658             : 
    3659          66 :         poGeosInput = poModifiedInput.get();
    3660             :     }
    3661             : 
    3662             :     {
    3663             :         // Rather than check for conditions that would prevent conversion to
    3664             :         // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
    3665             :         // conversion as-is. If the conversion fails, we don't want any
    3666             :         // warnings emitted; we'll repair the input and try again.
    3667           0 :         std::optional<GEOSWarningSilencer> oSilencer;
    3668      240719 :         if (bAddPointsIfNeeded)
    3669             :         {
    3670         152 :             oSilencer.emplace(hGEOSCtxt);
    3671             :         }
    3672             : 
    3673      240719 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3674             :     }
    3675             : 
    3676      240719 :     if (hGeom == nullptr && bAddPointsIfNeeded)
    3677             :     {
    3678          14 :         poModifiedInput = repairForGEOS(poGeosInput);
    3679          14 :         poGeosInput = poModifiedInput.get();
    3680             : 
    3681          14 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3682             :     }
    3683             : 
    3684      240719 :     return hGeom;
    3685             : 
    3686             : #endif  // HAVE_GEOS
    3687             : }
    3688             : 
    3689             : /************************************************************************/
    3690             : /*                          hasCurveGeometry()                          */
    3691             : /************************************************************************/
    3692             : 
    3693             : /**
    3694             :  * \brief Returns if this geometry is or has curve geometry.
    3695             :  *
    3696             :  * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
    3697             :  * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
    3698             :  *
    3699             :  * If bLookForNonLinear is set to TRUE, it will be actually looked if
    3700             :  * the geometry or its subgeometries are or contain a non-linear
    3701             :  * geometry in them. In which case, if the method returns TRUE, it
    3702             :  * means that getLinearGeometry() would return an approximate version
    3703             :  * of the geometry. Otherwise, getLinearGeometry() would do a
    3704             :  * conversion, but with just converting container type, like
    3705             :  * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
    3706             :  * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
    3707             :  * conversion.
    3708             :  *
    3709             :  * This method is the same as the C function OGR_G_HasCurveGeometry().
    3710             :  *
    3711             :  * @param bLookForNonLinear set it to TRUE to check if the geometry is
    3712             :  * or contains a CIRCULARSTRING.
    3713             :  *
    3714             :  * @return TRUE if this geometry is or has curve geometry.
    3715             :  *
    3716             :  */
    3717             : 
    3718      280546 : bool OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
    3719             : {
    3720      280546 :     return FALSE;
    3721             : }
    3722             : 
    3723             : /************************************************************************/
    3724             : /*                         getLinearGeometry()                          */
    3725             : /************************************************************************/
    3726             : 
    3727             : /**
    3728             :  * \brief Return, possibly approximate, non-curve version of this geometry.
    3729             :  *
    3730             :  * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
    3731             :  * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
    3732             :  *
    3733             :  * The ownership of the returned geometry belongs to the caller.
    3734             :  *
    3735             :  * The reverse method is OGRGeometry::getCurveGeometry().
    3736             :  *
    3737             :  * This method is the same as the C function OGR_G_GetLinearGeometry().
    3738             :  *
    3739             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    3740             :  * arc, zero to use the default setting.
    3741             :  * @param papszOptions options as a null-terminated list of strings.
    3742             :  *                     See OGRGeometryFactory::curveToLineString() for
    3743             :  *                     valid options.
    3744             :  *
    3745             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3746             :  *
    3747             :  */
    3748             : 
    3749             : OGRGeometry *
    3750          88 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
    3751             :                                CPL_UNUSED const char *const *papszOptions) const
    3752             : {
    3753          88 :     return clone();
    3754             : }
    3755             : 
    3756             : /************************************************************************/
    3757             : /*                          getCurveGeometry()                          */
    3758             : /************************************************************************/
    3759             : 
    3760             : /**
    3761             :  * \brief Return curve version of this geometry.
    3762             :  *
    3763             :  * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
    3764             :  * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
    3765             :  * curve geometries.
    3766             :  *
    3767             :  * If the geometry has no curve portion, the returned geometry will be a clone
    3768             :  * of it.
    3769             :  *
    3770             :  * The ownership of the returned geometry belongs to the caller.
    3771             :  *
    3772             :  * The reverse method is OGRGeometry::getLinearGeometry().
    3773             :  *
    3774             :  * This function is the same as C function OGR_G_GetCurveGeometry().
    3775             :  *
    3776             :  * @param papszOptions options as a null-terminated list of strings.
    3777             :  *                     Unused for now. Must be set to NULL.
    3778             :  *
    3779             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3780             :  *
    3781             :  */
    3782             : 
    3783             : OGRGeometry *
    3784           5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
    3785             : {
    3786           5 :     return clone();
    3787             : }
    3788             : 
    3789             : /************************************************************************/
    3790             : /*                              Distance()                              */
    3791             : /************************************************************************/
    3792             : 
    3793             : /**
    3794             :  * \brief Compute distance between two geometries.
    3795             :  *
    3796             :  * Returns the shortest distance between the two geometries. The distance is
    3797             :  * expressed into the same unit as the coordinates of the geometries.
    3798             :  *
    3799             :  * This method is the same as the C function OGR_G_Distance().
    3800             :  *
    3801             :  * This method is built on the GEOS library, check it for the definition
    3802             :  * of the geometry operation.
    3803             :  * If OGR is built without the GEOS library, this method will always fail,
    3804             :  * issuing a CPLE_NotSupported error.
    3805             :  *
    3806             :  * @param poOtherGeom the other geometry to compare against.
    3807             :  *
    3808             :  * @return the distance between the geometries or -1 if an error occurs.
    3809             :  */
    3810             : 
    3811          25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
    3812             : 
    3813             : {
    3814          25 :     if (nullptr == poOtherGeom)
    3815             :     {
    3816           0 :         CPLDebug("OGR",
    3817             :                  "OGRGeometry::Distance called with NULL geometry pointer");
    3818           0 :         return -1.0;
    3819             :     }
    3820             : 
    3821          25 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    3822             :     {
    3823             : #ifndef HAVE_SFCGAL
    3824             : 
    3825           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3826           0 :         return -1.0;
    3827             : 
    3828             : #else
    3829             : 
    3830             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3831             :         if (poThis == nullptr)
    3832             :             return -1.0;
    3833             : 
    3834             :         sfcgal_geometry_t *poOther =
    3835             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3836             :         if (poOther == nullptr)
    3837             :         {
    3838             :             sfcgal_geometry_delete(poThis);
    3839             :             return -1.0;
    3840             :         }
    3841             : 
    3842             :         const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
    3843             : 
    3844             :         sfcgal_geometry_delete(poThis);
    3845             :         sfcgal_geometry_delete(poOther);
    3846             : 
    3847             :         return dfDistance > 0.0 ? dfDistance : -1.0;
    3848             : 
    3849             : #endif
    3850             :     }
    3851             : 
    3852             :     else
    3853             :     {
    3854             : #ifndef HAVE_GEOS
    3855             : 
    3856             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3857             :         return -1.0;
    3858             : 
    3859             : #else
    3860             : 
    3861          25 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    3862             :         // GEOSGeom is a pointer
    3863          25 :         GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3864          25 :         GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
    3865             : 
    3866          25 :         int bIsErr = 0;
    3867          25 :         double dfDistance = 0.0;
    3868             : 
    3869          25 :         if (hThis != nullptr && hOther != nullptr)
    3870             :         {
    3871          25 :             bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
    3872             :         }
    3873             : 
    3874          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hThis);
    3875          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hOther);
    3876          25 :         freeGEOSContext(hGEOSCtxt);
    3877             : 
    3878          25 :         if (bIsErr > 0)
    3879             :         {
    3880          25 :             return dfDistance;
    3881             :         }
    3882             : 
    3883             :         /* Calculations error */
    3884           0 :         return -1.0;
    3885             : 
    3886             : #endif /* HAVE_GEOS */
    3887             :     }
    3888             : }
    3889             : 
    3890             : /************************************************************************/
    3891             : /*                           OGR_G_Distance()                           */
    3892             : /************************************************************************/
    3893             : /**
    3894             :  * \brief Compute distance between two geometries.
    3895             :  *
    3896             :  * Returns the shortest distance between the two geometries. The distance is
    3897             :  * expressed into the same unit as the coordinates of the geometries.
    3898             :  *
    3899             :  * This function is the same as the C++ method OGRGeometry::Distance().
    3900             :  *
    3901             :  * This function is built on the GEOS library, check it for the definition
    3902             :  * of the geometry operation.
    3903             :  * If OGR is built without the GEOS library, this function will always fail,
    3904             :  * issuing a CPLE_NotSupported error.
    3905             :  *
    3906             :  * @param hFirst the first geometry to compare against.
    3907             :  * @param hOther the other geometry to compare against.
    3908             :  *
    3909             :  * @return the distance between the geometries or -1 if an error occurs.
    3910             :  */
    3911             : 
    3912           2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
    3913             : 
    3914             : {
    3915           2 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
    3916             : 
    3917           4 :     return OGRGeometry::FromHandle(hFirst)->Distance(
    3918           4 :         OGRGeometry::FromHandle(hOther));
    3919             : }
    3920             : 
    3921             : /************************************************************************/
    3922             : /*                             Distance3D()                             */
    3923             : /************************************************************************/
    3924             : 
    3925             : /**
    3926             :  * \brief Returns the 3D distance between two geometries
    3927             :  *
    3928             :  * The distance is expressed into the same unit as the coordinates of the
    3929             :  * geometries.
    3930             :  *
    3931             :  * This method is built on the SFCGAL library, check it for the definition
    3932             :  * of the geometry operation.
    3933             :  * If OGR is built without the SFCGAL library, this method will always return
    3934             :  * -1.0
    3935             :  *
    3936             :  * This function is the same as the C function OGR_G_Distance3D().
    3937             :  *
    3938             :  * @return distance between the two geometries
    3939             :  */
    3940             : 
    3941           1 : double OGRGeometry::Distance3D(
    3942             :     UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
    3943             : {
    3944           1 :     if (poOtherGeom == nullptr)
    3945             :     {
    3946           0 :         CPLDebug("OGR",
    3947             :                  "OGRTriangle::Distance3D called with NULL geometry pointer");
    3948           0 :         return -1.0;
    3949             :     }
    3950             : 
    3951           1 :     if (!(poOtherGeom->Is3D() && Is3D()))
    3952             :     {
    3953           0 :         CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
    3954             :                         "geometry(geometries)");
    3955           0 :         return -1.0;
    3956             :     }
    3957             : 
    3958             : #ifndef HAVE_SFCGAL
    3959             : 
    3960           1 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3961           1 :     return -1.0;
    3962             : 
    3963             : #else
    3964             : 
    3965             :     sfcgal_init();
    3966             :     sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3967             :     if (poThis == nullptr)
    3968             :         return -1.0;
    3969             : 
    3970             :     sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3971             :     if (poOther == nullptr)
    3972             :     {
    3973             :         sfcgal_geometry_delete(poThis);
    3974             :         return -1.0;
    3975             :     }
    3976             : 
    3977             :     const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
    3978             : 
    3979             :     sfcgal_geometry_delete(poThis);
    3980             :     sfcgal_geometry_delete(poOther);
    3981             : 
    3982             :     return dfDistance > 0 ? dfDistance : -1.0;
    3983             : 
    3984             : #endif
    3985             : }
    3986             : 
    3987             : /************************************************************************/
    3988             : /*                          OGR_G_Distance3D()                          */
    3989             : /************************************************************************/
    3990             : /**
    3991             :  * \brief Returns the 3D distance between two geometries
    3992             :  *
    3993             :  * The distance is expressed into the same unit as the coordinates of the
    3994             :  * geometries.
    3995             :  *
    3996             :  * This method is built on the SFCGAL library, check it for the definition
    3997             :  * of the geometry operation.
    3998             :  * If OGR is built without the SFCGAL library, this method will always return
    3999             :  * -1.0
    4000             :  *
    4001             :  * This function is the same as the C++ method OGRGeometry::Distance3D().
    4002             :  *
    4003             :  * @param hFirst the first geometry to compare against.
    4004             :  * @param hOther the other geometry to compare against.
    4005             :  * @return distance between the two geometries
    4006             :  *
    4007             :  * @return the distance between the geometries or -1 if an error occurs.
    4008             :  */
    4009             : 
    4010           1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
    4011             : 
    4012             : {
    4013           1 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
    4014             : 
    4015           2 :     return OGRGeometry::FromHandle(hFirst)->Distance3D(
    4016           2 :         OGRGeometry::FromHandle(hOther));
    4017             : }
    4018             : 
    4019             : /************************************************************************/
    4020             : /*                      OGRGeometryRebuildCurves()                      */
    4021             : /************************************************************************/
    4022             : 
    4023             : #ifdef HAVE_GEOS
    4024        3936 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
    4025             :                                              const OGRGeometry *poOtherGeom,
    4026             :                                              OGRGeometry *poOGRProduct)
    4027             : {
    4028        7872 :     if (poOGRProduct != nullptr &&
    4029        7789 :         wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
    4030        3853 :         (poGeom->hasCurveGeometry(true) ||
    4031        2705 :          (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
    4032             :     {
    4033           8 :         OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
    4034           8 :         delete poOGRProduct;
    4035           8 :         return poCurveGeom;
    4036             :     }
    4037        3928 :     return poOGRProduct;
    4038             : }
    4039             : 
    4040             : /************************************************************************/
    4041             : /*                       BuildGeometryFromGEOS()                        */
    4042             : /************************************************************************/
    4043             : 
    4044        3786 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
    4045             :                                           GEOSGeom hGeosProduct,
    4046             :                                           const OGRGeometry *poSelf,
    4047             :                                           const OGRGeometry *poOtherGeom)
    4048             : {
    4049        3786 :     OGRGeometry *poOGRProduct = nullptr;
    4050        3786 :     if (hGeosProduct != nullptr)
    4051             :     {
    4052             :         poOGRProduct =
    4053        3784 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
    4054        3784 :         if (poOGRProduct != nullptr &&
    4055        9236 :             poSelf->getSpatialReference() != nullptr &&
    4056        1668 :             (poOtherGeom == nullptr ||
    4057        1668 :              (poOtherGeom->getSpatialReference() != nullptr &&
    4058        1533 :               poOtherGeom->getSpatialReference()->IsSame(
    4059             :                   poSelf->getSpatialReference()))))
    4060             :         {
    4061        1582 :             poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
    4062             :         }
    4063             :         poOGRProduct =
    4064        3784 :             OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
    4065        3784 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
    4066             :     }
    4067        3786 :     return poOGRProduct;
    4068             : }
    4069             : 
    4070             : /************************************************************************/
    4071             : /*                     BuildGeometryFromTwoGeoms()                      */
    4072             : /************************************************************************/
    4073             : 
    4074        2776 : static OGRGeometry *BuildGeometryFromTwoGeoms(
    4075             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    4076             :     GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
    4077             :                                        const GEOSGeometry *,
    4078             :                                        const GEOSGeometry *))
    4079             : {
    4080        2776 :     OGRGeometry *poOGRProduct = nullptr;
    4081             : 
    4082        2776 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    4083        2776 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    4084        2776 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    4085        2776 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    4086             :     {
    4087             :         GEOSGeom hGeosProduct =
    4088        2776 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
    4089             : 
    4090             :         poOGRProduct =
    4091        2776 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
    4092             :     }
    4093        2776 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    4094        2776 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    4095        2776 :     poSelf->freeGEOSContext(hGEOSCtxt);
    4096             : 
    4097        2776 :     return poOGRProduct;
    4098             : }
    4099             : 
    4100             : /************************************************************************/
    4101             : /*                      OGRGEOSBooleanPredicate()                       */
    4102             : /************************************************************************/
    4103             : 
    4104       22762 : static bool OGRGEOSBooleanPredicate(
    4105             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    4106             :     char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
    4107             :                               const GEOSGeometry *))
    4108             : {
    4109       22762 :     bool bResult = false;
    4110             : 
    4111       22762 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    4112       22762 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    4113       22762 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    4114       22762 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    4115             :     {
    4116       22203 :         bResult =
    4117       22203 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
    4118             :     }
    4119       22762 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    4120       22762 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    4121       22762 :     poSelf->freeGEOSContext(hGEOSCtxt);
    4122             : 
    4123       22762 :     return bResult;
    4124             : }
    4125             : 
    4126             : #endif  // HAVE_GEOS
    4127             : 
    4128             : /************************************************************************/
    4129             : /*                             MakeValid()                              */
    4130             : /************************************************************************/
    4131             : 
    4132             : /**
    4133             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4134             :  *
    4135             :  * Already-valid geometries are cloned without further intervention
    4136             :  * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
    4137             :  * may be subject to non-significant transformations, such as duplicated point
    4138             :  * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
    4139             :  * geometry collections could be returned a single geometry. GDAL 3.10
    4140             :  * returns the same type of geometry).
    4141             :  *
    4142             :  * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
    4143             :  * post-processing step is often desired.
    4144             :  *
    4145             :  * This method is the same as the C function OGR_G_MakeValid().
    4146             :  *
    4147             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4148             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4149             :  * library, this function will return a clone of the input geometry if it is
    4150             :  * valid, or NULL if it is invalid.
    4151             :  *
    4152             :  * Certain geometries cannot be read using GEOS, for example if Polygon rings
    4153             :  * are not closed or do not contain enough vertices. If a geometry cannot be
    4154             :  * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
    4155             :  * attempt to modify these geometries such that they can be read and
    4156             :  * repaired by GEOS.
    4157             :  *
    4158             :  * @param papszOptions NULL terminated list of options, or NULL. The following
    4159             :  * options are available:
    4160             :  * <ul>
    4161             :  * <li>METHOD=LINEWORK/STRUCTURE.
    4162             :  *     LINEWORK is the default method, which combines all rings into a set of
    4163             :  *     noded lines and then extracts valid polygons from that linework.
    4164             :  *     The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
    4165             :  *     all rings valid, then merges shells and
    4166             :  *     subtracts holes from shells to generate valid result. Assumes that
    4167             :  *     holes and shells are correctly categorized.</li>
    4168             :  * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
    4169             :  *     NO (default): collapses are converted to empty geometries
    4170             :  *     YES: collapses are converted to a valid geometry of lower dimension.</li>
    4171             :  * </ul>
    4172             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4173             :  *
    4174             :  * @since GDAL 3.0
    4175             :  */
    4176         153 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
    4177             : {
    4178             :     (void)papszOptions;
    4179             : #ifndef HAVE_GEOS
    4180             :     if (IsValid())
    4181             :         return clone();
    4182             : 
    4183             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4184             :     return nullptr;
    4185             : #else
    4186         153 :     if (IsSFCGALCompatible())
    4187             :     {
    4188           0 :         if (IsValid())
    4189           0 :             return clone();
    4190             :     }
    4191         153 :     else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
    4192             :     {
    4193           3 :         GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
    4194           3 :         bool bIsValid = false;
    4195           3 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4196           3 :         if (hGeosGeom)
    4197             :         {
    4198           3 :             bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
    4199           3 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4200             :         }
    4201           3 :         freeGEOSContext(hGEOSCtxt);
    4202           3 :         if (bIsValid)
    4203           1 :             return clone();
    4204             :     }
    4205             : 
    4206         152 :     const bool bStructureMethod = EQUAL(
    4207             :         CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
    4208         152 :     CPL_IGNORE_RET_VAL(bStructureMethod);
    4209             : #if !(GEOS_VERSION_MAJOR > 3 ||                                                \
    4210             :       (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    4211             :     if (bStructureMethod)
    4212             :     {
    4213             :         CPLError(CE_Failure, CPLE_NotSupported,
    4214             :                  "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
    4215             :         return nullptr;
    4216             :     }
    4217             : #endif
    4218             : 
    4219         152 :     OGRGeometry *poOGRProduct = nullptr;
    4220             : 
    4221         152 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4222         152 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
    4223         152 :     if (hGeosGeom != nullptr)
    4224             :     {
    4225             :         GEOSGeom hGEOSRet;
    4226             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4227             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4228         152 :         if (bStructureMethod)
    4229             :         {
    4230             :             GEOSMakeValidParams *params =
    4231          15 :                 GEOSMakeValidParams_create_r(hGEOSCtxt);
    4232          15 :             CPLAssert(params);
    4233          15 :             GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
    4234             :                                             GEOS_MAKE_VALID_STRUCTURE);
    4235          15 :             GEOSMakeValidParams_setKeepCollapsed_r(
    4236             :                 hGEOSCtxt, params,
    4237          15 :                 CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
    4238          15 :             hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
    4239          15 :             GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
    4240             :         }
    4241             :         else
    4242             : #endif
    4243             :         {
    4244         137 :             hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
    4245             :         }
    4246         152 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4247             : 
    4248         152 :         if (hGEOSRet != nullptr)
    4249             :         {
    4250             :             poOGRProduct =
    4251         152 :                 OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
    4252         152 :             if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
    4253           6 :                 poOGRProduct->assignSpatialReference(getSpatialReference());
    4254             :             poOGRProduct =
    4255         152 :                 OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
    4256         152 :             GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
    4257             : 
    4258             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4259             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4260             :             // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
    4261             :             // if the input is a multiple geometry
    4262         152 :             if (poOGRProduct && bStructureMethod &&
    4263         310 :                 OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
    4264           6 :                 !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
    4265             :                                      wkbGeometryCollection))
    4266             :             {
    4267           6 :                 poOGRProduct = OGRGeometryFactory::forceTo(
    4268           6 :                                    std::unique_ptr<OGRGeometry>(poOGRProduct),
    4269           3 :                                    getGeometryType())
    4270           3 :                                    .release();
    4271             :             }
    4272             : #endif
    4273             :         }
    4274             :     }
    4275         152 :     freeGEOSContext(hGEOSCtxt);
    4276             : 
    4277         152 :     return poOGRProduct;
    4278             : #endif
    4279             : }
    4280             : 
    4281             : /************************************************************************/
    4282             : /*                          OGR_G_MakeValid()                           */
    4283             : /************************************************************************/
    4284             : 
    4285             : /**
    4286             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4287             :  *
    4288             :  * Already-valid geometries are cloned without further intervention.
    4289             :  *
    4290             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4291             :  *
    4292             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4293             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4294             :  * library, this function will return a clone of the input geometry if it is
    4295             :  * valid, or NULL if it is invalid
    4296             :  *
    4297             :  * @param hGeom The Geometry to make valid.
    4298             :  *
    4299             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4300             :  * or NULL if an error occurs.
    4301             :  *
    4302             :  * @since GDAL 3.0
    4303             :  */
    4304             : 
    4305           0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
    4306             : 
    4307             : {
    4308           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
    4309             : 
    4310           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
    4311             : }
    4312             : 
    4313             : /************************************************************************/
    4314             : /*                         OGR_G_MakeValidEx()                          */
    4315             : /************************************************************************/
    4316             : 
    4317             : /**
    4318             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4319             :  *
    4320             :  * Already-valid geometries are cloned without further intervention.
    4321             :  *
    4322             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4323             :  *
    4324             :  * See documentation of that method for possible options.
    4325             :  *
    4326             :  * @param hGeom The Geometry to make valid.
    4327             :  * @param papszOptions Options.
    4328             :  *
    4329             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4330             :  * or NULL if an error occurs.
    4331             :  *
    4332             :  * @since GDAL 3.4
    4333             :  */
    4334             : 
    4335          25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
    4336             : 
    4337             : {
    4338          25 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
    4339             : 
    4340          25 :     return OGRGeometry::ToHandle(
    4341          50 :         OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
    4342             : }
    4343             : 
    4344             : /************************************************************************/
    4345             : /*                             Normalize()                              */
    4346             : /************************************************************************/
    4347             : 
    4348             : /**
    4349             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4350             :  *
    4351             :  * This method is the same as the C function OGR_G_Normalize().
    4352             :  *
    4353             :  * This function is built on the GEOS library; check it for the definition
    4354             :  * of the geometry operation.
    4355             :  * If OGR is built without the GEOS library, this function will always fail,
    4356             :  * issuing a CPLE_NotSupported error.
    4357             :  *
    4358             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4359             :  *
    4360             :  * @since GDAL 3.3
    4361             :  */
    4362          51 : OGRGeometry *OGRGeometry::Normalize() const
    4363             : {
    4364             : #ifndef HAVE_GEOS
    4365             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4366             :     return nullptr;
    4367             : #else
    4368          51 :     OGRGeometry *poOGRProduct = nullptr;
    4369             : 
    4370          51 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4371          51 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4372          51 :     if (hGeosGeom != nullptr)
    4373             :     {
    4374             : 
    4375          51 :         int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
    4376             : 
    4377          51 :         if (hGEOSRet == 0)
    4378             :         {
    4379             :             poOGRProduct =
    4380          51 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
    4381             :         }
    4382             :         else
    4383             :         {
    4384           0 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4385             :         }
    4386             :     }
    4387          51 :     freeGEOSContext(hGEOSCtxt);
    4388             : 
    4389          51 :     return poOGRProduct;
    4390             : #endif
    4391             : }
    4392             : 
    4393             : /************************************************************************/
    4394             : /*                          OGR_G_Normalize()                           */
    4395             : /************************************************************************/
    4396             : 
    4397             : /**
    4398             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4399             :  *
    4400             :  * This function is the same as the C++ method OGRGeometry::Normalize().
    4401             :  *
    4402             :  * This function is built on the GEOS library; check it for the definition
    4403             :  * of the geometry operation.
    4404             :  * If OGR is built without the GEOS library, this function will always fail,
    4405             :  * issuing a CPLE_NotSupported error.
    4406             :  * @param hGeom The Geometry to normalize.
    4407             :  *
    4408             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4409             :  * or NULL if an error occurs.
    4410             :  *
    4411             :  * @since GDAL 3.3
    4412             :  */
    4413             : 
    4414          21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
    4415             : 
    4416             : {
    4417          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
    4418             : 
    4419          21 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
    4420             : }
    4421             : 
    4422             : /************************************************************************/
    4423             : /*                             ConvexHull()                             */
    4424             : /************************************************************************/
    4425             : 
    4426             : /**
    4427             :  * \brief Compute convex hull.
    4428             :  *
    4429             :  * A new geometry object is created and returned containing the convex
    4430             :  * hull of the geometry on which the method is invoked.
    4431             :  *
    4432             :  * This method is the same as the C function OGR_G_ConvexHull().
    4433             :  *
    4434             :  * This method is built on the GEOS library, check it for the definition
    4435             :  * of the geometry operation.
    4436             :  * If OGR is built without the GEOS library, this method will always fail,
    4437             :  * issuing a CPLE_NotSupported error.
    4438             :  *
    4439             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4440             :  */
    4441             : 
    4442           6 : OGRGeometry *OGRGeometry::ConvexHull() const
    4443             : 
    4444             : {
    4445           6 :     if (IsSFCGALCompatible())
    4446             :     {
    4447             : #ifndef HAVE_SFCGAL
    4448             : 
    4449           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    4450           0 :         return nullptr;
    4451             : 
    4452             : #else
    4453             : 
    4454             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    4455             :         if (poThis == nullptr)
    4456             :             return nullptr;
    4457             : 
    4458             :         sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
    4459             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    4460             :         if (h_prodGeom)
    4461             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    4462             : 
    4463             :         sfcgal_geometry_delete(poThis);
    4464             :         sfcgal_geometry_delete(poRes);
    4465             : 
    4466             :         return h_prodGeom;
    4467             : 
    4468             : #endif
    4469             :     }
    4470             : 
    4471             :     else
    4472             :     {
    4473             : #ifndef HAVE_GEOS
    4474             : 
    4475             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4476             :         return nullptr;
    4477             : 
    4478             : #else
    4479             : 
    4480           6 :         OGRGeometry *poOGRProduct = nullptr;
    4481             : 
    4482           6 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4483           6 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4484           6 :         if (hGeosGeom != nullptr)
    4485             :         {
    4486           6 :             GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
    4487           6 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4488             : 
    4489             :             poOGRProduct =
    4490           6 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4491             :         }
    4492           6 :         freeGEOSContext(hGEOSCtxt);
    4493             : 
    4494           6 :         return poOGRProduct;
    4495             : 
    4496             : #endif /* HAVE_GEOS */
    4497             :     }
    4498             : }
    4499             : 
    4500             : /************************************************************************/
    4501             : /*                          OGR_G_ConvexHull()                          */
    4502             : /************************************************************************/
    4503             : /**
    4504             :  * \brief Compute convex hull.
    4505             :  *
    4506             :  * A new geometry object is created and returned containing the convex
    4507             :  * hull of the geometry on which the method is invoked.
    4508             :  *
    4509             :  * This function is the same as the C++ method OGRGeometry::ConvexHull().
    4510             :  *
    4511             :  * This function is built on the GEOS library, check it for the definition
    4512             :  * of the geometry operation.
    4513             :  * If OGR is built without the GEOS library, this function will always fail,
    4514             :  * issuing a CPLE_NotSupported error.
    4515             :  *
    4516             :  * @param hTarget The Geometry to calculate the convex hull of.
    4517             :  *
    4518             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4519             :  * or NULL if an error occurs.
    4520             :  */
    4521             : 
    4522           1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
    4523             : 
    4524             : {
    4525           1 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
    4526             : 
    4527           1 :     return OGRGeometry::ToHandle(
    4528           1 :         OGRGeometry::FromHandle(hTarget)->ConvexHull());
    4529             : }
    4530             : 
    4531             : /************************************************************************/
    4532             : /*                            ConcaveHull()                             */
    4533             : /************************************************************************/
    4534             : 
    4535             : /**
    4536             :  * \brief Compute the concave hull of a geometry.
    4537             :  *
    4538             :  * The concave hull is fully contained within the convex hull and also
    4539             :  * contains all the points of the input, but in a smaller area.
    4540             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4541             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4542             :  * that contains all the points in the input Geometry.
    4543             :  *
    4544             :  * A new geometry object is created and returned containing the concave
    4545             :  * hull of the geometry on which the method is invoked.
    4546             :  *
    4547             :  * This method is the same as the C function OGR_G_ConcaveHull().
    4548             :  *
    4549             :  * This method is built on the GEOS >= 3.11 library
    4550             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4551             :  * fail, issuing a CPLE_NotSupported error.
    4552             :  *
    4553             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4554             :  * @param bAllowHoles Whether holes are allowed.
    4555             :  *
    4556             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4557             :  *
    4558             :  * @since GDAL 3.6
    4559             :  * @see OGRGeometry::ConcaveHullOfPolygons()
    4560             :  */
    4561             : 
    4562           8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
    4563             : {
    4564             : #ifndef HAVE_GEOS
    4565             :     (void)dfRatio;
    4566             :     (void)bAllowHoles;
    4567             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4568             :     return nullptr;
    4569             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4570             :     (void)dfRatio;
    4571             :     (void)bAllowHoles;
    4572             :     CPLError(CE_Failure, CPLE_NotSupported,
    4573             :              "GEOS 3.11 or later needed for ConcaveHull.");
    4574             :     return nullptr;
    4575             : #else
    4576           8 :     OGRGeometry *poOGRProduct = nullptr;
    4577             : 
    4578           8 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4579           8 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4580           8 :     if (hGeosGeom != nullptr)
    4581             :     {
    4582             :         GEOSGeom hGeosHull =
    4583           8 :             GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
    4584           8 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4585             : 
    4586             :         poOGRProduct =
    4587           8 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4588             :     }
    4589           8 :     freeGEOSContext(hGEOSCtxt);
    4590             : 
    4591           8 :     return poOGRProduct;
    4592             : #endif /* HAVE_GEOS */
    4593             : }
    4594             : 
    4595             : /************************************************************************/
    4596             : /*                         OGR_G_ConcaveHull()                          */
    4597             : /************************************************************************/
    4598             : /**
    4599             :  * \brief Compute the concave hull of a geometry.
    4600             :  *
    4601             :  * The concave hull is fully contained within the convex hull and also
    4602             :  * contains all the points of the input, but in a smaller area.
    4603             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4604             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4605             :  * that contains all the points in the input Geometry.
    4606             :  *
    4607             :  * A new geometry object is created and returned containing the convex
    4608             :  * hull of the geometry on which the function is invoked.
    4609             :  *
    4610             :  * This function is the same as the C++ method OGRGeometry::ConcaveHull().
    4611             :  *
    4612             :  * This function is built on the GEOS >= 3.11 library
    4613             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4614             :  * fail, issuing a CPLE_NotSupported error.
    4615             :  *
    4616             :  * @param hTarget The Geometry to calculate the concave hull of.
    4617             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4618             :  * @param bAllowHoles Whether holes are allowed.
    4619             :  *
    4620             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4621             :  * or NULL if an error occurs.
    4622             :  *
    4623             :  * @since GDAL 3.6
    4624             :  * @see OGR_G_ConcaveHullOfPolygons()
    4625             :  */
    4626             : 
    4627           2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
    4628             :                                bool bAllowHoles)
    4629             : 
    4630             : {
    4631           2 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
    4632             : 
    4633           2 :     return OGRGeometry::ToHandle(
    4634           2 :         OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
    4635             : }
    4636             : 
    4637             : /************************************************************************/
    4638             : /*                       ConcaveHullOfPolygons()                        */
    4639             : /************************************************************************/
    4640             : 
    4641             : /**
    4642             :  * \brief Compute the concave hull of a set of polygons, respecting
    4643             :  * the polygons as constraints.
    4644             :  *
    4645             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4646             :  * polygons.
    4647             :  * The computed hull "fills the gap" between the polygons,
    4648             :  * and does not intersect their interior.
    4649             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4650             :  * determined by a numeric target parameter.
    4651             :  *
    4652             :  * The concave hull is constructed by removing the longest outer edges
    4653             :  * of the Delaunay Triangulation of the space between the polygons,
    4654             :  * until the target criterion parameter is reached.
    4655             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4656             :  * between polygons to be no larger than this value.
    4657             :  * This can be expressed as a ratio between the lengths of the longest and
    4658             :  * shortest edges.
    4659             :  *
    4660             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4661             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4662             :  * for more details.
    4663             :  *
    4664             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4665             :  * be non-overlapping).
    4666             :  *
    4667             :  * A new geometry object is created and returned containing the concave
    4668             :  * hull of the geometry on which the method is invoked.
    4669             :  *
    4670             :  * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
    4671             :  *
    4672             :  * This method is built on the GEOS >= 3.11 library
    4673             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4674             :  * fail, issuing a CPLE_NotSupported error.
    4675             :  *
    4676             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4677             :  *                      difference between the longest and shortest edge lengths
    4678             :  *                      between the polygons.
    4679             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4680             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4681             :  *                      the original polygons.
    4682             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4683             :  *                 polygons.
    4684             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4685             :  *
    4686             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4687             :  *
    4688             :  * @since GDAL 3.13
    4689             :  * @see OGRGeometry::ConcaveHull()
    4690             :  */
    4691             : 
    4692          13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
    4693             :                                                 bool bIsTight,
    4694             :                                                 bool bAllowHoles) const
    4695             : {
    4696             : #ifndef HAVE_GEOS
    4697             :     (void)dfLengthRatio;
    4698             :     (void)bIsTight;
    4699             :     (void)bAllowHoles;
    4700             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4701             :     return nullptr;
    4702             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4703             :     (void)dfLengthRatio;
    4704             :     (void)bIsTight;
    4705             :     (void)bAllowHoles;
    4706             :     CPLError(CE_Failure, CPLE_NotSupported,
    4707             :              "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
    4708             :     return nullptr;
    4709             : #else
    4710          13 :     OGRGeometry *poOGRProduct = nullptr;
    4711             : 
    4712          13 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4713          13 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4714          13 :     if (hGeosGeom != nullptr)
    4715             :     {
    4716          13 :         GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
    4717             :             hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
    4718          13 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4719             : 
    4720             :         poOGRProduct =
    4721          13 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4722             :     }
    4723          13 :     freeGEOSContext(hGEOSCtxt);
    4724             : 
    4725          13 :     return poOGRProduct;
    4726             : #endif /* HAVE_GEOS */
    4727             : }
    4728             : 
    4729             : /************************************************************************/
    4730             : /*                    OGR_G_ConcaveHullOfPolygons()                     */
    4731             : /************************************************************************/
    4732             : /**
    4733             :  * \brief Compute the concave hull of a set of polygons, respecting
    4734             :  * the polygons as constraints.
    4735             :  *
    4736             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4737             :  * polygons.
    4738             :  * The computed hull "fills the gap" between the polygons,
    4739             :  * and does not intersect their interior.
    4740             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4741             :  * determined by a numeric target parameter.
    4742             :  *
    4743             :  * The concave hull is constructed by removing the longest outer edges
    4744             :  * of the Delaunay Triangulation of the space between the polygons,
    4745             :  * until the target criterion parameter is reached.
    4746             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4747             :  * between polygons to be no larger than this value.
    4748             :  * This can be expressed as a ratio between the lengths of the longest and
    4749             :  * shortest edges.
    4750             :  *
    4751             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4752             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4753             :  * for more details.
    4754             :  *
    4755             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4756             :  * be non-overlapping).
    4757             :  *
    4758             :  * A new geometry object is created and returned containing the concave
    4759             :  * hull of the geometry on which the method is invoked.
    4760             :  *
    4761             :  * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
    4762             :  *
    4763             :  * This function is built on the GEOS >= 3.11 library
    4764             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4765             :  * fail, issuing a CPLE_NotSupported error.
    4766             :  *
    4767             :  * @param hTarget The Geometry to calculate the concave hull of.
    4768             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4769             :  *                      difference between the longest and shortest edge lengths
    4770             :  *                      between the polygons.
    4771             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4772             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4773             :  *                      the original polygons.
    4774             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4775             :  *                 polygons.
    4776             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4777             :  *
    4778             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4779             :  * or NULL if an error occurs.
    4780             :  *
    4781             :  * @since GDAL 3.13
    4782             :  * @see OGR_G_ConcaveHull()
    4783             :  */
    4784             : 
    4785           7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
    4786             :                                          double dfLengthRatio, bool bIsTight,
    4787             :                                          bool bAllowHoles)
    4788             : 
    4789             : {
    4790           7 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
    4791             : 
    4792           7 :     return OGRGeometry::ToHandle(
    4793             :         OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
    4794           7 :             dfLengthRatio, bIsTight, bAllowHoles));
    4795             : }
    4796             : 
    4797             : /************************************************************************/
    4798             : /*                              Boundary()                              */
    4799             : /************************************************************************/
    4800             : 
    4801             : /**
    4802             :  * \brief Compute boundary.
    4803             :  *
    4804             :  * A new geometry object is created and returned containing the boundary
    4805             :  * of the geometry on which the method is invoked.
    4806             :  *
    4807             :  * This method is the same as the C function OGR_G_Boundary().
    4808             :  *
    4809             :  * This method is built on the GEOS library, check it for the definition
    4810             :  * of the geometry operation.
    4811             :  * If OGR is built without the GEOS library, this method will always fail,
    4812             :  * issuing a CPLE_NotSupported error.
    4813             :  *
    4814             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4815             :  *
    4816             :  */
    4817             : 
    4818           6 : OGRGeometry *OGRGeometry::Boundary() const
    4819             : 
    4820             : {
    4821             : #ifndef HAVE_GEOS
    4822             : 
    4823             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4824             :     return nullptr;
    4825             : 
    4826             : #else
    4827             : 
    4828           6 :     OGRGeometry *poOGRProduct = nullptr;
    4829             : 
    4830           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4831           6 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4832           6 :     if (hGeosGeom != nullptr)
    4833             :     {
    4834           6 :         GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
    4835           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4836             : 
    4837             :         poOGRProduct =
    4838           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4839             :     }
    4840           6 :     freeGEOSContext(hGEOSCtxt);
    4841             : 
    4842           6 :     return poOGRProduct;
    4843             : 
    4844             : #endif  // HAVE_GEOS
    4845             : }
    4846             : 
    4847             : //! @cond Doxygen_Suppress
    4848             : /**
    4849             :  * \brief Compute boundary (deprecated)
    4850             :  *
    4851             :  * @deprecated
    4852             :  *
    4853             :  * @see Boundary()
    4854             :  */
    4855           0 : OGRGeometry *OGRGeometry::getBoundary() const
    4856             : 
    4857             : {
    4858           0 :     return Boundary();
    4859             : }
    4860             : 
    4861             : //! @endcond
    4862             : 
    4863             : /************************************************************************/
    4864             : /*                           OGR_G_Boundary()                           */
    4865             : /************************************************************************/
    4866             : /**
    4867             :  * \brief Compute boundary.
    4868             :  *
    4869             :  * A new geometry object is created and returned containing the boundary
    4870             :  * of the geometry on which the method is invoked.
    4871             :  *
    4872             :  * This function is the same as the C++ method OGR_G_Boundary().
    4873             :  *
    4874             :  * This function is built on the GEOS library, check it for the definition
    4875             :  * of the geometry operation.
    4876             :  * If OGR is built without the GEOS library, this function will always fail,
    4877             :  * issuing a CPLE_NotSupported error.
    4878             :  *
    4879             :  * @param hTarget The Geometry to calculate the boundary of.
    4880             :  *
    4881             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4882             :  * or NULL if an error occurs.
    4883             :  *
    4884             :  */
    4885           6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
    4886             : 
    4887             : {
    4888           6 :     VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
    4889             : 
    4890           6 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4891             : }
    4892             : 
    4893             : /**
    4894             :  * \brief Compute boundary (deprecated)
    4895             :  *
    4896             :  * @deprecated
    4897             :  *
    4898             :  * @see OGR_G_Boundary()
    4899             :  */
    4900           0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
    4901             : 
    4902             : {
    4903           0 :     VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
    4904             : 
    4905           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4906             : }
    4907             : 
    4908             : /************************************************************************/
    4909             : /*                               Buffer()                               */
    4910             : /************************************************************************/
    4911             : 
    4912             : /**
    4913             :  * \brief Compute buffer of geometry.
    4914             :  *
    4915             :  * Builds a new geometry containing the buffer region around the geometry
    4916             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4917             :  * the buffer distance of the original geometry.
    4918             :  *
    4919             :  * Some buffer sections are properly described as curves, but are converted to
    4920             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4921             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4922             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4923             :  * large numbers of vertices in the resulting buffer geometry while small
    4924             :  * numbers reduce the accuracy of the result.
    4925             :  *
    4926             :  * This method is the same as the C function OGR_G_Buffer().
    4927             :  *
    4928             :  * This method is built on the GEOS library, check it for the definition
    4929             :  * of the geometry operation.
    4930             :  * If OGR is built without the GEOS library, this method will always fail,
    4931             :  * issuing a CPLE_NotSupported error.
    4932             :  *
    4933             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4934             :  *               the same unit as the coordinates of the geometry.
    4935             :  *
    4936             :  * @param nQuadSegs the number of segments used to approximate a 90
    4937             :  * degree (quadrant) of curvature.
    4938             :  *
    4939             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4940             :  */
    4941             : 
    4942          42 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
    4943             : 
    4944             : {
    4945             :     (void)dfDist;
    4946             :     (void)nQuadSegs;
    4947             : #ifndef HAVE_GEOS
    4948             : 
    4949             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4950             :     return nullptr;
    4951             : 
    4952             : #else
    4953             : 
    4954          42 :     OGRGeometry *poOGRProduct = nullptr;
    4955             : 
    4956          42 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4957          42 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4958          42 :     if (hGeosGeom != nullptr)
    4959             :     {
    4960             :         GEOSGeom hGeosProduct =
    4961          42 :             GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
    4962          42 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4963             : 
    4964             :         poOGRProduct =
    4965          42 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4966             :     }
    4967          42 :     freeGEOSContext(hGEOSCtxt);
    4968             : 
    4969          42 :     return poOGRProduct;
    4970             : 
    4971             : #endif  // HAVE_GEOS
    4972             : }
    4973             : 
    4974             : /************************************************************************/
    4975             : /*                            OGR_G_Buffer()                            */
    4976             : /************************************************************************/
    4977             : 
    4978             : /**
    4979             :  * \brief Compute buffer of geometry.
    4980             :  *
    4981             :  * Builds a new geometry containing the buffer region around the geometry
    4982             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4983             :  * the buffer distance of the original geometry.
    4984             :  *
    4985             :  * Some buffer sections are properly described as curves, but are converted to
    4986             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4987             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4988             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4989             :  * large numbers of vertices in the resulting buffer geometry while small
    4990             :  * numbers reduce the accuracy of the result.
    4991             :  *
    4992             :  * This function is the same as the C++ method OGRGeometry::Buffer().
    4993             :  *
    4994             :  * This function is built on the GEOS library, check it for the definition
    4995             :  * of the geometry operation.
    4996             :  * If OGR is built without the GEOS library, this function will always fail,
    4997             :  * issuing a CPLE_NotSupported error.
    4998             :  *
    4999             :  * @param hTarget the geometry.
    5000             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5001             :  *               the same unit as the coordinates of the geometry.
    5002             :  *
    5003             :  * @param nQuadSegs the number of segments used to approximate a 90 degree
    5004             :  * (quadrant) of curvature.
    5005             :  *
    5006             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5007             :  * or NULL if an error occurs.
    5008             :  */
    5009             : 
    5010          42 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
    5011             : 
    5012             : {
    5013          42 :     VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
    5014             : 
    5015          42 :     return OGRGeometry::ToHandle(
    5016          42 :         OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
    5017             : }
    5018             : 
    5019             : /**
    5020             :  * \brief Compute buffer of geometry.
    5021             :  *
    5022             :  * Builds a new geometry containing the buffer region around the geometry
    5023             :  * on which it is invoked.  The buffer is a polygon containing the region within
    5024             :  * the buffer distance of the original geometry.
    5025             :  *
    5026             :  * This function is built on the GEOS library, check it for the definition
    5027             :  * of the geometry operation.
    5028             :  * If OGR is built without the GEOS library, this function will always fail,
    5029             :  * issuing a CPLE_NotSupported error.
    5030             :  *
    5031             :  * The following options are supported. See the GEOS library for more detailed
    5032             :  * descriptions.
    5033             :  *
    5034             :  * <ul>
    5035             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    5036             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    5037             :  * <li>MITRE_LIMIT=double</li>
    5038             :  * <li>QUADRANT_SEGMENTS=int</li>
    5039             :  * <li>SINGLE_SIDED=YES/NO</li>
    5040             :  * </ul>
    5041             :  *
    5042             :  * This function is the same as the C function OGR_G_BufferEx().
    5043             :  *
    5044             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5045             :  *               the same unit as the coordinates of the geometry.
    5046             :  * @param papszOptions NULL terminated list of options (may be NULL)
    5047             :  *
    5048             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5049             :  *
    5050             :  * @since GDAL 3.10
    5051             :  */
    5052             : 
    5053          35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
    5054             :                                    CSLConstList papszOptions) const
    5055             : {
    5056             :     (void)dfDist;
    5057             :     (void)papszOptions;
    5058             : #ifndef HAVE_GEOS
    5059             : 
    5060             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5061             :     return nullptr;
    5062             : 
    5063             : #else
    5064          35 :     OGRGeometry *poOGRProduct = nullptr;
    5065          35 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5066             : 
    5067          35 :     auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
    5068          35 :     bool bParamsAreValid = true;
    5069             : 
    5070         166 :     for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
    5071             :     {
    5072         131 :         if (EQUAL(pszParam, "ENDCAP_STYLE"))
    5073             :         {
    5074             :             int nStyle;
    5075          25 :             if (EQUAL(pszValue, "ROUND"))
    5076             :             {
    5077          22 :                 nStyle = GEOSBUF_CAP_ROUND;
    5078             :             }
    5079           3 :             else if (EQUAL(pszValue, "FLAT"))
    5080             :             {
    5081           1 :                 nStyle = GEOSBUF_CAP_FLAT;
    5082             :             }
    5083           2 :             else if (EQUAL(pszValue, "SQUARE"))
    5084             :             {
    5085           1 :                 nStyle = GEOSBUF_CAP_SQUARE;
    5086             :             }
    5087             :             else
    5088             :             {
    5089           1 :                 bParamsAreValid = false;
    5090           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    5091             :                          "Invalid value for ENDCAP_STYLE: %s", pszValue);
    5092           2 :                 break;
    5093             :             }
    5094             : 
    5095          24 :             if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
    5096             :             {
    5097           0 :                 bParamsAreValid = false;
    5098             :             }
    5099             :         }
    5100         106 :         else if (EQUAL(pszParam, "JOIN_STYLE"))
    5101             :         {
    5102             :             int nStyle;
    5103          25 :             if (EQUAL(pszValue, "ROUND"))
    5104             :             {
    5105          21 :                 nStyle = GEOSBUF_JOIN_ROUND;
    5106             :             }
    5107           4 :             else if (EQUAL(pszValue, "MITRE"))
    5108             :             {
    5109           3 :                 nStyle = GEOSBUF_JOIN_MITRE;
    5110             :             }
    5111           1 :             else if (EQUAL(pszValue, "BEVEL"))
    5112             :             {
    5113           0 :                 nStyle = GEOSBUF_JOIN_BEVEL;
    5114             :             }
    5115             :             else
    5116             :             {
    5117           1 :                 bParamsAreValid = false;
    5118           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    5119             :                          "Invalid value for JOIN_STYLE: %s", pszValue);
    5120           1 :                 break;
    5121             :             }
    5122             : 
    5123          24 :             if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
    5124             :             {
    5125           0 :                 bParamsAreValid = false;
    5126           0 :                 break;
    5127             :             }
    5128             :         }
    5129          81 :         else if (EQUAL(pszParam, "MITRE_LIMIT"))
    5130             :         {
    5131             :             try
    5132             :             {
    5133             :                 std::size_t end;
    5134          30 :                 double dfLimit = std::stod(pszValue, &end);
    5135             : 
    5136          24 :                 if (end != strlen(pszValue))
    5137             :                 {
    5138           0 :                     throw std::invalid_argument("");
    5139             :                 }
    5140             : 
    5141          24 :                 if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
    5142             :                                                       dfLimit))
    5143             :                 {
    5144           0 :                     bParamsAreValid = false;
    5145           0 :                     break;
    5146             :                 }
    5147             :             }
    5148           4 :             catch (const std::invalid_argument &)
    5149             :             {
    5150           2 :                 bParamsAreValid = false;
    5151           2 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5152             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5153             :             }
    5154           0 :             catch (const std::out_of_range &)
    5155             :             {
    5156           0 :                 bParamsAreValid = false;
    5157           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5158             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5159             :             }
    5160             :         }
    5161          55 :         else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
    5162             :         {
    5163             :             try
    5164             :             {
    5165             :                 std::size_t end;
    5166          38 :                 int nQuadSegs = std::stoi(pszValue, &end, 10);
    5167             : 
    5168          26 :                 if (end != strlen(pszValue))
    5169             :                 {
    5170           0 :                     throw std::invalid_argument("");
    5171             :                 }
    5172             : 
    5173          26 :                 if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
    5174             :                                                             nQuadSegs))
    5175             :                 {
    5176           0 :                     bParamsAreValid = false;
    5177           0 :                     break;
    5178             :                 }
    5179             :             }
    5180           6 :             catch (const std::invalid_argument &)
    5181             :             {
    5182           3 :                 bParamsAreValid = false;
    5183           3 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5184             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5185             :             }
    5186           2 :             catch (const std::out_of_range &)
    5187             :             {
    5188           1 :                 bParamsAreValid = false;
    5189           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5190             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5191             :             }
    5192             :         }
    5193          25 :         else if (EQUAL(pszParam, "SINGLE_SIDED"))
    5194             :         {
    5195          24 :             bool bSingleSided = CPLTestBool(pszValue);
    5196             : 
    5197          24 :             if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
    5198             :                                                    bSingleSided))
    5199             :             {
    5200           0 :                 bParamsAreValid = false;
    5201           0 :                 break;
    5202             :             }
    5203             :         }
    5204             :         else
    5205             :         {
    5206           1 :             bParamsAreValid = false;
    5207           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    5208             :                      "Unsupported buffer option: %s", pszValue);
    5209             :         }
    5210             :     }
    5211             : 
    5212          35 :     if (bParamsAreValid)
    5213             :     {
    5214          26 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    5215          26 :         if (hGeosGeom != nullptr)
    5216             :         {
    5217             :             GEOSGeom hGeosProduct =
    5218          26 :                 GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
    5219          26 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    5220             : 
    5221          26 :             if (hGeosProduct != nullptr)
    5222             :             {
    5223          26 :                 poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
    5224             :                                                      this, nullptr);
    5225             :             }
    5226             :         }
    5227             :     }
    5228             : 
    5229          35 :     GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
    5230          35 :     freeGEOSContext(hGEOSCtxt);
    5231          35 :     return poOGRProduct;
    5232             : #endif
    5233             : }
    5234             : 
    5235             : /**
    5236             :  * \brief Compute buffer of geometry.
    5237             :  *
    5238             :  * Builds a new geometry containing the buffer region around the geometry
    5239             :  * on which it is invoked.  The buffer is a polygon containing the region within
    5240             :  * the buffer distance of the original geometry.
    5241             :  *
    5242             :  * This function is built on the GEOS library, check it for the definition
    5243             :  * of the geometry operation.
    5244             :  * If OGR is built without the GEOS library, this function will always fail,
    5245             :  * issuing a CPLE_NotSupported error.
    5246             :  *
    5247             :  * The following options are supported. See the GEOS library for more detailed
    5248             :  * descriptions.
    5249             :  *
    5250             :  * <ul>
    5251             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    5252             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    5253             :  * <li>MITRE_LIMIT=double</li>
    5254             :  * <li>QUADRANT_SEGMENTS=int</li>
    5255             :  * <li>SINGLE_SIDED=YES/NO</li>
    5256             :  * </ul>
    5257             :  *
    5258             :  * This function is the same as the C++ method OGRGeometry::BufferEx().
    5259             :  *
    5260             :  * @param hTarget the geometry.
    5261             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5262             :  *               the same unit as the coordinates of the geometry.
    5263             :  * @param papszOptions NULL terminated list of options (may be NULL)
    5264             :  *
    5265             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5266             :  * or NULL if an error occurs.
    5267             :  *
    5268             :  * @since GDAL 3.10
    5269             :  */
    5270             : 
    5271          12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
    5272             :                             CSLConstList papszOptions)
    5273             : 
    5274             : {
    5275          12 :     VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
    5276             : 
    5277          12 :     return OGRGeometry::ToHandle(
    5278          12 :         OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
    5279             : }
    5280             : 
    5281             : /************************************************************************/
    5282             : /*                            Intersection()                            */
    5283             : /************************************************************************/
    5284             : 
    5285             : /**
    5286             :  * \brief Compute intersection.
    5287             :  *
    5288             :  * Generates a new geometry which is the region of intersection of the
    5289             :  * two geometries operated on.  The Intersects() method can be used to test if
    5290             :  * two geometries intersect.
    5291             :  *
    5292             :  * Geometry validity is not checked. In case you are unsure of the validity
    5293             :  * of the input geometries, call IsValid() before, otherwise the result might
    5294             :  * be wrong.
    5295             :  *
    5296             :  * This method is the same as the C function OGR_G_Intersection().
    5297             :  *
    5298             :  * This method is built on the GEOS library, check it for the definition
    5299             :  * of the geometry operation.
    5300             :  * If OGR is built without the GEOS library, this method will always fail,
    5301             :  * issuing a CPLE_NotSupported error.
    5302             :  *
    5303             :  * @param poOtherGeom the other geometry intersected with "this" geometry.
    5304             :  *
    5305             :  * @return a new geometry to be freed by the caller, or NULL if there is no
    5306             :  * intersection or if an error occurs.
    5307             :  *
    5308             :  */
    5309             : 
    5310             : OGRGeometry *
    5311        1949 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5312             : 
    5313             : {
    5314        1949 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5315             :     {
    5316             : #ifndef HAVE_SFCGAL
    5317             : 
    5318           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5319           0 :         return nullptr;
    5320             : 
    5321             : #else
    5322             : 
    5323             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5324             :         if (poThis == nullptr)
    5325             :             return nullptr;
    5326             : 
    5327             :         sfcgal_geometry_t *poOther =
    5328             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5329             :         if (poOther == nullptr)
    5330             :         {
    5331             :             sfcgal_geometry_delete(poThis);
    5332             :             return nullptr;
    5333             :         }
    5334             : 
    5335             :         sfcgal_geometry_t *poRes =
    5336             :             sfcgal_geometry_intersection_3d(poThis, poOther);
    5337             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    5338             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5339             :             poOtherGeom->getSpatialReference() != nullptr &&
    5340             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5341             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5342             : 
    5343             :         sfcgal_geometry_delete(poThis);
    5344             :         sfcgal_geometry_delete(poOther);
    5345             :         sfcgal_geometry_delete(poRes);
    5346             : 
    5347             :         return h_prodGeom;
    5348             : 
    5349             : #endif
    5350             :     }
    5351             : 
    5352             :     else
    5353             :     {
    5354             : #ifndef HAVE_GEOS
    5355             : 
    5356             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5357             :         return nullptr;
    5358             : 
    5359             : #else
    5360        1949 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
    5361             : #endif /* HAVE_GEOS */
    5362             :     }
    5363             : }
    5364             : 
    5365             : /************************************************************************/
    5366             : /*                         OGR_G_Intersection()                         */
    5367             : /************************************************************************/
    5368             : 
    5369             : /**
    5370             :  * \brief Compute intersection.
    5371             :  *
    5372             :  * Generates a new geometry which is the region of intersection of the
    5373             :  * two geometries operated on.  The OGR_G_Intersects() function can be used to
    5374             :  * test if two geometries intersect.
    5375             :  *
    5376             :  * Geometry validity is not checked. In case you are unsure of the validity
    5377             :  * of the input geometries, call IsValid() before, otherwise the result might
    5378             :  * be wrong.
    5379             :  *
    5380             :  * This function is the same as the C++ method OGRGeometry::Intersection().
    5381             :  *
    5382             :  * This function is built on the GEOS library, check it for the definition
    5383             :  * of the geometry operation.
    5384             :  * If OGR is built without the GEOS library, this function will always fail,
    5385             :  * issuing a CPLE_NotSupported error.
    5386             :  *
    5387             :  * @param hThis the geometry.
    5388             :  * @param hOther the other geometry.
    5389             :  *
    5390             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5391             :  * or NULL if there is not intersection of if an error occurs.
    5392             :  */
    5393             : 
    5394          12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
    5395             : 
    5396             : {
    5397          12 :     VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
    5398             : 
    5399          24 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
    5400          24 :         OGRGeometry::FromHandle(hOther)));
    5401             : }
    5402             : 
    5403             : /************************************************************************/
    5404             : /*                               Union()                                */
    5405             : /************************************************************************/
    5406             : 
    5407             : /**
    5408             :  * \brief Compute union.
    5409             :  *
    5410             :  * Generates a new geometry which is the region of union of the
    5411             :  * two geometries operated on.
    5412             :  *
    5413             :  * Geometry validity is not checked. In case you are unsure of the validity
    5414             :  * of the input geometries, call IsValid() before, otherwise the result might
    5415             :  * be wrong.
    5416             :  *
    5417             :  * This method is the same as the C function OGR_G_Union().
    5418             :  *
    5419             :  * This method is built on the GEOS library, check it for the definition
    5420             :  * of the geometry operation.
    5421             :  * If OGR is built without the GEOS library, this method will always fail,
    5422             :  * issuing a CPLE_NotSupported error.
    5423             :  *
    5424             :  * @param poOtherGeom the other geometry unioned with "this" geometry.
    5425             :  *
    5426             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5427             :  */
    5428             : 
    5429             : OGRGeometry *
    5430          68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5431             : 
    5432             : {
    5433          68 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5434             :     {
    5435             : #ifndef HAVE_SFCGAL
    5436             : 
    5437           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5438           0 :         return nullptr;
    5439             : 
    5440             : #else
    5441             : 
    5442             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5443             :         if (poThis == nullptr)
    5444             :             return nullptr;
    5445             : 
    5446             :         sfcgal_geometry_t *poOther =
    5447             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5448             :         if (poOther == nullptr)
    5449             :         {
    5450             :             sfcgal_geometry_delete(poThis);
    5451             :             return nullptr;
    5452             :         }
    5453             : 
    5454             :         sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
    5455             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5456             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5457             :             poOtherGeom->getSpatialReference() != nullptr &&
    5458             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5459             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5460             : 
    5461             :         sfcgal_geometry_delete(poThis);
    5462             :         sfcgal_geometry_delete(poOther);
    5463             :         sfcgal_geometry_delete(poRes);
    5464             : 
    5465             :         return h_prodGeom;
    5466             : 
    5467             : #endif
    5468             :     }
    5469             : 
    5470             :     else
    5471             :     {
    5472             : #ifndef HAVE_GEOS
    5473             : 
    5474             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5475             :         return nullptr;
    5476             : 
    5477             : #else
    5478          68 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
    5479             : #endif /* HAVE_GEOS */
    5480             :     }
    5481             : }
    5482             : 
    5483             : /************************************************************************/
    5484             : /*                            OGR_G_Union()                             */
    5485             : /************************************************************************/
    5486             : 
    5487             : /**
    5488             :  * \brief Compute union.
    5489             :  *
    5490             :  * Generates a new geometry which is the region of union of the
    5491             :  * two geometries operated on.
    5492             :  *
    5493             :  * Geometry validity is not checked. In case you are unsure of the validity
    5494             :  * of the input geometries, call IsValid() before, otherwise the result might
    5495             :  * be wrong.
    5496             :  *
    5497             :  * This function is the same as the C++ method OGRGeometry::Union().
    5498             :  *
    5499             :  * This function is built on the GEOS library, check it for the definition
    5500             :  * of the geometry operation.
    5501             :  * If OGR is built without the GEOS library, this function will always fail,
    5502             :  * issuing a CPLE_NotSupported error.
    5503             :  *
    5504             :  * @param hThis the geometry.
    5505             :  * @param hOther the other geometry.
    5506             :  *
    5507             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5508             :  * or NULL if an error occurs.
    5509             :  */
    5510             : 
    5511          10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
    5512             : 
    5513             : {
    5514          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
    5515             : 
    5516          20 :     return OGRGeometry::ToHandle(
    5517          20 :         OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
    5518             : }
    5519             : 
    5520             : /************************************************************************/
    5521             : /*                           UnionCascaded()                            */
    5522             : /************************************************************************/
    5523             : 
    5524             : /**
    5525             :  * \brief Compute union using cascading.
    5526             :  *
    5527             :  * Geometry validity is not checked. In case you are unsure of the validity
    5528             :  * of the input geometries, call IsValid() before, otherwise the result might
    5529             :  * be wrong.
    5530             :  *
    5531             :  * The input geometry must be a MultiPolygon.
    5532             :  *
    5533             :  * This method is the same as the C function OGR_G_UnionCascaded().
    5534             :  *
    5535             :  * This method is built on the GEOS library, check it for the definition
    5536             :  * of the geometry operation.
    5537             :  * If OGR is built without the GEOS library, this method will always fail,
    5538             :  * issuing a CPLE_NotSupported error.
    5539             :  *
    5540             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5541             :  *
    5542             :  *
    5543             :  * @deprecated Use UnaryUnion() instead
    5544             :  */
    5545             : 
    5546           2 : OGRGeometry *OGRGeometry::UnionCascaded() const
    5547             : 
    5548             : {
    5549             : #ifndef HAVE_GEOS
    5550             : 
    5551             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5552             :     return nullptr;
    5553             : #else
    5554             : 
    5555             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5556             :     if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
    5557             :     {
    5558             :         // GEOS < 3.11 crashes on an empty multipolygon input
    5559             :         auto poRet = new OGRGeometryCollection();
    5560             :         poRet->assignSpatialReference(getSpatialReference());
    5561             :         return poRet;
    5562             :     }
    5563             : #endif
    5564           2 :     OGRGeometry *poOGRProduct = nullptr;
    5565             : 
    5566           2 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5567           2 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5568           2 :     if (hThisGeosGeom != nullptr)
    5569             :     {
    5570           2 :         GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
    5571           2 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5572             : 
    5573             :         poOGRProduct =
    5574           2 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5575             :     }
    5576           2 :     freeGEOSContext(hGEOSCtxt);
    5577             : 
    5578           2 :     return poOGRProduct;
    5579             : 
    5580             : #endif  // HAVE_GEOS
    5581             : }
    5582             : 
    5583             : /************************************************************************/
    5584             : /*                        OGR_G_UnionCascaded()                         */
    5585             : /************************************************************************/
    5586             : 
    5587             : /**
    5588             :  * \brief Compute union using cascading.
    5589             :  *
    5590             :  * Geometry validity is not checked. In case you are unsure of the validity
    5591             :  * of the input geometries, call IsValid() before, otherwise the result might
    5592             :  * be wrong.
    5593             :  *
    5594             :  * The input geometry must be a MultiPolygon.
    5595             :  *
    5596             :  * This function is the same as the C++ method OGRGeometry::UnionCascaded().
    5597             :  *
    5598             :  * This function is built on the GEOS library, check it for the definition
    5599             :  * of the geometry operation.
    5600             :  * If OGR is built without the GEOS library, this function will always fail,
    5601             :  * issuing a CPLE_NotSupported error.
    5602             :  *
    5603             :  * @param hThis the geometry.
    5604             :  *
    5605             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5606             :  * or NULL if an error occurs.
    5607             :  *
    5608             :  * @deprecated Use OGR_G_UnaryUnion() instead
    5609             :  */
    5610             : 
    5611           2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
    5612             : 
    5613             : {
    5614           2 :     VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
    5615             : 
    5616           2 :     return OGRGeometry::ToHandle(
    5617           2 :         OGRGeometry::FromHandle(hThis)->UnionCascaded());
    5618             : }
    5619             : 
    5620             : /************************************************************************/
    5621             : /*                             UnaryUnion()                             */
    5622             : /************************************************************************/
    5623             : 
    5624             : /**
    5625             :  * \brief Returns the union of all components of a single geometry.
    5626             :  *
    5627             :  * Usually used to convert a collection into the smallest set of polygons that
    5628             :  * cover the same area.
    5629             :  *
    5630             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5631             :  *
    5632             :  * This method is the same as the C function OGR_G_UnaryUnion().
    5633             :  *
    5634             :  * This method is built on the GEOS library, check it for the definition
    5635             :  * of the geometry operation.
    5636             :  * If OGR is built without the GEOS library, this method will always fail,
    5637             :  * issuing a CPLE_NotSupported error.
    5638             :  *
    5639             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5640             :  *
    5641             :  * @since GDAL 3.7
    5642             :  */
    5643             : 
    5644         629 : OGRGeometry *OGRGeometry::UnaryUnion() const
    5645             : 
    5646             : {
    5647             : #ifndef HAVE_GEOS
    5648             : 
    5649             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5650             :     return nullptr;
    5651             : #else
    5652             : 
    5653             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5654             :     if (IsEmpty())
    5655             :     {
    5656             :         // GEOS < 3.11 crashes on an empty geometry
    5657             :         auto poRet = new OGRGeometryCollection();
    5658             :         poRet->assignSpatialReference(getSpatialReference());
    5659             :         return poRet;
    5660             :     }
    5661             : #endif
    5662         629 :     OGRGeometry *poOGRProduct = nullptr;
    5663             : 
    5664         629 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5665         629 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5666         629 :     if (hThisGeosGeom != nullptr)
    5667             :     {
    5668         629 :         GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
    5669         629 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5670             : 
    5671             :         poOGRProduct =
    5672         629 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5673             :     }
    5674         629 :     freeGEOSContext(hGEOSCtxt);
    5675             : 
    5676         629 :     return poOGRProduct;
    5677             : 
    5678             : #endif  // HAVE_GEOS
    5679             : }
    5680             : 
    5681             : /************************************************************************/
    5682             : /*                          OGR_G_UnaryUnion()                          */
    5683             : /************************************************************************/
    5684             : 
    5685             : /**
    5686             :  * \brief Returns the union of all components of a single geometry.
    5687             :  *
    5688             :  * Usually used to convert a collection into the smallest set of polygons that
    5689             :  * cover the same area.
    5690             :  *
    5691             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5692             :  *
    5693             :  * Geometry validity is not checked. In case you are unsure of the validity
    5694             :  * of the input geometries, call IsValid() before, otherwise the result might
    5695             :  * be wrong.
    5696             :  *
    5697             :  * This function is the same as the C++ method OGRGeometry::UnaryUnion().
    5698             :  *
    5699             :  * This function is built on the GEOS library, check it for the definition
    5700             :  * of the geometry operation.
    5701             :  * If OGR is built without the GEOS library, this function will always fail,
    5702             :  * issuing a CPLE_NotSupported error.
    5703             :  *
    5704             :  * @param hThis the geometry.
    5705             :  *
    5706             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5707             :  * or NULL if an error occurs.
    5708             :  *
    5709             :  * @since GDAL 3.7
    5710             :  */
    5711             : 
    5712           3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
    5713             : 
    5714             : {
    5715           3 :     VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
    5716             : 
    5717           3 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
    5718             : }
    5719             : 
    5720             : /************************************************************************/
    5721             : /*                             Difference()                             */
    5722             : /************************************************************************/
    5723             : 
    5724             : /**
    5725             :  * \brief Compute difference.
    5726             :  *
    5727             :  * Generates a new geometry which is the region of this geometry with the
    5728             :  * region of the second geometry removed.
    5729             :  *
    5730             :  * Geometry validity is not checked. In case you are unsure of the validity
    5731             :  * of the input geometries, call IsValid() before, otherwise the result might
    5732             :  * be wrong.
    5733             :  *
    5734             :  * This method is the same as the C function OGR_G_Difference().
    5735             :  *
    5736             :  * This method is built on the GEOS library, check it for the definition
    5737             :  * of the geometry operation.
    5738             :  * If OGR is built without the GEOS library, this method will always fail,
    5739             :  * issuing a CPLE_NotSupported error.
    5740             :  *
    5741             :  * @param poOtherGeom the other geometry removed from "this" geometry.
    5742             :  *
    5743             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5744             :  * is empty or if an error occurs.
    5745             :  */
    5746             : 
    5747             : OGRGeometry *
    5748         752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5749             : 
    5750             : {
    5751         752 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5752             :     {
    5753             : #ifndef HAVE_SFCGAL
    5754             : 
    5755           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5756           0 :         return nullptr;
    5757             : 
    5758             : #else
    5759             : 
    5760             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5761             :         if (poThis == nullptr)
    5762             :             return nullptr;
    5763             : 
    5764             :         sfcgal_geometry_t *poOther =
    5765             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5766             :         if (poOther == nullptr)
    5767             :         {
    5768             :             sfcgal_geometry_delete(poThis);
    5769             :             return nullptr;
    5770             :         }
    5771             : 
    5772             :         sfcgal_geometry_t *poRes =
    5773             :             sfcgal_geometry_difference_3d(poThis, poOther);
    5774             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5775             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5776             :             poOtherGeom->getSpatialReference() != nullptr &&
    5777             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5778             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5779             : 
    5780             :         sfcgal_geometry_delete(poThis);
    5781             :         sfcgal_geometry_delete(poOther);
    5782             :         sfcgal_geometry_delete(poRes);
    5783             : 
    5784             :         return h_prodGeom;
    5785             : 
    5786             : #endif
    5787             :     }
    5788             : 
    5789             :     else
    5790             :     {
    5791             : #ifndef HAVE_GEOS
    5792             : 
    5793             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5794             :         return nullptr;
    5795             : 
    5796             : #else
    5797         752 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
    5798             : #endif /* HAVE_GEOS */
    5799             :     }
    5800             : }
    5801             : 
    5802             : /************************************************************************/
    5803             : /*                          OGR_G_Difference()                          */
    5804             : /************************************************************************/
    5805             : 
    5806             : /**
    5807             :  * \brief Compute difference.
    5808             :  *
    5809             :  * Generates a new geometry which is the region of this geometry with the
    5810             :  * region of the other geometry removed.
    5811             :  *
    5812             :  * Geometry validity is not checked. In case you are unsure of the validity
    5813             :  * of the input geometries, call IsValid() before, otherwise the result might
    5814             :  * be wrong.
    5815             :  *
    5816             :  * This function is the same as the C++ method OGRGeometry::Difference().
    5817             :  *
    5818             :  * This function is built on the GEOS library, check it for the definition
    5819             :  * of the geometry operation.
    5820             :  * If OGR is built without the GEOS library, this function will always fail,
    5821             :  * issuing a CPLE_NotSupported error.
    5822             :  *
    5823             :  * @param hThis the geometry.
    5824             :  * @param hOther the other geometry.
    5825             :  *
    5826             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5827             :  * or NULL if the difference is empty or if an error occurs.
    5828             :  */
    5829             : 
    5830           6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
    5831             : 
    5832             : {
    5833           6 :     VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
    5834             : 
    5835          12 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
    5836          12 :         OGRGeometry::FromHandle(hOther)));
    5837             : }
    5838             : 
    5839             : /************************************************************************/
    5840             : /*                           SymDifference()                            */
    5841             : /************************************************************************/
    5842             : 
    5843             : /**
    5844             :  * \brief Compute symmetric difference.
    5845             :  *
    5846             :  * Generates a new geometry which is the symmetric difference of this
    5847             :  * geometry and the second geometry passed into the method.
    5848             :  *
    5849             :  * Geometry validity is not checked. In case you are unsure of the validity
    5850             :  * of the input geometries, call IsValid() before, otherwise the result might
    5851             :  * be wrong.
    5852             :  *
    5853             :  * This method is the same as the C function OGR_G_SymDifference().
    5854             :  *
    5855             :  * This method is built on the GEOS library, check it for the definition
    5856             :  * of the geometry operation.
    5857             :  * If OGR is built without the GEOS library, this method will always fail,
    5858             :  * issuing a CPLE_NotSupported error.
    5859             :  *
    5860             :  * @param poOtherGeom the other geometry.
    5861             :  *
    5862             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5863             :  * is empty or if an error occurs.
    5864             :  *
    5865             :  */
    5866             : 
    5867           7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
    5868             : 
    5869             : {
    5870             :     (void)poOtherGeom;
    5871           7 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5872             :     {
    5873             : #ifndef HAVE_SFCGAL
    5874           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5875           0 :         return nullptr;
    5876             : #else
    5877             :         OGRGeometry *poFirstDifference = Difference(poOtherGeom);
    5878             :         if (poFirstDifference == nullptr)
    5879             :             return nullptr;
    5880             : 
    5881             :         OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
    5882             :         if (poOtherDifference == nullptr)
    5883             :         {
    5884             :             delete poFirstDifference;
    5885             :             return nullptr;
    5886             :         }
    5887             : 
    5888             :         OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
    5889             :         delete poFirstDifference;
    5890             :         delete poOtherDifference;
    5891             :         return poSymDiff;
    5892             : #endif
    5893             :     }
    5894             : 
    5895             : #ifndef HAVE_GEOS
    5896             : 
    5897             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5898             :     return nullptr;
    5899             : 
    5900             : #else
    5901           7 :     return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
    5902             : #endif  // HAVE_GEOS
    5903             : }
    5904             : 
    5905             : //! @cond Doxygen_Suppress
    5906             : /**
    5907             :  * \brief Compute symmetric difference (deprecated)
    5908             :  *
    5909             :  * @deprecated
    5910             :  *
    5911             :  * @see OGRGeometry::SymDifference()
    5912             :  */
    5913             : OGRGeometry *
    5914           0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
    5915             : 
    5916             : {
    5917           0 :     return SymDifference(poOtherGeom);
    5918             : }
    5919             : 
    5920             : //! @endcond
    5921             : 
    5922             : /************************************************************************/
    5923             : /*                        OGR_G_SymDifference()                         */
    5924             : /************************************************************************/
    5925             : 
    5926             : /**
    5927             :  * \brief Compute symmetric difference.
    5928             :  *
    5929             :  * Generates a new geometry which is the symmetric difference of this
    5930             :  * geometry and the other geometry.
    5931             :  *
    5932             :  * Geometry validity is not checked. In case you are unsure of the validity
    5933             :  * of the input geometries, call IsValid() before, otherwise the result might
    5934             :  * be wrong.
    5935             :  *
    5936             :  * This function is the same as the C++ method
    5937             :  * OGRGeometry::SymmetricDifference().
    5938             :  *
    5939             :  * This function is built on the GEOS library, check it for the definition
    5940             :  * of the geometry operation.
    5941             :  * If OGR is built without the GEOS library, this function will always fail,
    5942             :  * issuing a CPLE_NotSupported error.
    5943             :  *
    5944             :  * @param hThis the geometry.
    5945             :  * @param hOther the other geometry.
    5946             :  *
    5947             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5948             :  * or NULL if the difference is empty or if an error occurs.
    5949             :  *
    5950             :  */
    5951             : 
    5952           7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5953             : 
    5954             : {
    5955           7 :     VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
    5956             : 
    5957          14 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5958          14 :         OGRGeometry::FromHandle(hOther)));
    5959             : }
    5960             : 
    5961             : /**
    5962             :  * \brief Compute symmetric difference (deprecated)
    5963             :  *
    5964             :  * @deprecated
    5965             :  *
    5966             :  * @see OGR_G_SymmetricDifference()
    5967             :  */
    5968           0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5969             : 
    5970             : {
    5971           0 :     VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
    5972             : 
    5973           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5974           0 :         OGRGeometry::FromHandle(hOther)));
    5975             : }
    5976             : 
    5977             : /************************************************************************/
    5978             : /*                              Disjoint()                              */
    5979             : /************************************************************************/
    5980             : 
    5981             : /**
    5982             :  * \brief Test for disjointness.
    5983             :  *
    5984             :  * Tests if this geometry and the other passed into the method are disjoint.
    5985             :  *
    5986             :  * Geometry validity is not checked. In case you are unsure of the validity
    5987             :  * of the input geometries, call IsValid() before, otherwise the result might
    5988             :  * be wrong.
    5989             :  *
    5990             :  * This method is the same as the C function OGR_G_Disjoint().
    5991             :  *
    5992             :  * This method is built on the GEOS library, check it for the definition
    5993             :  * of the geometry operation.
    5994             :  * If OGR is built without the GEOS library, this method will always fail,
    5995             :  * issuing a CPLE_NotSupported error.
    5996             :  *
    5997             :  * @param poOtherGeom the geometry to compare to this geometry.
    5998             :  *
    5999             :  * @return TRUE if they are disjoint, otherwise FALSE.
    6000             :  */
    6001             : 
    6002           8 : bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
    6003             : 
    6004             : {
    6005             :     (void)poOtherGeom;
    6006             : #ifndef HAVE_GEOS
    6007             : 
    6008             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6009             :     return FALSE;
    6010             : 
    6011             : #else
    6012           8 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
    6013             : #endif  // HAVE_GEOS
    6014             : }
    6015             : 
    6016             : /************************************************************************/
    6017             : /*                           OGR_G_Disjoint()                           */
    6018             : /************************************************************************/
    6019             : 
    6020             : /**
    6021             :  * \brief Test for disjointness.
    6022             :  *
    6023             :  * Tests if this geometry and the other geometry are disjoint.
    6024             :  *
    6025             :  * Geometry validity is not checked. In case you are unsure of the validity
    6026             :  * of the input geometries, call IsValid() before, otherwise the result might
    6027             :  * be wrong.
    6028             :  *
    6029             :  * This function is the same as the C++ method OGRGeometry::Disjoint().
    6030             :  *
    6031             :  * This function is built on the GEOS library, check it for the definition
    6032             :  * of the geometry operation.
    6033             :  * If OGR is built without the GEOS library, this function will always fail,
    6034             :  * issuing a CPLE_NotSupported error.
    6035             :  *
    6036             :  * @param hThis the geometry to compare.
    6037             :  * @param hOther the other geometry to compare.
    6038             :  *
    6039             :  * @return TRUE if they are disjoint, otherwise FALSE.
    6040             :  */
    6041           8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
    6042             : 
    6043             : {
    6044           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
    6045             : 
    6046          16 :     return OGRGeometry::FromHandle(hThis)->Disjoint(
    6047          16 :         OGRGeometry::FromHandle(hOther));
    6048             : }
    6049             : 
    6050             : /************************************************************************/
    6051             : /*                              Touches()                               */
    6052             : /************************************************************************/
    6053             : 
    6054             : /**
    6055             :  * \brief Test for touching.
    6056             :  *
    6057             :  * Tests if this geometry and the other passed into the method are touching.
    6058             :  *
    6059             :  * Geometry validity is not checked. In case you are unsure of the validity
    6060             :  * of the input geometries, call IsValid() before, otherwise the result might
    6061             :  * be wrong.
    6062             :  *
    6063             :  * This method is the same as the C function OGR_G_Touches().
    6064             :  *
    6065             :  * This method is built on the GEOS library, check it for the definition
    6066             :  * of the geometry operation.
    6067             :  * If OGR is built without the GEOS library, this method will always fail,
    6068             :  * issuing a CPLE_NotSupported error.
    6069             :  *
    6070             :  * @param poOtherGeom the geometry to compare to this geometry.
    6071             :  *
    6072             :  * @return TRUE if they are touching, otherwise FALSE.
    6073             :  */
    6074             : 
    6075          11 : bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
    6076             : 
    6077             : {
    6078             :     (void)poOtherGeom;
    6079             : #ifndef HAVE_GEOS
    6080             : 
    6081             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6082             :     return FALSE;
    6083             : 
    6084             : #else
    6085          11 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
    6086             : #endif  // HAVE_GEOS
    6087             : }
    6088             : 
    6089             : /************************************************************************/
    6090             : /*                           OGR_G_Touches()                            */
    6091             : /************************************************************************/
    6092             : /**
    6093             :  * \brief Test for touching.
    6094             :  *
    6095             :  * Tests if this geometry and the other geometry are touching.
    6096             :  *
    6097             :  * Geometry validity is not checked. In case you are unsure of the validity
    6098             :  * of the input geometries, call IsValid() before, otherwise the result might
    6099             :  * be wrong.
    6100             :  *
    6101             :  * This function is the same as the C++ method OGRGeometry::Touches().
    6102             :  *
    6103             :  * This function is built on the GEOS library, check it for the definition
    6104             :  * of the geometry operation.
    6105             :  * If OGR is built without the GEOS library, this function will always fail,
    6106             :  * issuing a CPLE_NotSupported error.
    6107             :  *
    6108             :  * @param hThis the geometry to compare.
    6109             :  * @param hOther the other geometry to compare.
    6110             :  *
    6111             :  * @return TRUE if they are touching, otherwise FALSE.
    6112             :  */
    6113             : 
    6114           8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
    6115             : 
    6116             : {
    6117           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
    6118             : 
    6119          16 :     return OGRGeometry::FromHandle(hThis)->Touches(
    6120          16 :         OGRGeometry::FromHandle(hOther));
    6121             : }
    6122             : 
    6123             : /************************************************************************/
    6124             : /*                              Crosses()                               */
    6125             : /************************************************************************/
    6126             : 
    6127             : /**
    6128             :  * \brief Test for crossing.
    6129             :  *
    6130             :  * Tests if this geometry and the other passed into the method are crossing.
    6131             :  *
    6132             :  * Geometry validity is not checked. In case you are unsure of the validity
    6133             :  * of the input geometries, call IsValid() before, otherwise the result might
    6134             :  * be wrong.
    6135             :  *
    6136             :  * This method is the same as the C function OGR_G_Crosses().
    6137             :  *
    6138             :  * This method is built on the GEOS library, check it for the definition
    6139             :  * of the geometry operation.
    6140             :  * If OGR is built without the GEOS library, this method will always fail,
    6141             :  * issuing a CPLE_NotSupported error.
    6142             :  *
    6143             :  * @param poOtherGeom the geometry to compare to this geometry.
    6144             :  *
    6145             :  * @return TRUE if they are crossing, otherwise FALSE.
    6146             :  */
    6147             : 
    6148           8 : bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    6149             : 
    6150             : {
    6151           8 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    6152             :     {
    6153             : #ifndef HAVE_SFCGAL
    6154             : 
    6155           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    6156           0 :         return FALSE;
    6157             : 
    6158             : #else
    6159             : 
    6160             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    6161             :         if (poThis == nullptr)
    6162             :             return FALSE;
    6163             : 
    6164             :         sfcgal_geometry_t *poOther =
    6165             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    6166             :         if (poOther == nullptr)
    6167             :         {
    6168             :             sfcgal_geometry_delete(poThis);
    6169             :             return FALSE;
    6170             :         }
    6171             : 
    6172             :         int res = sfcgal_geometry_intersects_3d(poThis, poOther);
    6173             : 
    6174             :         sfcgal_geometry_delete(poThis);
    6175             :         sfcgal_geometry_delete(poOther);
    6176             : 
    6177             :         return (res == 1) ? TRUE : FALSE;
    6178             : 
    6179             : #endif
    6180             :     }
    6181             : 
    6182             :     else
    6183             :     {
    6184             : 
    6185             : #ifndef HAVE_GEOS
    6186             : 
    6187             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6188             :         return FALSE;
    6189             : 
    6190             : #else
    6191           8 :         return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
    6192             : #endif /* HAVE_GEOS */
    6193             :     }
    6194             : }
    6195             : 
    6196             : /************************************************************************/
    6197             : /*                           OGR_G_Crosses()                            */
    6198             : /************************************************************************/
    6199             : /**
    6200             :  * \brief Test for crossing.
    6201             :  *
    6202             :  * Tests if this geometry and the other geometry are crossing.
    6203             :  *
    6204             :  * Geometry validity is not checked. In case you are unsure of the validity
    6205             :  * of the input geometries, call IsValid() before, otherwise the result might
    6206             :  * be wrong.
    6207             :  *
    6208             :  * This function is the same as the C++ method OGRGeometry::Crosses().
    6209             :  *
    6210             :  * This function is built on the GEOS library, check it for the definition
    6211             :  * of the geometry operation.
    6212             :  * If OGR is built without the GEOS library, this function will always fail,
    6213             :  * issuing a CPLE_NotSupported error.
    6214             :  *
    6215             :  * @param hThis the geometry to compare.
    6216             :  * @param hOther the other geometry to compare.
    6217             :  *
    6218             :  * @return TRUE if they are crossing, otherwise FALSE.
    6219             :  */
    6220             : 
    6221           8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
    6222             : 
    6223             : {
    6224           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
    6225             : 
    6226          16 :     return OGRGeometry::FromHandle(hThis)->Crosses(
    6227          16 :         OGRGeometry::FromHandle(hOther));
    6228             : }
    6229             : 
    6230             : /************************************************************************/
    6231             : /*                               Within()                               */
    6232             : /************************************************************************/
    6233             : 
    6234             : /**
    6235             :  * \brief Test for containment.
    6236             :  *
    6237             :  * Tests if actual geometry object is within the passed geometry.
    6238             :  *
    6239             :  * Geometry validity is not checked. In case you are unsure of the validity
    6240             :  * of the input geometries, call IsValid() before, otherwise the result might
    6241             :  * be wrong.
    6242             :  *
    6243             :  * This method is the same as the C function OGR_G_Within().
    6244             :  *
    6245             :  * This method is built on the GEOS library, check it for the definition
    6246             :  * of the geometry operation.
    6247             :  * If OGR is built without the GEOS library, this method will always fail,
    6248             :  * issuing a CPLE_NotSupported error.
    6249             :  *
    6250             :  * @param poOtherGeom the geometry to compare to this geometry.
    6251             :  *
    6252             :  * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
    6253             :  */
    6254             : 
    6255       22417 : bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
    6256             : 
    6257             : {
    6258             :     (void)poOtherGeom;
    6259             : #ifndef HAVE_GEOS
    6260             : 
    6261             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6262             :     return FALSE;
    6263             : 
    6264             : #else
    6265       22417 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
    6266             : #endif  // HAVE_GEOS
    6267             : }
    6268             : 
    6269             : /************************************************************************/
    6270             : /*                            OGR_G_Within()                            */
    6271             : /************************************************************************/
    6272             : 
    6273             : /**
    6274             :  * \brief Test for containment.
    6275             :  *
    6276             :  * Tests if this geometry is within the other geometry.
    6277             :  *
    6278             :  * Geometry validity is not checked. In case you are unsure of the validity
    6279             :  * of the input geometries, call IsValid() before, otherwise the result might
    6280             :  * be wrong.
    6281             :  *
    6282             :  * This function is the same as the C++ method OGRGeometry::Within().
    6283             :  *
    6284             :  * This function is built on the GEOS library, check it for the definition
    6285             :  * of the geometry operation.
    6286             :  * If OGR is built without the GEOS library, this function will always fail,
    6287             :  * issuing a CPLE_NotSupported error.
    6288             :  *
    6289             :  * @param hThis the geometry to compare.
    6290             :  * @param hOther the other geometry to compare.
    6291             :  *
    6292             :  * @return TRUE if hThis is within hOther, otherwise FALSE.
    6293             :  */
    6294        7374 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
    6295             : 
    6296             : {
    6297        7374 :     VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
    6298             : 
    6299       14748 :     return OGRGeometry::FromHandle(hThis)->Within(
    6300       14748 :         OGRGeometry::FromHandle(hOther));
    6301             : }
    6302             : 
    6303             : /************************************************************************/
    6304             : /*                              Contains()                              */
    6305             : /************************************************************************/
    6306             : 
    6307             : /**
    6308             :  * \brief Test for containment.
    6309             :  *
    6310             :  * Tests if actual geometry object contains the passed geometry.
    6311             :  *
    6312             :  * Geometry validity is not checked. In case you are unsure of the validity
    6313             :  * of the input geometries, call IsValid() before, otherwise the result might
    6314             :  * be wrong.
    6315             :  *
    6316             :  * This method is the same as the C function OGR_G_Contains().
    6317             :  *
    6318             :  * This method is built on the GEOS library, check it for the definition
    6319             :  * of the geometry operation.
    6320             :  * If OGR is built without the GEOS library, this method will always fail,
    6321             :  * issuing a CPLE_NotSupported error.
    6322             :  *
    6323             :  * @param poOtherGeom the geometry to compare to this geometry.
    6324             :  *
    6325             :  * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
    6326             :  */
    6327             : 
    6328         311 : bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
    6329             : 
    6330             : {
    6331             :     (void)poOtherGeom;
    6332             : #ifndef HAVE_GEOS
    6333             : 
    6334             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6335             :     return FALSE;
    6336             : 
    6337             : #else
    6338         311 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
    6339             : #endif  // HAVE_GEOS
    6340             : }
    6341             : 
    6342             : /************************************************************************/
    6343             : /*                           OGR_G_Contains()                           */
    6344             : /************************************************************************/
    6345             : 
    6346             : /**
    6347             :  * \brief Test for containment.
    6348             :  *
    6349             :  * Tests if this geometry contains the other geometry.
    6350             :  *
    6351             :  * Geometry validity is not checked. In case you are unsure of the validity
    6352             :  * of the input geometries, call IsValid() before, otherwise the result might
    6353             :  * be wrong.
    6354             :  *
    6355             :  * This function is the same as the C++ method OGRGeometry::Contains().
    6356             :  *
    6357             :  * This function is built on the GEOS library, check it for the definition
    6358             :  * of the geometry operation.
    6359             :  * If OGR is built without the GEOS library, this function will always fail,
    6360             :  * issuing a CPLE_NotSupported error.
    6361             :  *
    6362             :  * @param hThis the geometry to compare.
    6363             :  * @param hOther the other geometry to compare.
    6364             :  *
    6365             :  * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
    6366             :  */
    6367          10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
    6368             : 
    6369             : {
    6370          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
    6371             : 
    6372          20 :     return OGRGeometry::FromHandle(hThis)->Contains(
    6373          20 :         OGRGeometry::FromHandle(hOther));
    6374             : }
    6375             : 
    6376             : /************************************************************************/
    6377             : /*                              Overlaps()                              */
    6378             : /************************************************************************/
    6379             : 
    6380             : /**
    6381             :  * \brief Test for overlap.
    6382             :  *
    6383             :  * Tests if this geometry and the other passed into the method overlap, that is
    6384             :  * their intersection has a non-zero area.
    6385             :  *
    6386             :  * Geometry validity is not checked. In case you are unsure of the validity
    6387             :  * of the input geometries, call IsValid() before, otherwise the result might
    6388             :  * be wrong.
    6389             :  *
    6390             :  * This method is the same as the C function OGR_G_Overlaps().
    6391             :  *
    6392             :  * This method is built on the GEOS library, check it for the definition
    6393             :  * of the geometry operation.
    6394             :  * If OGR is built without the GEOS library, this method will always fail,
    6395             :  * issuing a CPLE_NotSupported error.
    6396             :  *
    6397             :  * @param poOtherGeom the geometry to compare to this geometry.
    6398             :  *
    6399             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6400             :  */
    6401             : 
    6402           7 : bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
    6403             : 
    6404             : {
    6405             :     (void)poOtherGeom;
    6406             : #ifndef HAVE_GEOS
    6407             : 
    6408             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6409             :     return FALSE;
    6410             : 
    6411             : #else
    6412           7 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
    6413             : #endif  // HAVE_GEOS
    6414             : }
    6415             : 
    6416             : /************************************************************************/
    6417             : /*                           OGR_G_Overlaps()                           */
    6418             : /************************************************************************/
    6419             : /**
    6420             :  * \brief Test for overlap.
    6421             :  *
    6422             :  * Tests if this geometry and the other geometry overlap, that is their
    6423             :  * intersection has a non-zero area.
    6424             :  *
    6425             :  * Geometry validity is not checked. In case you are unsure of the validity
    6426             :  * of the input geometries, call IsValid() before, otherwise the result might
    6427             :  * be wrong.
    6428             :  *
    6429             :  * This function is the same as the C++ method OGRGeometry::Overlaps().
    6430             :  *
    6431             :  * This function is built on the GEOS library, check it for the definition
    6432             :  * of the geometry operation.
    6433             :  * If OGR is built without the GEOS library, this function will always fail,
    6434             :  * issuing a CPLE_NotSupported error.
    6435             :  *
    6436             :  * @param hThis the geometry to compare.
    6437             :  * @param hOther the other geometry to compare.
    6438             :  *
    6439             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6440             :  */
    6441             : 
    6442           7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
    6443             : 
    6444             : {
    6445           7 :     VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
    6446             : 
    6447          14 :     return OGRGeometry::FromHandle(hThis)->Overlaps(
    6448          14 :         OGRGeometry::FromHandle(hOther));
    6449             : }
    6450             : 
    6451             : /************************************************************************/
    6452             : /*                             closeRings()                             */
    6453             : /************************************************************************/
    6454             : 
    6455             : /**
    6456             :  * \brief Force rings to be closed.
    6457             :  *
    6458             :  * If this geometry, or any contained geometries has polygon rings that
    6459             :  * are not closed, they will be closed by adding the starting point at
    6460             :  * the end.
    6461             :  */
    6462             : 
    6463        1264 : void OGRGeometry::closeRings()
    6464             : {
    6465        1264 : }
    6466             : 
    6467             : /************************************************************************/
    6468             : /*                          OGR_G_CloseRings()                          */
    6469             : /************************************************************************/
    6470             : 
    6471             : /**
    6472             :  * \brief Force rings to be closed.
    6473             :  *
    6474             :  * If this geometry, or any contained geometries has polygon rings that
    6475             :  * are not closed, they will be closed by adding the starting point at
    6476             :  * the end.
    6477             :  *
    6478             :  * @param hGeom handle to the geometry.
    6479             :  */
    6480             : 
    6481           6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
    6482             : 
    6483             : {
    6484           6 :     VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
    6485             : 
    6486           6 :     OGRGeometry::FromHandle(hGeom)->closeRings();
    6487             : }
    6488             : 
    6489             : /************************************************************************/
    6490             : /*                              Centroid()                              */
    6491             : /************************************************************************/
    6492             : 
    6493             : /**
    6494             :  * \brief Compute the geometry centroid.
    6495             :  *
    6496             :  * The centroid location is applied to the passed in OGRPoint object.
    6497             :  * The centroid is not necessarily within the geometry.
    6498             :  *
    6499             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6500             :  * however the current implementation based on GEOS can operate on other
    6501             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6502             :  * multipolygons.
    6503             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6504             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6505             :  * (multipolygons).
    6506             :  *
    6507             :  * This function is the same as the C function OGR_G_Centroid().
    6508             :  *
    6509             :  * This function is built on the GEOS library, check it for the definition
    6510             :  * of the geometry operation.
    6511             :  * If OGR is built without the GEOS library, this function will always fail,
    6512             :  * issuing a CPLE_NotSupported error.
    6513             :  *
    6514             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6515             :  *
    6516             :  * to OGRPolygon)
    6517             :  */
    6518             : 
    6519           5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
    6520             : 
    6521             : {
    6522           5 :     if (poPoint == nullptr)
    6523           0 :         return OGRERR_FAILURE;
    6524             : 
    6525             : #ifndef HAVE_GEOS
    6526             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6527             :     return OGRERR_FAILURE;
    6528             : 
    6529             : #else
    6530             : 
    6531           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6532             :     GEOSGeom hThisGeosGeom =
    6533           5 :         exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
    6534             : 
    6535           5 :     if (hThisGeosGeom != nullptr)
    6536             :     {
    6537           5 :         GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
    6538           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6539             : 
    6540           5 :         if (hOtherGeosGeom == nullptr)
    6541             :         {
    6542           0 :             freeGEOSContext(hGEOSCtxt);
    6543           0 :             return OGRERR_FAILURE;
    6544             :         }
    6545             : 
    6546             :         OGRGeometry *poCentroidGeom =
    6547           5 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6548             : 
    6549           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6550             : 
    6551           5 :         if (poCentroidGeom == nullptr)
    6552             :         {
    6553           0 :             freeGEOSContext(hGEOSCtxt);
    6554           0 :             return OGRERR_FAILURE;
    6555             :         }
    6556           5 :         if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6557             :         {
    6558           0 :             delete poCentroidGeom;
    6559           0 :             freeGEOSContext(hGEOSCtxt);
    6560           0 :             return OGRERR_FAILURE;
    6561             :         }
    6562             : 
    6563           5 :         if (getSpatialReference() != nullptr)
    6564           0 :             poCentroidGeom->assignSpatialReference(getSpatialReference());
    6565             : 
    6566           5 :         OGRPoint *poCentroid = poCentroidGeom->toPoint();
    6567             : 
    6568           5 :         if (!poCentroid->IsEmpty())
    6569             :         {
    6570           4 :             poPoint->setX(poCentroid->getX());
    6571           4 :             poPoint->setY(poCentroid->getY());
    6572             :         }
    6573             :         else
    6574             :         {
    6575           1 :             poPoint->empty();
    6576             :         }
    6577             : 
    6578           5 :         delete poCentroidGeom;
    6579             : 
    6580           5 :         freeGEOSContext(hGEOSCtxt);
    6581           5 :         return OGRERR_NONE;
    6582             :     }
    6583             :     else
    6584             :     {
    6585           0 :         freeGEOSContext(hGEOSCtxt);
    6586           0 :         return OGRERR_FAILURE;
    6587             :     }
    6588             : 
    6589             : #endif  // HAVE_GEOS
    6590             : }
    6591             : 
    6592             : /************************************************************************/
    6593             : /*                           OGR_G_Centroid()                           */
    6594             : /************************************************************************/
    6595             : 
    6596             : /**
    6597             :  * \brief Compute the geometry centroid.
    6598             :  *
    6599             :  * The centroid location is applied to the passed in OGRPoint object.
    6600             :  * The centroid is not necessarily within the geometry.
    6601             :  *
    6602             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6603             :  * however the current implementation based on GEOS can operate on other
    6604             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6605             :  * multipolygons.
    6606             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6607             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6608             :  * (multipolygons).
    6609             :  *
    6610             :  * This function is the same as the C++ method OGRGeometry::Centroid().
    6611             :  *
    6612             :  * This function is built on the GEOS library, check it for the definition
    6613             :  * of the geometry operation.
    6614             :  * If OGR is built without the GEOS library, this function will always fail,
    6615             :  * issuing a CPLE_NotSupported error.
    6616             :  *
    6617             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6618             :  */
    6619             : 
    6620           5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
    6621             : 
    6622             : {
    6623           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
    6624             : 
    6625           5 :     OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
    6626           5 :     if (poCentroidGeom == nullptr)
    6627           0 :         return OGRERR_FAILURE;
    6628           5 :     if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6629             :     {
    6630           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6631             :                  "Passed wrong geometry type as centroid argument.");
    6632           0 :         return OGRERR_FAILURE;
    6633             :     }
    6634             : 
    6635           5 :     return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
    6636             : }
    6637             : 
    6638             : /************************************************************************/
    6639             : /*                        OGR_G_PointOnSurface()                        */
    6640             : /************************************************************************/
    6641             : 
    6642             : /**
    6643             :  * \brief Returns a point guaranteed to lie on the surface.
    6644             :  *
    6645             :  * This method relates to the SFCOM ISurface::get_PointOnSurface() method
    6646             :  * however the current implementation based on GEOS can operate on other
    6647             :  * geometry types than the types that are supported by SQL/MM-Part 3 :
    6648             :  * surfaces (polygons) and multisurfaces (multipolygons).
    6649             :  *
    6650             :  * This method is built on the GEOS library, check it for the definition
    6651             :  * of the geometry operation.
    6652             :  * If OGR is built without the GEOS library, this method will always fail,
    6653             :  * issuing a CPLE_NotSupported error.
    6654             :  *
    6655             :  * @param hGeom the geometry to operate on.
    6656             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6657             :  * or NULL if an error occurs.
    6658             :  *
    6659             :  */
    6660             : 
    6661           4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
    6662             : 
    6663             : {
    6664           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
    6665             : 
    6666             : #ifndef HAVE_GEOS
    6667             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6668             :     return nullptr;
    6669             : #else
    6670             : 
    6671           4 :     OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
    6672             : 
    6673           4 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    6674           4 :     GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
    6675             : 
    6676           4 :     if (hThisGeosGeom != nullptr)
    6677             :     {
    6678             :         GEOSGeom hOtherGeosGeom =
    6679           4 :             GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
    6680           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6681             : 
    6682           4 :         if (hOtherGeosGeom == nullptr)
    6683             :         {
    6684           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6685           0 :             return nullptr;
    6686             :         }
    6687             : 
    6688             :         OGRGeometry *poInsidePointGeom =
    6689           4 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6690             : 
    6691           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6692             : 
    6693           4 :         if (poInsidePointGeom == nullptr)
    6694             :         {
    6695           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6696           0 :             return nullptr;
    6697             :         }
    6698           4 :         if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
    6699             :         {
    6700           0 :             delete poInsidePointGeom;
    6701           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6702           0 :             return nullptr;
    6703             :         }
    6704             : 
    6705           4 :         if (poThis->getSpatialReference() != nullptr)
    6706           0 :             poInsidePointGeom->assignSpatialReference(
    6707           0 :                 poThis->getSpatialReference());
    6708             : 
    6709           4 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6710           4 :         return OGRGeometry::ToHandle(poInsidePointGeom);
    6711             :     }
    6712             : 
    6713           0 :     OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6714           0 :     return nullptr;
    6715             : #endif
    6716             : }
    6717             : 
    6718             : /************************************************************************/
    6719             : /*                       PointOnSurfaceInternal()                       */
    6720             : /************************************************************************/
    6721             : 
    6722             : //! @cond Doxygen_Suppress
    6723           0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
    6724             : {
    6725           0 :     if (poPoint == nullptr || poPoint->IsEmpty())
    6726           0 :         return OGRERR_FAILURE;
    6727             : 
    6728           0 :     OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
    6729             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
    6730           0 :     if (hInsidePoint == nullptr)
    6731           0 :         return OGRERR_FAILURE;
    6732             : 
    6733           0 :     OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
    6734           0 :     if (poInsidePoint->IsEmpty())
    6735             :     {
    6736           0 :         poPoint->empty();
    6737             :     }
    6738             :     else
    6739             :     {
    6740           0 :         poPoint->setX(poInsidePoint->getX());
    6741           0 :         poPoint->setY(poInsidePoint->getY());
    6742             :     }
    6743             : 
    6744           0 :     OGR_G_DestroyGeometry(hInsidePoint);
    6745             : 
    6746           0 :     return OGRERR_NONE;
    6747             : }
    6748             : 
    6749             : //! @endcond
    6750             : 
    6751             : /************************************************************************/
    6752             : /*                              Simplify()                              */
    6753             : /************************************************************************/
    6754             : 
    6755             : /**
    6756             :  * \brief Simplify the geometry.
    6757             :  *
    6758             :  * This function is the same as the C function OGR_G_Simplify().
    6759             :  *
    6760             :  * This function is built on the GEOS library, check it for the definition
    6761             :  * of the geometry operation.
    6762             :  * If OGR is built without the GEOS library, this function will always fail,
    6763             :  * issuing a CPLE_NotSupported error.
    6764             :  *
    6765             :  * @param dTolerance the distance tolerance for the simplification.
    6766             :  *
    6767             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6768             :  *
    6769             :  */
    6770             : 
    6771          55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
    6772             : 
    6773             : {
    6774             :     (void)dTolerance;
    6775             : #ifndef HAVE_GEOS
    6776             : 
    6777             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6778             :     return nullptr;
    6779             : 
    6780             : #else
    6781          55 :     OGRGeometry *poOGRProduct = nullptr;
    6782             : 
    6783          55 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6784          55 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6785          55 :     if (hThisGeosGeom != nullptr)
    6786             :     {
    6787             :         GEOSGeom hGeosProduct =
    6788          55 :             GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
    6789          55 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6790             :         poOGRProduct =
    6791          55 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6792             :     }
    6793          55 :     freeGEOSContext(hGEOSCtxt);
    6794          55 :     return poOGRProduct;
    6795             : 
    6796             : #endif  // HAVE_GEOS
    6797             : }
    6798             : 
    6799             : /************************************************************************/
    6800             : /*                           OGR_G_Simplify()                           */
    6801             : /************************************************************************/
    6802             : 
    6803             : /**
    6804             :  * \brief Compute a simplified geometry.
    6805             :  *
    6806             :  * This function is the same as the C++ method OGRGeometry::Simplify().
    6807             :  *
    6808             :  * This function is built on the GEOS library, check it for the definition
    6809             :  * of the geometry operation.
    6810             :  * If OGR is built without the GEOS library, this function will always fail,
    6811             :  * issuing a CPLE_NotSupported error.
    6812             :  *
    6813             :  * @param hThis the geometry.
    6814             :  * @param dTolerance the distance tolerance for the simplification.
    6815             :  *
    6816             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6817             :  * or NULL if an error occurs.
    6818             :  *
    6819             :  */
    6820             : 
    6821           1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
    6822             : 
    6823             : {
    6824           1 :     VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
    6825           1 :     return OGRGeometry::ToHandle(
    6826           1 :         OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
    6827             : }
    6828             : 
    6829             : /************************************************************************/
    6830             : /*                      SimplifyPreserveTopology()                      */
    6831             : /************************************************************************/
    6832             : 
    6833             : /**
    6834             :  * \brief Simplify the geometry while preserving topology.
    6835             :  *
    6836             :  * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
    6837             :  *
    6838             :  * This function is built on the GEOS library, check it for the definition
    6839             :  * of the geometry operation.
    6840             :  * If OGR is built without the GEOS library, this function will always fail,
    6841             :  * issuing a CPLE_NotSupported error.
    6842             :  *
    6843             :  * @param dTolerance the distance tolerance for the simplification.
    6844             :  *
    6845             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6846             :  *
    6847             :  */
    6848             : 
    6849          17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
    6850             : 
    6851             : {
    6852             :     (void)dTolerance;
    6853             : #ifndef HAVE_GEOS
    6854             : 
    6855             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6856             :     return nullptr;
    6857             : 
    6858             : #else
    6859          17 :     OGRGeometry *poOGRProduct = nullptr;
    6860             : 
    6861          17 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6862          17 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6863          17 :     if (hThisGeosGeom != nullptr)
    6864             :     {
    6865          17 :         GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
    6866             :             hGEOSCtxt, hThisGeosGeom, dTolerance);
    6867          17 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6868             :         poOGRProduct =
    6869          17 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6870             :     }
    6871          17 :     freeGEOSContext(hGEOSCtxt);
    6872          17 :     return poOGRProduct;
    6873             : 
    6874             : #endif  // HAVE_GEOS
    6875             : }
    6876             : 
    6877             : /************************************************************************/
    6878             : /*                   OGR_G_SimplifyPreserveTopology()                   */
    6879             : /************************************************************************/
    6880             : 
    6881             : /**
    6882             :  * \brief Simplify the geometry while preserving topology.
    6883             :  *
    6884             :  * This function is the same as the C++ method
    6885             :  * OGRGeometry::SimplifyPreserveTopology().
    6886             :  *
    6887             :  * This function is built on the GEOS library, check it for the definition
    6888             :  * of the geometry operation.
    6889             :  * If OGR is built without the GEOS library, this function will always fail,
    6890             :  * issuing a CPLE_NotSupported error.
    6891             :  *
    6892             :  * @param hThis the geometry.
    6893             :  * @param dTolerance the distance tolerance for the simplification.
    6894             :  *
    6895             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6896             :  * or NULL if an error occurs.
    6897             :  *
    6898             :  */
    6899             : 
    6900           1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
    6901             :                                             double dTolerance)
    6902             : 
    6903             : {
    6904           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
    6905           1 :     return OGRGeometry::ToHandle(
    6906           1 :         OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
    6907             : }
    6908             : 
    6909             : /************************************************************************/
    6910             : /*                          roundCoordinates()                          */
    6911             : /************************************************************************/
    6912             : 
    6913             : /** Round coordinates of the geometry to the specified precision.
    6914             :  *
    6915             :  * Note that this is not the same as OGRGeometry::SetPrecision(). The later
    6916             :  * will return valid geometries, whereas roundCoordinates() does not make
    6917             :  * such guarantee and may return geometries with invalidities, if they are
    6918             :  * not compatible of the specified precision. roundCoordinates() supports
    6919             :  * curve geometries, whereas SetPrecision() does not currently.
    6920             :  *
    6921             :  * One use case for roundCoordinates() is to undo the effect of
    6922             :  * quantizeCoordinates().
    6923             :  *
    6924             :  * @param sPrecision Contains the precision requirements.
    6925             :  * @since GDAL 3.9
    6926             :  */
    6927          39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
    6928             : {
    6929             :     struct Rounder : public OGRDefaultGeometryVisitor
    6930             :     {
    6931             :         const OGRGeomCoordinatePrecision &m_precision;
    6932             :         const double m_invXYResolution;
    6933             :         const double m_invZResolution;
    6934             :         const double m_invMResolution;
    6935             : 
    6936          39 :         explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
    6937          39 :             : m_precision(sPrecisionIn),
    6938          39 :               m_invXYResolution(m_precision.dfXYResolution !=
    6939             :                                         OGRGeomCoordinatePrecision::UNKNOWN
    6940          39 :                                     ? 1.0 / m_precision.dfXYResolution
    6941             :                                     : 0.0),
    6942          39 :               m_invZResolution(m_precision.dfZResolution !=
    6943             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6944          39 :                                    ? 1.0 / m_precision.dfZResolution
    6945             :                                    : 0.0),
    6946          39 :               m_invMResolution(m_precision.dfMResolution !=
    6947             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6948          39 :                                    ? 1.0 / m_precision.dfMResolution
    6949         117 :                                    : 0.0)
    6950             :         {
    6951          39 :         }
    6952             : 
    6953             :         using OGRDefaultGeometryVisitor::visit;
    6954             : 
    6955         379 :         void visit(OGRPoint *poPoint) override
    6956             :         {
    6957         379 :             if (m_precision.dfXYResolution !=
    6958             :                 OGRGeomCoordinatePrecision::UNKNOWN)
    6959             :             {
    6960         379 :                 poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
    6961         379 :                               m_precision.dfXYResolution);
    6962         379 :                 poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
    6963         379 :                               m_precision.dfXYResolution);
    6964             :             }
    6965         758 :             if (m_precision.dfZResolution !=
    6966         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6967           4 :                 poPoint->Is3D())
    6968             :             {
    6969           4 :                 poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
    6970           4 :                               m_precision.dfZResolution);
    6971             :             }
    6972         758 :             if (m_precision.dfMResolution !=
    6973         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6974           4 :                 poPoint->IsMeasured())
    6975             :             {
    6976           4 :                 poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
    6977           4 :                               m_precision.dfMResolution);
    6978             :             }
    6979         379 :         }
    6980             :     };
    6981             : 
    6982          78 :     Rounder rounder(sPrecision);
    6983          39 :     accept(&rounder);
    6984          39 : }
    6985             : 
    6986             : /************************************************************************/
    6987             : /*                            SetPrecision()                            */
    6988             : /************************************************************************/
    6989             : 
    6990             : /** Set the geometry's precision, rounding all its coordinates to the precision
    6991             :  * grid, and making sure the geometry is still valid.
    6992             :  *
    6993             :  * This is a stronger version of roundCoordinates().
    6994             :  *
    6995             :  * Note that at time of writing GEOS does no supported curve geometries. So
    6996             :  * currently if this function is called on such a geometry, OGR will first call
    6997             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    6998             :  * that it is unlikely to yield to the expected result.
    6999             :  *
    7000             :  * This function is the same as the C function OGR_G_SetPrecision().
    7001             :  *
    7002             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    7003             :  * GEOS library. Check it for the definition of the geometry operation.
    7004             :  * If OGR is built without the GEOS library, this function will always fail,
    7005             :  * issuing a CPLE_NotSupported error.
    7006             :  *
    7007             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    7008             :  *                 precision.
    7009             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    7010             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    7011             :  *
    7012             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7013             :  *
    7014             :  * @since GDAL 3.9
    7015             :  */
    7016             : 
    7017           6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
    7018             : {
    7019             :     (void)dfGridSize;
    7020             :     (void)nFlags;
    7021             : #ifndef HAVE_GEOS
    7022             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7023             :     return nullptr;
    7024             : 
    7025             : #else
    7026           6 :     OGRGeometry *poOGRProduct = nullptr;
    7027             : 
    7028           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7029           6 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7030           6 :     if (hThisGeosGeom != nullptr)
    7031             :     {
    7032           6 :         GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
    7033             :             hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
    7034           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7035             :         poOGRProduct =
    7036           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7037             :     }
    7038           6 :     freeGEOSContext(hGEOSCtxt);
    7039           6 :     return poOGRProduct;
    7040             : 
    7041             : #endif  // HAVE_GEOS
    7042             : }
    7043             : 
    7044             : /************************************************************************/
    7045             : /*                         OGR_G_SetPrecision()                         */
    7046             : /************************************************************************/
    7047             : 
    7048             : /** Set the geometry's precision, rounding all its coordinates to the precision
    7049             :  * grid, and making sure the geometry is still valid.
    7050             :  *
    7051             :  * This is a stronger version of roundCoordinates().
    7052             :  *
    7053             :  * Note that at time of writing GEOS does no supported curve geometries. So
    7054             :  * currently if this function is called on such a geometry, OGR will first call
    7055             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    7056             :  * that it is unlikely to yield to the expected result.
    7057             :  *
    7058             :  * This function is the same as the C++ method OGRGeometry::SetPrecision().
    7059             :  *
    7060             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    7061             :  * GEOS library. Check it for the definition of the geometry operation.
    7062             :  * If OGR is built without the GEOS library, this function will always fail,
    7063             :  * issuing a CPLE_NotSupported error.
    7064             :  *
    7065             :  * @param hThis the geometry.
    7066             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    7067             :  *                 precision.
    7068             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    7069             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    7070             :  *
    7071             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7072             :  * or NULL if an error occurs.
    7073             :  *
    7074             :  * @since GDAL 3.9
    7075             :  */
    7076           1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
    7077             :                                 int nFlags)
    7078             : {
    7079           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
    7080           1 :     return OGRGeometry::ToHandle(
    7081           1 :         OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
    7082             : }
    7083             : 
    7084             : /************************************************************************/
    7085             : /*                       DelaunayTriangulation()                        */
    7086             : /************************************************************************/
    7087             : 
    7088             : /**
    7089             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    7090             :  *
    7091             :  * This function is the same as the C function OGR_G_DelaunayTriangulation().
    7092             :  *
    7093             :  * This function is built on the GEOS library, v3.4 or above.
    7094             :  * If OGR is built without the GEOS library, this function will always fail,
    7095             :  * issuing a CPLE_NotSupported error.
    7096             :  *
    7097             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    7098             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    7099             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    7100             :  *
    7101             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7102             :  */
    7103             : 
    7104             : #ifndef HAVE_GEOS
    7105             : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
    7106             :                                                 int /*bOnlyEdges*/) const
    7107             : {
    7108             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7109             :     return nullptr;
    7110             : }
    7111             : #else
    7112           1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
    7113             :                                                 int bOnlyEdges) const
    7114             : {
    7115           1 :     OGRGeometry *poOGRProduct = nullptr;
    7116             : 
    7117           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7118           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7119           1 :     if (hThisGeosGeom != nullptr)
    7120             :     {
    7121           1 :         GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
    7122             :             hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
    7123           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7124             :         poOGRProduct =
    7125           1 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7126             :     }
    7127           1 :     freeGEOSContext(hGEOSCtxt);
    7128           1 :     return poOGRProduct;
    7129             : }
    7130             : #endif
    7131             : 
    7132             : /************************************************************************/
    7133             : /*                    OGR_G_DelaunayTriangulation()                     */
    7134             : /************************************************************************/
    7135             : 
    7136             : /**
    7137             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    7138             :  *
    7139             :  * This function is the same as the C++ method
    7140             :  * OGRGeometry::DelaunayTriangulation().
    7141             :  *
    7142             :  * This function is built on the GEOS library, v3.4 or above.
    7143             :  * If OGR is built without the GEOS library, this function will always fail,
    7144             :  * issuing a CPLE_NotSupported error.
    7145             :  *
    7146             :  * @param hThis the geometry.
    7147             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    7148             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    7149             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    7150             :  *
    7151             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7152             :  * or NULL if an error occurs.
    7153             :  */
    7154             : 
    7155           1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
    7156             :                                          int bOnlyEdges)
    7157             : 
    7158             : {
    7159           1 :     VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
    7160             : 
    7161           1 :     return OGRGeometry::ToHandle(
    7162             :         OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
    7163           1 :                                                               bOnlyEdges));
    7164             : }
    7165             : 
    7166             : /************************************************************************/
    7167             : /*                  ConstrainedDelaunayTriangulation()                  */
    7168             : /************************************************************************/
    7169             : 
    7170             : /**
    7171             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7172             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7173             :  * geometry collection.
    7174             :  *
    7175             :  * This function is the same as the C function
    7176             :  * OGR_G_ConstrainedDelaunayTriangulation().
    7177             :  *
    7178             :  * This function is built on the GEOS library, v3.10 or above.
    7179             :  * If OGR is built without the GEOS library, this function will always fail,
    7180             :  * issuing a CPLE_NotSupported error.
    7181             :  *
    7182             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7183             :  *
    7184             :  * @since OGR 3.12
    7185             :  */
    7186             : 
    7187           3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
    7188             : {
    7189             : #ifndef HAVE_GEOS
    7190             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7191             :     return nullptr;
    7192             : #elif !(GEOS_VERSION_MAJOR > 3 ||                                              \
    7193             :         (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    7194             :     CPLError(
    7195             :         CE_Failure, CPLE_NotSupported,
    7196             :         "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
    7197             :     return nullptr;
    7198             : #else
    7199             : 
    7200           3 :     OGRGeometry *poOGRProduct = nullptr;
    7201             : 
    7202           3 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7203           3 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7204           3 :     if (hThisGeosGeom != nullptr)
    7205             :     {
    7206             :         GEOSGeom hGeosProduct =
    7207           3 :             GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
    7208           3 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7209             :         poOGRProduct =
    7210           3 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7211             :     }
    7212           3 :     freeGEOSContext(hGEOSCtxt);
    7213           3 :     return poOGRProduct;
    7214             : #endif
    7215             : }
    7216             : 
    7217             : /************************************************************************/
    7218             : /*               OGR_G_ConstrainedDelaunayTriangulation()               */
    7219             : /************************************************************************/
    7220             : 
    7221             : /**
    7222             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7223             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7224             :  * geometry collection.
    7225             :  *
    7226             :  * This function is the same as the C++ method
    7227             :  * OGRGeometry::ConstrainedDelaunayTriangulation().
    7228             :  *
    7229             :  * This function is built on the GEOS library, v3.10 or above.
    7230             :  * If OGR is built without the GEOS library, this function will always fail,
    7231             :  * issuing a CPLE_NotSupported error.
    7232             :  *
    7233             :  * @param hThis the geometry.
    7234             :  *
    7235             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7236             :  * or NULL if an error occurs.
    7237             :  *
    7238             :  * @since OGR 3.12
    7239             :  */
    7240             : 
    7241           3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
    7242             : {
    7243           3 :     VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
    7244             : 
    7245           3 :     return OGRGeometry::ToHandle(
    7246           3 :         OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
    7247             : }
    7248             : 
    7249             : /************************************************************************/
    7250             : /*                             Polygonize()                             */
    7251             : /************************************************************************/
    7252             : /* Contributor: Alessandro Furieri, a.furieri@lqt.it                    */
    7253             : /* Developed for Faunalia (http://www.faunalia.it) with funding from    */
    7254             : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED        */
    7255             : /*                   AMBIENTALE                                         */
    7256             : /************************************************************************/
    7257             : 
    7258             : /**
    7259             :  * \brief Polygonizes a set of sparse edges.
    7260             :  *
    7261             :  * A new geometry object is created and returned containing a collection
    7262             :  * of reassembled Polygons: NULL will be returned if the input collection
    7263             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7264             :  * into Polygons is impossible due to topological inconsistencies.
    7265             :  *
    7266             :  * This method is the same as the C function OGR_G_Polygonize().
    7267             :  *
    7268             :  * This method is built on the GEOS library, check it for the definition
    7269             :  * of the geometry operation.
    7270             :  * If OGR is built without the GEOS library, this method will always fail,
    7271             :  * issuing a CPLE_NotSupported error.
    7272             :  *
    7273             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7274             :  *
    7275             :  */
    7276             : 
    7277         117 : OGRGeometry *OGRGeometry::Polygonize() const
    7278             : 
    7279             : {
    7280             : #ifndef HAVE_GEOS
    7281             : 
    7282             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7283             :     return nullptr;
    7284             : 
    7285             : #else
    7286             : 
    7287         117 :     const OGRGeometryCollection *poColl = nullptr;
    7288         233 :     if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
    7289         116 :         wkbFlatten(getGeometryType()) == wkbMultiLineString)
    7290         116 :         poColl = toGeometryCollection();
    7291             :     else
    7292           1 :         return nullptr;
    7293             : 
    7294         116 :     const int nCount = poColl->getNumGeometries();
    7295             : 
    7296         116 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7297         116 :     bool bError = false;
    7298             : 
    7299         116 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7300             : 
    7301         116 :     GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
    7302         747 :     for (int ig = 0; ig < nCount; ig++)
    7303             :     {
    7304         631 :         GEOSGeom hGeosGeom = nullptr;
    7305         631 :         const OGRGeometry *poChild = poColl->getGeometryRef(ig);
    7306        1262 :         if (poChild == nullptr ||
    7307         631 :             wkbFlatten(poChild->getGeometryType()) != wkbLineString)
    7308           1 :             bError = true;
    7309             :         else
    7310             :         {
    7311         630 :             hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
    7312         630 :             if (hGeosGeom == nullptr)
    7313           0 :                 bError = true;
    7314             :         }
    7315         631 :         pahGeosGeomList[ig] = hGeosGeom;
    7316             :     }
    7317             : 
    7318         116 :     if (!bError)
    7319             :     {
    7320             :         GEOSGeom hGeosPolygs =
    7321         115 :             GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
    7322             : 
    7323             :         poPolygsOGRGeom =
    7324         115 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7325             :     }
    7326             : 
    7327         747 :     for (int ig = 0; ig < nCount; ig++)
    7328             :     {
    7329         631 :         GEOSGeom hGeosGeom = pahGeosGeomList[ig];
    7330         631 :         if (hGeosGeom != nullptr)
    7331         630 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    7332             :     }
    7333         116 :     delete[] pahGeosGeomList;
    7334         116 :     freeGEOSContext(hGEOSCtxt);
    7335             : 
    7336         116 :     return poPolygsOGRGeom;
    7337             : 
    7338             : #endif  // HAVE_GEOS
    7339             : }
    7340             : 
    7341             : /************************************************************************/
    7342             : /*                          OGR_G_Polygonize()                          */
    7343             : /************************************************************************/
    7344             : /**
    7345             :  * \brief Polygonizes a set of sparse edges.
    7346             :  *
    7347             :  * A new geometry object is created and returned containing a collection
    7348             :  * of reassembled Polygons: NULL will be returned if the input collection
    7349             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7350             :  * into Polygons is impossible due to topological inconsistencies.
    7351             :  *
    7352             :  * This function is the same as the C++ method OGRGeometry::Polygonize().
    7353             :  *
    7354             :  * This function is built on the GEOS library, check it for the definition
    7355             :  * of the geometry operation.
    7356             :  * If OGR is built without the GEOS library, this function will always fail,
    7357             :  * issuing a CPLE_NotSupported error.
    7358             :  *
    7359             :  * @param hTarget The Geometry to be polygonized.
    7360             :  *
    7361             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7362             :  * or NULL if an error occurs.
    7363             :  *
    7364             :  */
    7365             : 
    7366           3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
    7367             : 
    7368             : {
    7369           3 :     VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
    7370             : 
    7371           3 :     return OGRGeometry::ToHandle(
    7372           3 :         OGRGeometry::FromHandle(hTarget)->Polygonize());
    7373             : }
    7374             : 
    7375             : /************************************************************************/
    7376             : /*                             BuildArea()                              */
    7377             : /************************************************************************/
    7378             : 
    7379             : /**
    7380             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7381             :  *
    7382             :  * This method is the same as the C function OGR_G_BuildArea().
    7383             :  *
    7384             :  * Polygonization is performed similarly to OGRGeometry::Polygonize().
    7385             :  * Additionally, holes are dropped and the result is unified producing
    7386             :  * a single Polygon or a MultiPolygon.
    7387             :  *
    7388             :  * A new geometry object is created and returned: NULL on failure,
    7389             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7390             :  * Polygon or MultiPolygon on success.
    7391             :  *
    7392             :  * This method is built on the GEOSBuildArea_r() function of the GEOS
    7393             :  * library, check it for the definition of the geometry operation.
    7394             :  * If OGR is built without the GEOS library, this method will always fail,
    7395             :  * issuing a CPLE_NotSupported error.
    7396             :  *
    7397             :  * @return a newly allocated geometry now owned by the caller,
    7398             :  *         or NULL on failure.
    7399             :  *
    7400             :  * @since OGR 3.11
    7401             :  */
    7402             : 
    7403          30 : OGRGeometry *OGRGeometry::BuildArea() const
    7404             : 
    7405             : {
    7406             : #ifndef HAVE_GEOS
    7407             : 
    7408             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7409             :     return nullptr;
    7410             : 
    7411             : #else
    7412             : 
    7413          30 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7414             : 
    7415          30 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7416          30 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7417          30 :     if (hThisGeosGeom != nullptr)
    7418             :     {
    7419          30 :         GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
    7420             :         poPolygsOGRGeom =
    7421          30 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7422          30 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7423             :     }
    7424          30 :     freeGEOSContext(hGEOSCtxt);
    7425             : 
    7426          30 :     return poPolygsOGRGeom;
    7427             : 
    7428             : #endif  // HAVE_GEOS
    7429             : }
    7430             : 
    7431             : /************************************************************************/
    7432             : /*                          OGR_G_BuildArea()                           */
    7433             : /************************************************************************/
    7434             : 
    7435             : /**
    7436             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7437             :  *
    7438             :  * This function is the same as the C++ method OGRGeometry::BuildArea().
    7439             :  *
    7440             :  * Polygonization is performed similarly to OGR_G_Polygonize().
    7441             :  * Additionally, holes are dropped and the result is unified producing
    7442             :  * a single Polygon or a MultiPolygon.
    7443             :  *
    7444             :  * A new geometry object is created and returned: NULL on failure,
    7445             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7446             :  * Polygon or MultiPolygon on success.
    7447             :  *
    7448             :  * This function is built on the GEOSBuildArea_r() function of the GEOS
    7449             :  * library, check it for the definition of the geometry operation.
    7450             :  * If OGR is built without the GEOS library, this function will always fail,
    7451             :  * issuing a CPLE_NotSupported error.
    7452             :  *
    7453             :  * @param hGeom handle on the geometry to polygonize.
    7454             :  *
    7455             :  * @return a handle on newly allocated geometry now owned by the caller,
    7456             :  *         or NULL on failure.
    7457             :  *
    7458             :  * @since OGR 3.11
    7459             :  */
    7460             : 
    7461           0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
    7462             : 
    7463             : {
    7464           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
    7465             : 
    7466           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
    7467             : }
    7468             : 
    7469             : /************************************************************************/
    7470             : /*                               swapXY()                               */
    7471             : /************************************************************************/
    7472             : 
    7473             : /**
    7474             :  * \brief Swap x and y coordinates.
    7475             :  *
    7476             :  */
    7477             : 
    7478           0 : void OGRGeometry::swapXY()
    7479             : 
    7480             : {
    7481           0 : }
    7482             : 
    7483             : /************************************************************************/
    7484             : /*                               swapXY()                               */
    7485             : /************************************************************************/
    7486             : 
    7487             : /**
    7488             :  * \brief Swap x and y coordinates.
    7489             :  *
    7490             :  * @param hGeom geometry.
    7491             :  */
    7492             : 
    7493          32 : void OGR_G_SwapXY(OGRGeometryH hGeom)
    7494             : {
    7495          32 :     VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
    7496             : 
    7497          32 :     OGRGeometry::FromHandle(hGeom)->swapXY();
    7498             : }
    7499             : 
    7500             : /************************************************************************/
    7501             : /*                        Prepared geometry API                         */
    7502             : /************************************************************************/
    7503             : 
    7504             : #if defined(HAVE_GEOS)
    7505             : struct _OGRPreparedGeometry
    7506             : {
    7507             :     GEOSContextHandle_t hGEOSCtxt;
    7508             :     GEOSGeom hGEOSGeom;
    7509             :     const GEOSPreparedGeometry *poPreparedGEOSGeom;
    7510             : };
    7511             : #endif
    7512             : 
    7513             : /************************************************************************/
    7514             : /*                   OGRHasPreparedGeometrySupport()                    */
    7515             : /************************************************************************/
    7516             : 
    7517             : /** Returns if GEOS has prepared geometry support.
    7518             :  * @return TRUE or FALSE
    7519             :  */
    7520           1 : int OGRHasPreparedGeometrySupport()
    7521             : {
    7522             : #if defined(HAVE_GEOS)
    7523           1 :     return TRUE;
    7524             : #else
    7525             :     return FALSE;
    7526             : #endif
    7527             : }
    7528             : 
    7529             : /************************************************************************/
    7530             : /*                     OGRCreatePreparedGeometry()                      */
    7531             : /************************************************************************/
    7532             : 
    7533             : /** Creates a prepared geometry.
    7534             :  *
    7535             :  * To free with OGRDestroyPreparedGeometry()
    7536             :  *
    7537             :  * @param hGeom input geometry to prepare.
    7538             :  * @return handle to a prepared geometry.
    7539             :  * @since GDAL 3.3
    7540             :  */
    7541       53027 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
    7542             : {
    7543             :     (void)hGeom;
    7544             : #if defined(HAVE_GEOS)
    7545       53027 :     OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
    7546       53027 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    7547       53027 :     GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
    7548       53027 :     if (hGEOSGeom == nullptr)
    7549             :     {
    7550           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7551           0 :         return nullptr;
    7552             :     }
    7553             :     const GEOSPreparedGeometry *poPreparedGEOSGeom =
    7554       53027 :         GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
    7555       53027 :     if (poPreparedGEOSGeom == nullptr)
    7556             :     {
    7557           0 :         GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
    7558           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7559           0 :         return nullptr;
    7560             :     }
    7561             : 
    7562       53027 :     OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
    7563       53027 :     poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
    7564       53027 :     poPreparedGeom->hGEOSGeom = hGEOSGeom;
    7565       53027 :     poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
    7566             : 
    7567       53027 :     return poPreparedGeom;
    7568             : #else
    7569             :     return nullptr;
    7570             : #endif
    7571             : }
    7572             : 
    7573             : /************************************************************************/
    7574             : /*                     OGRDestroyPreparedGeometry()                     */
    7575             : /************************************************************************/
    7576             : 
    7577             : /** Destroys a prepared geometry.
    7578             :  * @param hPreparedGeom prepared geometry.
    7579             :  * @since GDAL 3.3
    7580             :  */
    7581       53073 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
    7582             : {
    7583             :     (void)hPreparedGeom;
    7584             : #if defined(HAVE_GEOS)
    7585       53073 :     if (hPreparedGeom != nullptr)
    7586             :     {
    7587       53027 :         GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
    7588             :                                    hPreparedGeom->poPreparedGEOSGeom);
    7589       53027 :         GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
    7590       53027 :         OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
    7591       53027 :         delete hPreparedGeom;
    7592             :     }
    7593             : #endif
    7594       53073 : }
    7595             : 
    7596             : /************************************************************************/
    7597             : /*                   OGRPreparedGeometryIntersects()                    */
    7598             : /************************************************************************/
    7599             : 
    7600             : /** Returns whether a prepared geometry intersects with a geometry.
    7601             :  * @param hPreparedGeom prepared geometry.
    7602             :  * @param hOtherGeom other geometry.
    7603             :  * @return TRUE or FALSE.
    7604             :  * @since GDAL 3.3
    7605             :  */
    7606        5538 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
    7607             :                                   const OGRGeometryH hOtherGeom)
    7608             : {
    7609             :     (void)hPreparedGeom;
    7610             :     (void)hOtherGeom;
    7611             : #if defined(HAVE_GEOS)
    7612        5538 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7613        5538 :     if (hPreparedGeom == nullptr ||
    7614             :         poOtherGeom == nullptr
    7615             :         // The check for IsEmpty() is for buggy GEOS versions.
    7616             :         // See https://github.com/libgeos/geos/pull/423
    7617       11076 :         || poOtherGeom->IsEmpty())
    7618             :     {
    7619           1 :         return FALSE;
    7620             :     }
    7621             : 
    7622             :     GEOSGeom hGEOSOtherGeom =
    7623        5537 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7624        5537 :     if (hGEOSOtherGeom == nullptr)
    7625           0 :         return FALSE;
    7626             : 
    7627             :     const bool bRet =
    7628        5537 :         GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
    7629             :                                  hPreparedGeom->poPreparedGEOSGeom,
    7630        5537 :                                  hGEOSOtherGeom) == 1;
    7631        5537 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7632             : 
    7633        5537 :     return bRet;
    7634             : #else
    7635             :     return FALSE;
    7636             : #endif
    7637             : }
    7638             : 
    7639             : /** Returns whether a prepared geometry contains a geometry.
    7640             :  * @param hPreparedGeom prepared geometry.
    7641             :  * @param hOtherGeom other geometry.
    7642             :  * @return TRUE or FALSE.
    7643             :  */
    7644      120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
    7645             :                                 const OGRGeometryH hOtherGeom)
    7646             : {
    7647             :     (void)hPreparedGeom;
    7648             :     (void)hOtherGeom;
    7649             : #if defined(HAVE_GEOS)
    7650      120516 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7651      120516 :     if (hPreparedGeom == nullptr ||
    7652             :         poOtherGeom == nullptr
    7653             :         // The check for IsEmpty() is for buggy GEOS versions.
    7654             :         // See https://github.com/libgeos/geos/pull/423
    7655      241032 :         || poOtherGeom->IsEmpty())
    7656             :     {
    7657           1 :         return FALSE;
    7658             :     }
    7659             : 
    7660             :     GEOSGeom hGEOSOtherGeom =
    7661      120515 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7662      120515 :     if (hGEOSOtherGeom == nullptr)
    7663           0 :         return FALSE;
    7664             : 
    7665      120515 :     const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
    7666             :                                              hPreparedGeom->poPreparedGEOSGeom,
    7667      120515 :                                              hGEOSOtherGeom) == 1;
    7668      120515 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7669             : 
    7670      120515 :     return bRet;
    7671             : #else
    7672             :     return FALSE;
    7673             : #endif
    7674             : }
    7675             : 
    7676             : /************************************************************************/
    7677             : /*                        OGRGeometryFromEWKB()                         */
    7678             : /************************************************************************/
    7679             : 
    7680        1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
    7681             :                                  int bIsPostGIS1_EWKB)
    7682             : 
    7683             : {
    7684        1445 :     OGRGeometry *poGeometry = nullptr;
    7685             : 
    7686        1445 :     size_t nWKBSize = 0;
    7687        1445 :     const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
    7688        1445 :     if (pabyWKB == nullptr)
    7689           0 :         return nullptr;
    7690             : 
    7691             :     /* -------------------------------------------------------------------- */
    7692             :     /*      Try to ingest the geometry.                                     */
    7693             :     /* -------------------------------------------------------------------- */
    7694        1445 :     (void)OGRGeometryFactory::createFromWkb(
    7695             :         pabyWKB, nullptr, &poGeometry, nWKBSize,
    7696             :         (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
    7697             : 
    7698        1445 :     return poGeometry;
    7699             : }
    7700             : 
    7701             : /************************************************************************/
    7702             : /*                       OGRGeometryFromHexEWKB()                       */
    7703             : /************************************************************************/
    7704             : 
    7705        1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
    7706             :                                     int bIsPostGIS1_EWKB)
    7707             : 
    7708             : {
    7709        1443 :     if (pszBytea == nullptr)
    7710           0 :         return nullptr;
    7711             : 
    7712        1443 :     int nWKBLength = 0;
    7713        1443 :     GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
    7714             : 
    7715             :     OGRGeometry *poGeometry =
    7716        1443 :         OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
    7717             : 
    7718        1443 :     CPLFree(pabyWKB);
    7719             : 
    7720        1443 :     return poGeometry;
    7721             : }
    7722             : 
    7723             : /************************************************************************/
    7724             : /*                        OGRGeometryToHexEWKB()                        */
    7725             : /************************************************************************/
    7726             : 
    7727        1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
    7728             :                            int nPostGISMajor, int nPostGISMinor)
    7729             : {
    7730        1071 :     const size_t nWkbSize = poGeometry->WkbSize();
    7731        1071 :     GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
    7732        1071 :     if (pabyWKB == nullptr)
    7733           0 :         return CPLStrdup("");
    7734             : 
    7735         118 :     if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
    7736        1815 :         wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
    7737         626 :         poGeometry->IsEmpty())
    7738             :     {
    7739           2 :         if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
    7740             :             OGRERR_NONE)
    7741             :         {
    7742           0 :             CPLFree(pabyWKB);
    7743           0 :             return CPLStrdup("");
    7744             :         }
    7745             :     }
    7746        1069 :     else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
    7747             :                                      (nPostGISMajor < 2)
    7748             :                                          ? wkbVariantPostGIS1
    7749        1069 :                                          : wkbVariantOldOgc) != OGRERR_NONE)
    7750             :     {
    7751           0 :         CPLFree(pabyWKB);
    7752           0 :         return CPLStrdup("");
    7753             :     }
    7754             : 
    7755             :     // When converting to hex, each byte takes 2 hex characters.  In addition
    7756             :     // we add in 8 characters to represent the SRID integer in hex, and
    7757             :     // one for a null terminator.
    7758             :     // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
    7759             :     // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
    7760             :     // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
    7761        1071 :     if (nWkbSize >
    7762        1071 :         static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
    7763             :     {
    7764           0 :         CPLFree(pabyWKB);
    7765           0 :         return CPLStrdup("");
    7766             :     }
    7767        1071 :     const size_t nTextSize = nWkbSize * 2 + 8 + 1;
    7768        1071 :     char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
    7769        1071 :     if (pszTextBuf == nullptr)
    7770             :     {
    7771           0 :         CPLFree(pabyWKB);
    7772           0 :         return CPLStrdup("");
    7773             :     }
    7774        1071 :     char *pszTextBufCurrent = pszTextBuf;
    7775             : 
    7776             :     // Convert the 1st byte, which is the endianness flag, to hex.
    7777        1071 :     char *pszHex = CPLBinaryToHex(1, pabyWKB);
    7778        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7779        1071 :     CPLFree(pszHex);
    7780        1071 :     pszTextBufCurrent += 2;
    7781             : 
    7782             :     // Next, get the geom type which is bytes 2 through 5.
    7783             :     GUInt32 geomType;
    7784        1071 :     memcpy(&geomType, pabyWKB + 1, 4);
    7785             : 
    7786             :     // Now add the SRID flag if an SRID is provided.
    7787        1071 :     if (nSRSId > 0)
    7788             :     {
    7789             :         // Change the flag to wkbNDR (little) endianness.
    7790         541 :         constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
    7791         541 :         GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
    7792             :         // Apply the flag.
    7793         541 :         geomType = geomType | nGSrsFlag;
    7794             :     }
    7795             : 
    7796             :     // Now write the geom type which is 4 bytes.
    7797        1071 :     pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
    7798        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7799        1071 :     CPLFree(pszHex);
    7800        1071 :     pszTextBufCurrent += 8;
    7801             : 
    7802             :     // Now include SRID if provided.
    7803        1071 :     if (nSRSId > 0)
    7804             :     {
    7805             :         // Force the srsid to wkbNDR (little) endianness.
    7806         541 :         const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
    7807         541 :         pszHex = CPLBinaryToHex(sizeof(nGSRSId),
    7808             :                                 reinterpret_cast<const GByte *>(&nGSRSId));
    7809         541 :         strcpy(pszTextBufCurrent, pszHex);
    7810         541 :         CPLFree(pszHex);
    7811         541 :         pszTextBufCurrent += 8;
    7812             :     }
    7813             : 
    7814             :     // Copy the rest of the data over - subtract
    7815             :     // 5 since we already copied 5 bytes above.
    7816        1071 :     pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
    7817        1071 :     CPLFree(pabyWKB);
    7818        1071 :     if (!pszHex || pszHex[0] == 0)
    7819             :     {
    7820           0 :         CPLFree(pszTextBuf);
    7821           0 :         return pszHex;
    7822             :     }
    7823        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7824        1071 :     CPLFree(pszHex);
    7825             : 
    7826        1071 :     return pszTextBuf;
    7827             : }
    7828             : 
    7829             : /************************************************************************/
    7830             : /*                       importPreambleFromWkb()                        */
    7831             : /************************************************************************/
    7832             : 
    7833             : //! @cond Doxygen_Suppress
    7834      158893 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
    7835             :                                           size_t nSize,
    7836             :                                           OGRwkbByteOrder &eByteOrder,
    7837             :                                           OGRwkbVariant eWkbVariant)
    7838             : {
    7839      158893 :     if (nSize < 9 && nSize != static_cast<size_t>(-1))
    7840           0 :         return OGRERR_NOT_ENOUGH_DATA;
    7841             : 
    7842             :     /* -------------------------------------------------------------------- */
    7843             :     /*      Get the byte order byte.                                        */
    7844             :     /* -------------------------------------------------------------------- */
    7845      158893 :     int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
    7846      158893 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    7847           0 :         return OGRERR_CORRUPT_DATA;
    7848      158893 :     eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    7849             : 
    7850             :     /* -------------------------------------------------------------------- */
    7851             :     /*      Get the geometry feature type.                                  */
    7852             :     /* -------------------------------------------------------------------- */
    7853             :     OGRwkbGeometryType eGeometryType;
    7854             :     const OGRErr err =
    7855      158893 :         OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
    7856      158893 :     if (wkbHasZ(eGeometryType))
    7857       62309 :         flags |= OGR_G_3D;
    7858      158893 :     if (wkbHasM(eGeometryType))
    7859       59692 :         flags |= OGR_G_MEASURED;
    7860             : 
    7861      158893 :     if (err != OGRERR_NONE || eGeometryType != getGeometryType())
    7862           0 :         return OGRERR_CORRUPT_DATA;
    7863             : 
    7864      158893 :     return OGRERR_NONE;
    7865             : }
    7866             : 
    7867             : /************************************************************************/
    7868             : /*                    importPreambleOfCollectionFromWkb()              */
    7869             : /*                                                                      */
    7870             : /*      Utility method for OGRSimpleCurve, OGRCompoundCurve,            */
    7871             : /*      OGRCurvePolygon and OGRGeometryCollection.                      */
    7872             : /************************************************************************/
    7873             : 
    7874       75525 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
    7875             :     const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
    7876             :     OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
    7877             :     OGRwkbVariant eWkbVariant)
    7878             : {
    7879       75525 :     nGeomCount = 0;
    7880             : 
    7881             :     OGRErr eErr =
    7882       75525 :         importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
    7883       75525 :     if (eErr != OGRERR_NONE)
    7884           0 :         return eErr;
    7885             : 
    7886             :     /* -------------------------------------------------------------------- */
    7887             :     /*      Clear existing Geoms.                                           */
    7888             :     /* -------------------------------------------------------------------- */
    7889       75525 :     int _flags = flags;  // flags set in importPreambleFromWkb
    7890       75525 :     empty();             // may reset flags etc.
    7891             : 
    7892             :     // restore
    7893       75525 :     if (_flags & OGR_G_3D)
    7894       59265 :         set3D(TRUE);
    7895       75525 :     if (_flags & OGR_G_MEASURED)
    7896       56768 :         setMeasured(TRUE);
    7897             : 
    7898             :     /* -------------------------------------------------------------------- */
    7899             :     /*      Get the sub-geometry count.                                     */
    7900             :     /* -------------------------------------------------------------------- */
    7901       75525 :     memcpy(&nGeomCount, pabyData + 5, 4);
    7902             : 
    7903       75525 :     if (OGR_SWAP(eByteOrder))
    7904         386 :         nGeomCount = CPL_SWAP32(nGeomCount);
    7905             : 
    7906      150916 :     if (nGeomCount < 0 ||
    7907       75391 :         static_cast<size_t>(nGeomCount) >
    7908       75391 :             std::numeric_limits<size_t>::max() / nMinSubGeomSize)
    7909             :     {
    7910         134 :         nGeomCount = 0;
    7911         134 :         return OGRERR_CORRUPT_DATA;
    7912             :     }
    7913       75391 :     const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
    7914             : 
    7915             :     // Each ring has a minimum of nMinSubGeomSize bytes.
    7916       75391 :     if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
    7917             :     {
    7918         910 :         CPLError(CE_Failure, CPLE_AppDefined,
    7919             :                  "Length of input WKB is too small");
    7920         910 :         nGeomCount = 0;
    7921         910 :         return OGRERR_NOT_ENOUGH_DATA;
    7922             :     }
    7923             : 
    7924       74481 :     nDataOffset = 9;
    7925       74481 :     if (nSize != static_cast<size_t>(-1))
    7926             :     {
    7927       74461 :         CPLAssert(nSize >= nDataOffset);
    7928       74461 :         nSize -= nDataOffset;
    7929             :     }
    7930             : 
    7931       74481 :     return OGRERR_NONE;
    7932             : }
    7933             : 
    7934             : /************************************************************************/
    7935             : /*                      importCurveCollectionFromWkt()                  */
    7936             : /*                                                                      */
    7937             : /*      Utility method for OGRCompoundCurve, OGRCurvePolygon and        */
    7938             : /*      OGRMultiCurve.                                                  */
    7939             : /************************************************************************/
    7940             : 
    7941        1450 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
    7942             :     const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
    7943             :     int bAllowCurve, int bAllowCompoundCurve,
    7944             :     OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
    7945             : 
    7946             : {
    7947        1450 :     int bHasZ = FALSE;
    7948        1450 :     int bHasM = FALSE;
    7949        1450 :     bool bIsEmpty = false;
    7950        1450 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
    7951        1450 :     flags = 0;
    7952        1450 :     if (eErr != OGRERR_NONE)
    7953          14 :         return eErr;
    7954        1436 :     if (bHasZ)
    7955         206 :         flags |= OGR_G_3D;
    7956        1436 :     if (bHasM)
    7957         132 :         flags |= OGR_G_MEASURED;
    7958        1436 :     if (bIsEmpty)
    7959         111 :         return OGRERR_NONE;
    7960             : 
    7961             :     char szToken[OGR_WKT_TOKEN_MAX];
    7962        1325 :     const char *pszInput = *ppszInput;
    7963        1325 :     eErr = OGRERR_NONE;
    7964             : 
    7965             :     // Skip first '('.
    7966        1325 :     pszInput = OGRWktReadToken(pszInput, szToken);
    7967             : 
    7968             :     /* ==================================================================== */
    7969             :     /*      Read each curve in turn.  Note that we try to reuse the same    */
    7970             :     /*      point list buffer from curve to curve to cut down on            */
    7971             :     /*      allocate/deallocate overhead.                                   */
    7972             :     /* ==================================================================== */
    7973        1325 :     OGRRawPoint *paoPoints = nullptr;
    7974        1325 :     int nMaxPoints = 0;
    7975        1325 :     double *padfZ = nullptr;
    7976             : 
    7977         656 :     do
    7978             :     {
    7979             : 
    7980             :         /* --------------------------------------------------------------------
    7981             :          */
    7982             :         /*      Get the first token, which should be the geometry type. */
    7983             :         /* --------------------------------------------------------------------
    7984             :          */
    7985        1981 :         const char *pszInputBefore = pszInput;
    7986        1981 :         pszInput = OGRWktReadToken(pszInput, szToken);
    7987             : 
    7988             :         /* --------------------------------------------------------------------
    7989             :          */
    7990             :         /*      Do the import. */
    7991             :         /* --------------------------------------------------------------------
    7992             :          */
    7993        1981 :         OGRCurve *poCurve = nullptr;
    7994        1981 :         if (EQUAL(szToken, "("))
    7995             :         {
    7996        1427 :             OGRLineString *poLine = new OGRLineString();
    7997        1427 :             poCurve = poLine;
    7998        1427 :             pszInput = pszInputBefore;
    7999        1427 :             eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
    8000             :                                                  paoPoints, nMaxPoints, padfZ);
    8001             :         }
    8002         554 :         else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
    8003             :         {
    8004          16 :             poCurve = new OGRLineString();
    8005             :         }
    8006             :         // Accept LINESTRING(), but this is an extension to the BNF, also
    8007             :         // accepted by PostGIS.
    8008         538 :         else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
    8009         523 :                  (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
    8010         523 :                   !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
    8011        1235 :                   OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
    8012         159 :                  (bAllowCompoundCurve &&
    8013         159 :                   STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
    8014             :         {
    8015         500 :             OGRGeometry *poGeom = nullptr;
    8016         500 :             pszInput = pszInputBefore;
    8017             :             eErr =
    8018         500 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
    8019         500 :             if (poGeom == nullptr)
    8020             :             {
    8021           1 :                 eErr = OGRERR_CORRUPT_DATA;
    8022             :             }
    8023             :             else
    8024             :             {
    8025         499 :                 poCurve = poGeom->toCurve();
    8026             :             }
    8027             :         }
    8028             :         else
    8029             :         {
    8030          38 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
    8031             :                      szToken);
    8032          38 :             eErr = OGRERR_CORRUPT_DATA;
    8033             :         }
    8034             : 
    8035             :         // If this has M it is an error if poGeom does not have M.
    8036        1981 :         if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
    8037           0 :             eErr = OGRERR_CORRUPT_DATA;
    8038             : 
    8039        1981 :         if (eErr == OGRERR_NONE)
    8040        1936 :             eErr = pfnAddCurveDirectly(this, poCurve);
    8041        1981 :         if (eErr != OGRERR_NONE)
    8042             :         {
    8043          55 :             delete poCurve;
    8044          55 :             break;
    8045             :         }
    8046             : 
    8047             :         /* --------------------------------------------------------------------
    8048             :          */
    8049             :         /*      Read the delimiter following the surface. */
    8050             :         /* --------------------------------------------------------------------
    8051             :          */
    8052        1926 :         pszInput = OGRWktReadToken(pszInput, szToken);
    8053        1926 :     } while (szToken[0] == ',' && eErr == OGRERR_NONE);
    8054             : 
    8055        1325 :     CPLFree(paoPoints);
    8056        1325 :     CPLFree(padfZ);
    8057             : 
    8058             :     /* -------------------------------------------------------------------- */
    8059             :     /*      freak if we don't get a closing bracket.                        */
    8060             :     /* -------------------------------------------------------------------- */
    8061             : 
    8062        1325 :     if (eErr != OGRERR_NONE)
    8063          55 :         return eErr;
    8064             : 
    8065        1270 :     if (szToken[0] != ')')
    8066           9 :         return OGRERR_CORRUPT_DATA;
    8067             : 
    8068        1261 :     *ppszInput = pszInput;
    8069        1261 :     return OGRERR_NONE;
    8070             : }
    8071             : 
    8072             : //! @endcond
    8073             : 
    8074             : /************************************************************************/
    8075             : /*                           OGR_GT_Flatten()                           */
    8076             : /************************************************************************/
    8077             : /**
    8078             :  * \brief Returns the 2D geometry type corresponding to the passed geometry
    8079             :  * type.
    8080             :  *
    8081             :  * This function is intended to work with geometry types as old-style 99-402
    8082             :  * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
    8083             :  * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
    8084             :  *
    8085             :  * @param eType Input geometry type
    8086             :  *
    8087             :  * @return 2D geometry type corresponding to the passed geometry type.
    8088             :  *
    8089             :  */
    8090             : 
    8091     7864010 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
    8092             : {
    8093     7864010 :     eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
    8094     7864010 :     if (eType >= 1000 && eType < 2000)  // ISO Z.
    8095     2776760 :         return static_cast<OGRwkbGeometryType>(eType - 1000);
    8096     5087240 :     if (eType >= 2000 && eType < 3000)  // ISO M.
    8097        6059 :         return static_cast<OGRwkbGeometryType>(eType - 2000);
    8098     5081180 :     if (eType >= 3000 && eType < 4000)  // ISO ZM.
    8099      136249 :         return static_cast<OGRwkbGeometryType>(eType - 3000);
    8100     4944940 :     return eType;
    8101             : }
    8102             : 
    8103             : /************************************************************************/
    8104             : /*                            OGR_GT_HasZ()                             */
    8105             : /************************************************************************/
    8106             : /**
    8107             :  * \brief Return if the geometry type is a 3D geometry type.
    8108             :  *
    8109             :  * @param eType Input geometry type
    8110             :  *
    8111             :  * @return TRUE if the geometry type is a 3D geometry type.
    8112             :  *
    8113             :  */
    8114             : 
    8115     2074980 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
    8116             : {
    8117     2074980 :     if (eType & wkb25DBitInternalUse)
    8118      157128 :         return TRUE;
    8119     1917860 :     if (eType >= 1000 && eType < 2000)  // Accept 1000 for wkbUnknownZ.
    8120         264 :         return TRUE;
    8121     1917590 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8122      121539 :         return TRUE;
    8123     1796050 :     return FALSE;
    8124             : }
    8125             : 
    8126             : /************************************************************************/
    8127             : /*                            OGR_GT_HasM()                             */
    8128             : /************************************************************************/
    8129             : /**
    8130             :  * \brief Return if the geometry type is a measured type.
    8131             :  *
    8132             :  * @param eType Input geometry type
    8133             :  *
    8134             :  * @return TRUE if the geometry type is a measured type.
    8135             :  *
    8136             :  */
    8137             : 
    8138     2134080 : int OGR_GT_HasM(OGRwkbGeometryType eType)
    8139             : {
    8140     2134080 :     if (eType >= 2000 && eType < 3000)  // Accept 2000 for wkbUnknownM.
    8141        2593 :         return TRUE;
    8142     2131480 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8143      121195 :         return TRUE;
    8144     2010290 :     return FALSE;
    8145             : }
    8146             : 
    8147             : /************************************************************************/
    8148             : /*                            OGR_GT_SetZ()                             */
    8149             : /************************************************************************/
    8150             : /**
    8151             :  * \brief Returns the 3D geometry type corresponding to the passed geometry
    8152             :  * type.
    8153             :  *
    8154             :  * @param eType Input geometry type
    8155             :  *
    8156             :  * @return 3D geometry type corresponding to the passed geometry type.
    8157             :  *
    8158             :  */
    8159             : 
    8160        5748 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
    8161             : {
    8162        5748 :     if (OGR_GT_HasZ(eType) || eType == wkbNone)
    8163         498 :         return eType;
    8164        5250 :     if (eType <= wkbGeometryCollection)
    8165        5148 :         return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
    8166             :     else
    8167         102 :         return static_cast<OGRwkbGeometryType>(eType + 1000);
    8168             : }
    8169             : 
    8170             : /************************************************************************/
    8171             : /*                            OGR_GT_SetM()                             */
    8172             : /************************************************************************/
    8173             : /**
    8174             :  * \brief Returns the measured geometry type corresponding to the passed
    8175             :  * geometry type.
    8176             :  *
    8177             :  * @param eType Input geometry type
    8178             :  *
    8179             :  * @return measured geometry type corresponding to the passed geometry type.
    8180             :  *
    8181             :  */
    8182             : 
    8183        2013 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
    8184             : {
    8185        2013 :     if (OGR_GT_HasM(eType) || eType == wkbNone)
    8186         262 :         return eType;
    8187        1751 :     if (eType & wkb25DBitInternalUse)
    8188             :     {
    8189         717 :         eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
    8190         717 :         eType = static_cast<OGRwkbGeometryType>(eType + 1000);
    8191             :     }
    8192        1751 :     return static_cast<OGRwkbGeometryType>(eType + 2000);
    8193             : }
    8194             : 
    8195             : /************************************************************************/
    8196             : /*                         OGR_GT_SetModifier()                         */
    8197             : /************************************************************************/
    8198             : /**
    8199             :  * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
    8200             :  *
    8201             :  * @param eType Input geometry type
    8202             :  * @param bHasZ TRUE if the output geometry type must be 3D.
    8203             :  * @param bHasM TRUE if the output geometry type must be measured.
    8204             :  *
    8205             :  * @return Output geometry type.
    8206             :  *
    8207             :  */
    8208             : 
    8209        5564 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
    8210             :                                       int bHasM)
    8211             : {
    8212        5564 :     if (bHasZ && bHasM)
    8213         342 :         return OGR_GT_SetM(OGR_GT_SetZ(eType));
    8214        5222 :     else if (bHasM)
    8215         333 :         return OGR_GT_SetM(wkbFlatten(eType));
    8216        4889 :     else if (bHasZ)
    8217        2110 :         return OGR_GT_SetZ(wkbFlatten(eType));
    8218             :     else
    8219        2779 :         return wkbFlatten(eType);
    8220             : }
    8221             : 
    8222             : /************************************************************************/
    8223             : /*                         OGR_GT_IsSubClassOf)                         */
    8224             : /************************************************************************/
    8225             : /**
    8226             :  * \brief Returns if a type is a subclass of another one
    8227             :  *
    8228             :  * @param eType Type.
    8229             :  * @param eSuperType Super type
    8230             :  *
    8231             :  * @return TRUE if eType is a subclass of eSuperType.
    8232             :  *
    8233             :  */
    8234             : 
    8235      131862 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
    8236             : {
    8237      131862 :     eSuperType = wkbFlatten(eSuperType);
    8238      131862 :     eType = wkbFlatten(eType);
    8239             : 
    8240      131862 :     if (eSuperType == eType || eSuperType == wkbUnknown)
    8241       19389 :         return TRUE;
    8242             : 
    8243      112473 :     if (eSuperType == wkbGeometryCollection)
    8244       31314 :         return eType == wkbMultiPoint || eType == wkbMultiLineString ||
    8245       64628 :                eType == wkbMultiPolygon || eType == wkbMultiCurve ||
    8246       33314 :                eType == wkbMultiSurface;
    8247             : 
    8248       79159 :     if (eSuperType == wkbCurvePolygon)
    8249       21960 :         return eType == wkbPolygon || eType == wkbTriangle;
    8250             : 
    8251       57199 :     if (eSuperType == wkbMultiCurve)
    8252         249 :         return eType == wkbMultiLineString;
    8253             : 
    8254       56950 :     if (eSuperType == wkbMultiSurface)
    8255         288 :         return eType == wkbMultiPolygon;
    8256             : 
    8257       56662 :     if (eSuperType == wkbCurve)
    8258       23388 :         return eType == wkbLineString || eType == wkbCircularString ||
    8259       23388 :                eType == wkbCompoundCurve;
    8260             : 
    8261       33274 :     if (eSuperType == wkbSurface)
    8262        3521 :         return eType == wkbCurvePolygon || eType == wkbPolygon ||
    8263        7190 :                eType == wkbTriangle || eType == wkbPolyhedralSurface ||
    8264        3669 :                eType == wkbTIN;
    8265             : 
    8266       29605 :     if (eSuperType == wkbPolygon)
    8267         221 :         return eType == wkbTriangle;
    8268             : 
    8269       29384 :     if (eSuperType == wkbPolyhedralSurface)
    8270       14640 :         return eType == wkbTIN;
    8271             : 
    8272       14744 :     return FALSE;
    8273             : }
    8274             : 
    8275             : /************************************************************************/
    8276             : /*                        OGR_GT_GetCollection()                        */
    8277             : /************************************************************************/
    8278             : /**
    8279             :  * \brief Returns the collection type that can contain the passed geometry type
    8280             :  *
    8281             :  * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
    8282             :  * wkbLineString->wkbMultiLineString,
    8283             :  * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
    8284             :  * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
    8285             :  * wkbCurvePolygon->wkbMultiSurface.
    8286             :  * In other cases, wkbUnknown is returned
    8287             :  *
    8288             :  * Passed Z, M, ZM flag is preserved.
    8289             :  *
    8290             :  *
    8291             :  * @param eType Input geometry type
    8292             :  *
    8293             :  * @return the collection type that can contain the passed geometry type or
    8294             :  * wkbUnknown
    8295             :  *
    8296             :  */
    8297             : 
    8298        2709 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
    8299             : {
    8300        2709 :     const bool bHasZ = wkbHasZ(eType);
    8301        2709 :     const bool bHasM = wkbHasM(eType);
    8302        2709 :     if (eType == wkbNone)
    8303           1 :         return wkbNone;
    8304        2708 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8305        2708 :     if (eFGType == wkbPoint)
    8306          67 :         eType = wkbMultiPoint;
    8307             : 
    8308        2641 :     else if (eFGType == wkbLineString)
    8309         187 :         eType = wkbMultiLineString;
    8310             : 
    8311        2454 :     else if (eFGType == wkbPolygon)
    8312        1004 :         eType = wkbMultiPolygon;
    8313             : 
    8314        1450 :     else if (eFGType == wkbTriangle)
    8315           7 :         eType = wkbTIN;
    8316             : 
    8317        1443 :     else if (OGR_GT_IsCurve(eFGType))
    8318         189 :         eType = wkbMultiCurve;
    8319             : 
    8320        1254 :     else if (OGR_GT_IsSurface(eFGType))
    8321         952 :         eType = wkbMultiSurface;
    8322             : 
    8323             :     else
    8324         302 :         return wkbUnknown;
    8325             : 
    8326        2406 :     if (bHasZ)
    8327           3 :         eType = wkbSetZ(eType);
    8328        2406 :     if (bHasM)
    8329           3 :         eType = wkbSetM(eType);
    8330             : 
    8331        2406 :     return eType;
    8332             : }
    8333             : 
    8334             : /************************************************************************/
    8335             : /*                          OGR_GT_GetSingle()                          */
    8336             : /************************************************************************/
    8337             : /**
    8338             :  * \brief Returns the non-collection type that be contained in the passed
    8339             :  * geometry type.
    8340             :  *
    8341             :  * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
    8342             :  * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
    8343             :  * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
    8344             :  * wkbGeometryCollection -> wkbUnknown
    8345             :  * In other cases, the original geometry is returned.
    8346             :  *
    8347             :  * Passed Z, M, ZM flag is preserved.
    8348             :  *
    8349             :  *
    8350             :  * @param eType Input geometry type
    8351             :  *
    8352             :  * @return the the non-collection type that be contained in the passed geometry
    8353             :  * type or wkbUnknown
    8354             :  *
    8355             :  * @since GDAL 3.11
    8356             :  */
    8357             : 
    8358          43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
    8359             : {
    8360          43 :     const bool bHasZ = wkbHasZ(eType);
    8361          43 :     const bool bHasM = wkbHasM(eType);
    8362          43 :     if (eType == wkbNone)
    8363           1 :         return wkbNone;
    8364          42 :     const OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8365          42 :     if (eFGType == wkbMultiPoint)
    8366           8 :         eType = wkbPoint;
    8367             : 
    8368          34 :     else if (eFGType == wkbMultiLineString)
    8369           4 :         eType = wkbLineString;
    8370             : 
    8371          30 :     else if (eFGType == wkbMultiPolygon)
    8372           2 :         eType = wkbPolygon;
    8373             : 
    8374          28 :     else if (eFGType == wkbMultiCurve)
    8375           2 :         eType = wkbCompoundCurve;
    8376             : 
    8377          26 :     else if (eFGType == wkbMultiSurface)
    8378           2 :         eType = wkbCurvePolygon;
    8379             : 
    8380          24 :     else if (eFGType == wkbGeometryCollection)
    8381           1 :         return wkbUnknown;
    8382             : 
    8383          41 :     if (bHasZ)
    8384           3 :         eType = wkbSetZ(eType);
    8385          41 :     if (bHasM)
    8386           2 :         eType = wkbSetM(eType);
    8387             : 
    8388          41 :     return eType;
    8389             : }
    8390             : 
    8391             : /************************************************************************/
    8392             : /*                          OGR_GT_GetCurve()                           */
    8393             : /************************************************************************/
    8394             : /**
    8395             :  * \brief Returns the curve geometry type that can contain the passed geometry
    8396             :  * type
    8397             :  *
    8398             :  * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
    8399             :  * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
    8400             :  * and wkbMultiLineString->wkbMultiCurve.
    8401             :  * In other cases, the passed geometry is returned.
    8402             :  *
    8403             :  * Passed Z, M, ZM flag is preserved.
    8404             :  *
    8405             :  * @param eType Input geometry type
    8406             :  *
    8407             :  * @return the curve type that can contain the passed geometry type
    8408             :  *
    8409             :  */
    8410             : 
    8411          35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
    8412             : {
    8413          35 :     const bool bHasZ = wkbHasZ(eType);
    8414          35 :     const bool bHasM = wkbHasM(eType);
    8415          35 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8416             : 
    8417          35 :     if (eFGType == wkbLineString)
    8418           3 :         eType = wkbCompoundCurve;
    8419             : 
    8420          32 :     else if (eFGType == wkbPolygon)
    8421           1 :         eType = wkbCurvePolygon;
    8422             : 
    8423          31 :     else if (eFGType == wkbTriangle)
    8424           0 :         eType = wkbCurvePolygon;
    8425             : 
    8426          31 :     else if (eFGType == wkbMultiLineString)
    8427           6 :         eType = wkbMultiCurve;
    8428             : 
    8429          25 :     else if (eFGType == wkbMultiPolygon)
    8430           4 :         eType = wkbMultiSurface;
    8431             : 
    8432          35 :     if (bHasZ)
    8433           4 :         eType = wkbSetZ(eType);
    8434          35 :     if (bHasM)
    8435           4 :         eType = wkbSetM(eType);
    8436             : 
    8437          35 :     return eType;
    8438             : }
    8439             : 
    8440             : /************************************************************************/
    8441             : /*                          OGR_GT_GetLinear()                          */
    8442             : /************************************************************************/
    8443             : /**
    8444             :  * \brief Returns the non-curve geometry type that can contain the passed
    8445             :  * geometry type
    8446             :  *
    8447             :  * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
    8448             :  * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
    8449             :  * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
    8450             :  * In other cases, the passed geometry is returned.
    8451             :  *
    8452             :  * Passed Z, M, ZM flag is preserved.
    8453             :  *
    8454             :  * @param eType Input geometry type
    8455             :  *
    8456             :  * @return the non-curve type that can contain the passed geometry type
    8457             :  *
    8458             :  */
    8459             : 
    8460         785 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
    8461             : {
    8462         785 :     const bool bHasZ = wkbHasZ(eType);
    8463         785 :     const bool bHasM = wkbHasM(eType);
    8464         785 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8465             : 
    8466         785 :     if (OGR_GT_IsCurve(eFGType))
    8467          56 :         eType = wkbLineString;
    8468             : 
    8469         729 :     else if (OGR_GT_IsSurface(eFGType))
    8470          43 :         eType = wkbPolygon;
    8471             : 
    8472         686 :     else if (eFGType == wkbMultiCurve)
    8473         186 :         eType = wkbMultiLineString;
    8474             : 
    8475         500 :     else if (eFGType == wkbMultiSurface)
    8476         166 :         eType = wkbMultiPolygon;
    8477             : 
    8478         785 :     if (bHasZ)
    8479         154 :         eType = wkbSetZ(eType);
    8480         785 :     if (bHasM)
    8481         101 :         eType = wkbSetM(eType);
    8482             : 
    8483         785 :     return eType;
    8484             : }
    8485             : 
    8486             : /************************************************************************/
    8487             : /*                           OGR_GT_IsCurve()                           */
    8488             : /************************************************************************/
    8489             : 
    8490             : /**
    8491             :  * \brief Return if a geometry type is an instance of Curve
    8492             :  *
    8493             :  * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
    8494             :  * and their Z/M/ZM variant.
    8495             :  *
    8496             :  * @param eGeomType the geometry type
    8497             :  * @return TRUE if the geometry type is an instance of Curve
    8498             :  *
    8499             :  */
    8500             : 
    8501       23379 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
    8502             : {
    8503       23379 :     return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
    8504             : }
    8505             : 
    8506             : /************************************************************************/
    8507             : /*                          OGR_GT_IsSurface()                          */
    8508             : /************************************************************************/
    8509             : 
    8510             : /**
    8511             :  * \brief Return if a geometry type is an instance of Surface
    8512             :  *
    8513             :  * Such geometry type are wkbCurvePolygon and wkbPolygon
    8514             :  * and their Z/M/ZM variant.
    8515             :  *
    8516             :  * @param eGeomType the geometry type
    8517             :  * @return TRUE if the geometry type is an instance of Surface
    8518             :  *
    8519             :  */
    8520             : 
    8521        3663 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
    8522             : {
    8523        3663 :     return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
    8524             : }
    8525             : 
    8526             : /************************************************************************/
    8527             : /*                         OGR_GT_IsNonLinear()                         */
    8528             : /************************************************************************/
    8529             : 
    8530             : /**
    8531             :  * \brief Return if a geometry type is a non-linear geometry type.
    8532             :  *
    8533             :  * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
    8534             :  * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
    8535             :  * Z/M variants.
    8536             :  *
    8537             :  * @param eGeomType the geometry type
    8538             :  * @return TRUE if the geometry type is a non-linear geometry type.
    8539             :  *
    8540             :  */
    8541             : 
    8542      114999 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
    8543             : {
    8544      114999 :     OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
    8545      114991 :     return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
    8546      114918 :            eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
    8547      229990 :            eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
    8548      114999 :            eFGeomType == wkbMultiSurface;
    8549             : }
    8550             : 
    8551             : /************************************************************************/
    8552             : /*                            CastToError()                             */
    8553             : /************************************************************************/
    8554             : 
    8555             : //! @cond Doxygen_Suppress
    8556           0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
    8557             : {
    8558           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
    8559           0 :              poGeom->getGeometryName());
    8560           0 :     delete poGeom;
    8561           0 :     return nullptr;
    8562             : }
    8563             : 
    8564             : //! @endcond
    8565             : 
    8566             : /************************************************************************/
    8567             : /*                         OGRexportToSFCGAL()                          */
    8568             : /************************************************************************/
    8569             : 
    8570             : //! @cond Doxygen_Suppress
    8571             : sfcgal_geometry_t *
    8572           0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
    8573             : {
    8574             : #ifdef HAVE_SFCGAL
    8575             : 
    8576             :     sfcgal_init();
    8577             : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8578             : 
    8579             :     const auto exportToSFCGALViaWKB =
    8580             :         [](const OGRGeometry *geom) -> sfcgal_geometry_t *
    8581             :     {
    8582             :         if (!geom)
    8583             :             return nullptr;
    8584             : 
    8585             :         // Get WKB size and allocate buffer
    8586             :         size_t nSize = geom->WkbSize();
    8587             :         unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
    8588             : 
    8589             :         // Set export options with NDR byte order
    8590             :         OGRwkbExportOptions oOptions;
    8591             :         oOptions.eByteOrder = wkbNDR;
    8592             :         // and ISO to avoid wkb25DBit for Z geometries
    8593             :         oOptions.eWkbVariant = wkbVariantIso;
    8594             : 
    8595             :         // Export to WKB
    8596             :         sfcgal_geometry_t *sfcgalGeom = nullptr;
    8597             :         if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
    8598             :         {
    8599             :             sfcgalGeom = sfcgal_io_read_wkb(
    8600             :                 reinterpret_cast<const char *>(pabyWkb), nSize);
    8601             :         }
    8602             : 
    8603             :         CPLFree(pabyWkb);
    8604             :         return sfcgalGeom;
    8605             :     };
    8606             : 
    8607             :     // Handle special cases
    8608             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8609             :     {
    8610             :         std::unique_ptr<OGRLineString> poLS(
    8611             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8612             :         return exportToSFCGALViaWKB(poLS.get());
    8613             :     }
    8614             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8615             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8616             :     {
    8617             :         std::unique_ptr<OGRLineString> poLS(
    8618             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8619             :                 ->toLineString());
    8620             :         return exportToSFCGALViaWKB(poLS.get());
    8621             :     }
    8622             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8623             :     {
    8624             :         std::unique_ptr<OGRPolygon> poPolygon(
    8625             :             OGRGeometryFactory::forceToPolygon(
    8626             :                 poGeom->clone()->toCurvePolygon())
    8627             :                 ->toPolygon());
    8628             :         return exportToSFCGALViaWKB(poPolygon.get());
    8629             :     }
    8630             :     else
    8631             :     {
    8632             :         // Default case - direct export
    8633             :         return exportToSFCGALViaWKB(poGeom);
    8634             :     }
    8635             : #else
    8636             :     char *buffer = nullptr;
    8637             : 
    8638             :     // special cases - LinearRing, Circular String, Compound Curve, Curve
    8639             :     // Polygon
    8640             : 
    8641             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8642             :     {
    8643             :         // cast it to LineString and get the WKT
    8644             :         std::unique_ptr<OGRLineString> poLS(
    8645             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8646             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8647             :         {
    8648             :             sfcgal_geometry_t *_geometry =
    8649             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8650             :             CPLFree(buffer);
    8651             :             return _geometry;
    8652             :         }
    8653             :         else
    8654             :         {
    8655             :             CPLFree(buffer);
    8656             :             return nullptr;
    8657             :         }
    8658             :     }
    8659             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8660             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8661             :     {
    8662             :         // convert it to LineString and get the WKT
    8663             :         std::unique_ptr<OGRLineString> poLS(
    8664             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8665             :                 ->toLineString());
    8666             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8667             :         {
    8668             :             sfcgal_geometry_t *_geometry =
    8669             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8670             :             CPLFree(buffer);
    8671             :             return _geometry;
    8672             :         }
    8673             :         else
    8674             :         {
    8675             :             CPLFree(buffer);
    8676             :             return nullptr;
    8677             :         }
    8678             :     }
    8679             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8680             :     {
    8681             :         // convert it to Polygon and get the WKT
    8682             :         std::unique_ptr<OGRPolygon> poPolygon(
    8683             :             OGRGeometryFactory::forceToPolygon(
    8684             :                 poGeom->clone()->toCurvePolygon())
    8685             :                 ->toPolygon());
    8686             :         if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
    8687             :         {
    8688             :             sfcgal_geometry_t *_geometry =
    8689             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8690             :             CPLFree(buffer);
    8691             :             return _geometry;
    8692             :         }
    8693             :         else
    8694             :         {
    8695             :             CPLFree(buffer);
    8696             :             return nullptr;
    8697             :         }
    8698             :     }
    8699             :     else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
    8700             :     {
    8701             :         sfcgal_geometry_t *_geometry =
    8702             :             sfcgal_io_read_wkt(buffer, strlen(buffer));
    8703             :         CPLFree(buffer);
    8704             :         return _geometry;
    8705             :     }
    8706             :     else
    8707             :     {
    8708             :         CPLFree(buffer);
    8709             :         return nullptr;
    8710             :     }
    8711             : #endif
    8712             : #else
    8713           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8714           0 :     return nullptr;
    8715             : #endif
    8716             : }
    8717             : 
    8718             : //! @endcond
    8719             : 
    8720             : /************************************************************************/
    8721             : /*                         SFCGALexportToOGR()                          */
    8722             : /************************************************************************/
    8723             : 
    8724             : //! @cond Doxygen_Suppress
    8725           0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
    8726             :     UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
    8727             : {
    8728             : #ifdef HAVE_SFCGAL
    8729             :     if (geometry == nullptr)
    8730             :         return nullptr;
    8731             : 
    8732             :     sfcgal_init();
    8733             :     char *pabySFCGAL = nullptr;
    8734             :     size_t nLength = 0;
    8735             : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8736             : 
    8737             :     sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
    8738             : 
    8739             :     if (pabySFCGAL == nullptr || nLength == 0)
    8740             :         return nullptr;
    8741             : 
    8742             :     OGRGeometry *poGeom = nullptr;
    8743             :     OGRErr eErr = OGRGeometryFactory::createFromWkb(
    8744             :         reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
    8745             :         nLength);
    8746             : 
    8747             :     free(pabySFCGAL);
    8748             : 
    8749             :     if (eErr == OGRERR_NONE)
    8750             :     {
    8751             :         return poGeom;
    8752             :     }
    8753             :     else
    8754             :     {
    8755             :         return nullptr;
    8756             :     }
    8757             : #else
    8758             :     sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
    8759             :     char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
    8760             :     memcpy(pszWKT, pabySFCGAL, nLength);
    8761             :     pszWKT[nLength] = 0;
    8762             :     free(pabySFCGAL);
    8763             : 
    8764             :     sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
    8765             : 
    8766             :     OGRGeometry *poGeom = nullptr;
    8767             :     if (geom_type == SFCGAL_TYPE_POINT)
    8768             :     {
    8769             :         poGeom = new OGRPoint();
    8770             :     }
    8771             :     else if (geom_type == SFCGAL_TYPE_LINESTRING)
    8772             :     {
    8773             :         poGeom = new OGRLineString();
    8774             :     }
    8775             :     else if (geom_type == SFCGAL_TYPE_POLYGON)
    8776             :     {
    8777             :         poGeom = new OGRPolygon();
    8778             :     }
    8779             :     else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
    8780             :     {
    8781             :         poGeom = new OGRMultiPoint();
    8782             :     }
    8783             :     else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
    8784             :     {
    8785             :         poGeom = new OGRMultiLineString();
    8786             :     }
    8787             :     else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
    8788             :     {
    8789             :         poGeom = new OGRMultiPolygon();
    8790             :     }
    8791             :     else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
    8792             :     {
    8793             :         poGeom = new OGRGeometryCollection();
    8794             :     }
    8795             :     else if (geom_type == SFCGAL_TYPE_TRIANGLE)
    8796             :     {
    8797             :         poGeom = new OGRTriangle();
    8798             :     }
    8799             :     else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
    8800             :     {
    8801             :         poGeom = new OGRPolyhedralSurface();
    8802             :     }
    8803             :     else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
    8804             :     {
    8805             :         poGeom = new OGRTriangulatedSurface();
    8806             :     }
    8807             :     else
    8808             :     {
    8809             :         CPLFree(pszWKT);
    8810             :         return nullptr;
    8811             :     }
    8812             : 
    8813             :     const char *pszWKTTmp = pszWKT;
    8814             :     if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
    8815             :     {
    8816             :         CPLFree(pszWKT);
    8817             :         return poGeom;
    8818             :     }
    8819             :     else
    8820             :     {
    8821             :         delete poGeom;
    8822             :         CPLFree(pszWKT);
    8823             :         return nullptr;
    8824             :     }
    8825             : #endif
    8826             : #else
    8827           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8828           0 :     return nullptr;
    8829             : #endif
    8830             : }
    8831             : 
    8832             : //! @endcond
    8833             : 
    8834             : //! @cond Doxygen_Suppress
    8835        8383 : bool OGRGeometry::IsSFCGALCompatible() const
    8836             : {
    8837        8383 :     const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
    8838        8383 :     if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
    8839             :         eGType == wkbTIN)
    8840             :     {
    8841           3 :         return TRUE;
    8842             :     }
    8843        8380 :     if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
    8844             :     {
    8845          13 :         const OGRGeometryCollection *poGC = toGeometryCollection();
    8846          13 :         bool bIsSFCGALCompatible = false;
    8847          13 :         for (auto &&poSubGeom : *poGC)
    8848             :         {
    8849             :             OGRwkbGeometryType eSubGeomType =
    8850          13 :                 wkbFlatten(poSubGeom->getGeometryType());
    8851          13 :             if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
    8852             :             {
    8853           0 :                 bIsSFCGALCompatible = true;
    8854             :             }
    8855          13 :             else if (eSubGeomType != wkbMultiPolygon)
    8856             :             {
    8857          13 :                 bIsSFCGALCompatible = false;
    8858          13 :                 break;
    8859             :             }
    8860             :         }
    8861          13 :         return bIsSFCGALCompatible;
    8862             :     }
    8863        8367 :     return FALSE;
    8864             : }
    8865             : 
    8866             : //! @endcond
    8867             : 
    8868             : /************************************************************************/
    8869             : /*                      roundCoordinatesIEEE754()                       */
    8870             : /************************************************************************/
    8871             : 
    8872             : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
    8873             :  * double-precision binary representation.
    8874             :  *
    8875             :  * Determines the number of bits (N) required to represent a coordinate value
    8876             :  * with a specified number of digits after the decimal point, and then sets all
    8877             :  * but the N most significant bits to zero. The resulting coordinate value will
    8878             :  * still round to the original value (e.g. after roundCoordinates()), but will
    8879             :  * have improved compressiblity.
    8880             :  *
    8881             :  * @param options Contains the precision requirements.
    8882             :  * @since GDAL 3.9
    8883             :  */
    8884           1 : void OGRGeometry::roundCoordinatesIEEE754(
    8885             :     const OGRGeomCoordinateBinaryPrecision &options)
    8886             : {
    8887             :     struct Quantizer : public OGRDefaultGeometryVisitor
    8888             :     {
    8889             :         const OGRGeomCoordinateBinaryPrecision &m_options;
    8890             : 
    8891           1 :         explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
    8892           1 :             : m_options(optionsIn)
    8893             :         {
    8894           1 :         }
    8895             : 
    8896             :         using OGRDefaultGeometryVisitor::visit;
    8897             : 
    8898           3 :         void visit(OGRPoint *poPoint) override
    8899             :         {
    8900           3 :             if (m_options.nXYBitPrecision != INT_MIN)
    8901             :             {
    8902             :                 uint64_t i;
    8903             :                 double d;
    8904           3 :                 d = poPoint->getX();
    8905           3 :                 memcpy(&i, &d, sizeof(i));
    8906           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8907           3 :                 memcpy(&d, &i, sizeof(i));
    8908           3 :                 poPoint->setX(d);
    8909           3 :                 d = poPoint->getY();
    8910           3 :                 memcpy(&i, &d, sizeof(i));
    8911           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8912           3 :                 memcpy(&d, &i, sizeof(i));
    8913           3 :                 poPoint->setY(d);
    8914             :             }
    8915           3 :             if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
    8916             :             {
    8917             :                 uint64_t i;
    8918             :                 double d;
    8919           3 :                 d = poPoint->getZ();
    8920           3 :                 memcpy(&i, &d, sizeof(i));
    8921           3 :                 i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
    8922           3 :                 memcpy(&d, &i, sizeof(i));
    8923           3 :                 poPoint->setZ(d);
    8924             :             }
    8925           3 :             if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
    8926             :             {
    8927             :                 uint64_t i;
    8928             :                 double d;
    8929           3 :                 d = poPoint->getM();
    8930           3 :                 memcpy(&i, &d, sizeof(i));
    8931           3 :                 i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
    8932           3 :                 memcpy(&d, &i, sizeof(i));
    8933           3 :                 poPoint->setM(d);
    8934             :             }
    8935           3 :         }
    8936             :     };
    8937             : 
    8938           2 :     Quantizer quantizer(options);
    8939           1 :     accept(&quantizer);
    8940           1 : }
    8941             : 
    8942             : /************************************************************************/
    8943             : /*                               visit()                                */
    8944             : /************************************************************************/
    8945             : 
    8946         105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
    8947             : {
    8948        1248 :     for (auto &&oPoint : *poGeom)
    8949             :     {
    8950        1143 :         oPoint.accept(this);
    8951             :     }
    8952         105 : }
    8953             : 
    8954         104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
    8955             : {
    8956         104 :     _visit(poGeom);
    8957         104 : }
    8958             : 
    8959          80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
    8960             : {
    8961          80 :     visit(poGeom->toUpperClass());
    8962          80 : }
    8963             : 
    8964           1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
    8965             : {
    8966           1 :     _visit(poGeom);
    8967           1 : }
    8968             : 
    8969          78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
    8970             : {
    8971         159 :     for (auto &&poSubGeom : *poGeom)
    8972          81 :         poSubGeom->accept(this);
    8973          78 : }
    8974             : 
    8975          77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
    8976             : {
    8977          77 :     visit(poGeom->toUpperClass());
    8978          77 : }
    8979             : 
    8980           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
    8981             : {
    8982           1 :     visit(poGeom->toUpperClass());
    8983           1 : }
    8984             : 
    8985           8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
    8986             : {
    8987           8 :     visit(poGeom->toUpperClass());
    8988           8 : }
    8989             : 
    8990          14 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
    8991             : {
    8992          14 :     visit(poGeom->toUpperClass());
    8993          14 : }
    8994             : 
    8995          26 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
    8996             : {
    8997          75 :     for (auto &&poSubGeom : *poGeom)
    8998          49 :         poSubGeom->accept(this);
    8999          26 : }
    9000             : 
    9001           1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
    9002             : {
    9003           2 :     for (auto &&poSubGeom : *poGeom)
    9004           1 :         poSubGeom->accept(this);
    9005           1 : }
    9006             : 
    9007           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
    9008             : {
    9009           1 :     visit(poGeom->toUpperClass());
    9010           1 : }
    9011             : 
    9012           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
    9013             : {
    9014           1 :     visit(poGeom->toUpperClass());
    9015           1 : }
    9016             : 
    9017           2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
    9018             : {
    9019           2 :     visit(poGeom->toUpperClass());
    9020           2 : }
    9021             : 
    9022           2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
    9023             : {
    9024           4 :     for (auto &&poSubGeom : *poGeom)
    9025           2 :         poSubGeom->accept(this);
    9026           2 : }
    9027             : 
    9028           1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
    9029             : {
    9030           1 :     visit(poGeom->toUpperClass());
    9031           1 : }
    9032             : 
    9033         127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
    9034             : {
    9035        2988 :     for (auto &&oPoint : *poGeom)
    9036             :     {
    9037        2861 :         oPoint.accept(this);
    9038             :     }
    9039         127 : }
    9040             : 
    9041         121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
    9042             : {
    9043         121 :     _visit(poGeom);
    9044         121 : }
    9045             : 
    9046         110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
    9047             : {
    9048         110 :     visit(poGeom->toUpperClass());
    9049         110 : }
    9050             : 
    9051           6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
    9052             : {
    9053           6 :     _visit(poGeom);
    9054           6 : }
    9055             : 
    9056         112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
    9057             : {
    9058         225 :     for (auto &&poSubGeom : *poGeom)
    9059         113 :         poSubGeom->accept(this);
    9060         112 : }
    9061             : 
    9062         109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
    9063             : {
    9064         109 :     visit(poGeom->toUpperClass());
    9065         109 : }
    9066             : 
    9067          64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
    9068             : {
    9069          64 :     visit(poGeom->toUpperClass());
    9070          64 : }
    9071             : 
    9072           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
    9073             : {
    9074           1 :     visit(poGeom->toUpperClass());
    9075           1 : }
    9076             : 
    9077           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
    9078             : {
    9079           1 :     visit(poGeom->toUpperClass());
    9080           1 : }
    9081             : 
    9082          69 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
    9083             : {
    9084         325 :     for (auto &&poSubGeom : *poGeom)
    9085         256 :         poSubGeom->accept(this);
    9086          69 : }
    9087             : 
    9088           3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
    9089             : {
    9090          14 :     for (auto &&poSubGeom : *poGeom)
    9091          11 :         poSubGeom->accept(this);
    9092           3 : }
    9093             : 
    9094           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
    9095             : {
    9096           1 :     visit(poGeom->toUpperClass());
    9097           1 : }
    9098             : 
    9099           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
    9100             : {
    9101           1 :     visit(poGeom->toUpperClass());
    9102           1 : }
    9103             : 
    9104           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
    9105             : {
    9106           2 :     visit(poGeom->toUpperClass());
    9107           2 : }
    9108             : 
    9109           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
    9110             : {
    9111           4 :     for (auto &&poSubGeom : *poGeom)
    9112           2 :         poSubGeom->accept(this);
    9113           2 : }
    9114             : 
    9115           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
    9116             : {
    9117           1 :     visit(poGeom->toUpperClass());
    9118           1 : }
    9119             : 
    9120             : /************************************************************************/
    9121             : /*                     OGRGeometryUniquePtrDeleter                      */
    9122             : /************************************************************************/
    9123             : 
    9124             : //! @cond Doxygen_Suppress
    9125        1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
    9126             : {
    9127        1333 :     delete poGeom;
    9128        1333 : }
    9129             : 
    9130             : //! @endcond
    9131             : 
    9132             : /************************************************************************/
    9133             : /*                 OGRPreparedGeometryUniquePtrDeleter                  */
    9134             : /************************************************************************/
    9135             : 
    9136             : //! @cond Doxygen_Suppress
    9137         144 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
    9138             :     OGRPreparedGeometry *poPreparedGeom) const
    9139             : {
    9140         144 :     OGRDestroyPreparedGeometry(poPreparedGeom);
    9141         144 : }
    9142             : 
    9143             : //! @endcond
    9144             : 
    9145             : /************************************************************************/
    9146             : /*                    HomogenizeDimensionalityWith()                    */
    9147             : /************************************************************************/
    9148             : 
    9149             : //! @cond Doxygen_Suppress
    9150     3299500 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
    9151             : {
    9152     3299500 :     if (poOtherGeom->Is3D() && !Is3D())
    9153     1328980 :         set3D(TRUE);
    9154             : 
    9155     3299500 :     if (poOtherGeom->IsMeasured() && !IsMeasured())
    9156         851 :         setMeasured(TRUE);
    9157             : 
    9158     3299500 :     if (!poOtherGeom->Is3D() && Is3D())
    9159         298 :         poOtherGeom->set3D(TRUE);
    9160             : 
    9161     3299500 :     if (!poOtherGeom->IsMeasured() && IsMeasured())
    9162          41 :         poOtherGeom->setMeasured(TRUE);
    9163     3299500 : }
    9164             : 
    9165             : //! @endcond
    9166             : 
    9167             : /************************************************************************/
    9168             : /*             OGRGeomCoordinateBinaryPrecision::SetFrom()              */
    9169             : /************************************************************************/
    9170             : 
    9171             : /** Set binary precision options from resolution.
    9172             :  *
    9173             :  * @since GDAL 3.9
    9174             :  */
    9175          16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
    9176             :     const OGRGeomCoordinatePrecision &prec)
    9177             : {
    9178          16 :     if (prec.dfXYResolution != 0)
    9179             :     {
    9180          16 :         nXYBitPrecision =
    9181          16 :             static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
    9182             :     }
    9183          16 :     if (prec.dfZResolution != 0)
    9184             :     {
    9185          12 :         nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
    9186             :     }
    9187          16 :     if (prec.dfMResolution != 0)
    9188             :     {
    9189          12 :         nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
    9190             :     }
    9191          16 : }
    9192             : 
    9193             : /************************************************************************/
    9194             : /*                     OGRwkbExportOptionsCreate()                      */
    9195             : /************************************************************************/
    9196             : 
    9197             : /**
    9198             :  * \brief Create geometry WKB export options.
    9199             :  *
    9200             :  * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
    9201             :  *
    9202             :  * @return object to be freed with OGRwkbExportOptionsDestroy().
    9203             :  * @since GDAL 3.9
    9204             :  */
    9205           2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
    9206             : {
    9207           2 :     return new OGRwkbExportOptions;
    9208             : }
    9209             : 
    9210             : /************************************************************************/
    9211             : /*                     OGRwkbExportOptionsDestroy()                     */
    9212             : /************************************************************************/
    9213             : 
    9214             : /**
    9215             :  * \brief Destroy object returned by OGRwkbExportOptionsCreate()
    9216             :  *
    9217             :  * @param psOptions WKB export options
    9218             :  * @since GDAL 3.9
    9219             :  */
    9220             : 
    9221           2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
    9222             : {
    9223           2 :     delete psOptions;
    9224           2 : }
    9225             : 
    9226             : /************************************************************************/
    9227             : /*                  OGRwkbExportOptionsSetByteOrder()                   */
    9228             : /************************************************************************/
    9229             : 
    9230             : /**
    9231             :  * \brief Set the WKB byte order.
    9232             :  *
    9233             :  * @param psOptions WKB export options
    9234             :  * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
    9235             :  * Intel)
    9236             :  * @since GDAL 3.9
    9237             :  */
    9238             : 
    9239           1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
    9240             :                                      OGRwkbByteOrder eByteOrder)
    9241             : {
    9242           1 :     psOptions->eByteOrder = eByteOrder;
    9243           1 : }
    9244             : 
    9245             : /************************************************************************/
    9246             : /*                   OGRwkbExportOptionsSetVariant()                    */
    9247             : /************************************************************************/
    9248             : 
    9249             : /**
    9250             :  * \brief Set the WKB variant
    9251             :  *
    9252             :  * @param psOptions WKB export options
    9253             :  * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
    9254             :  * wkbVariantPostGIS1
    9255             :  * @since GDAL 3.9
    9256             :  */
    9257             : 
    9258           1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
    9259             :                                    OGRwkbVariant eWkbVariant)
    9260             : {
    9261           1 :     psOptions->eWkbVariant = eWkbVariant;
    9262           1 : }
    9263             : 
    9264             : /************************************************************************/
    9265             : /*                  OGRwkbExportOptionsSetPrecision()                   */
    9266             : /************************************************************************/
    9267             : 
    9268             : /**
    9269             :  * \brief Set precision options
    9270             :  *
    9271             :  * @param psOptions WKB export options
    9272             :  * @param hPrecisionOptions Precision options (might be null to reset them)
    9273             :  * @since GDAL 3.9
    9274             :  */
    9275             : 
    9276           1 : void OGRwkbExportOptionsSetPrecision(
    9277             :     OGRwkbExportOptions *psOptions,
    9278             :     OGRGeomCoordinatePrecisionH hPrecisionOptions)
    9279             : {
    9280           1 :     psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
    9281           1 :     if (hPrecisionOptions)
    9282           1 :         psOptions->sPrecision.SetFrom(*hPrecisionOptions);
    9283           1 : }
    9284             : 
    9285             : /************************************************************************/
    9286             : /*                            IsRectangle()                             */
    9287             : /************************************************************************/
    9288             : 
    9289             : /**
    9290             :  * \brief Returns whether the geometry is a polygon with 4 corners forming
    9291             :  * a rectangle.
    9292             :  *
    9293             :  * @since GDAL 3.10
    9294             :  */
    9295       53030 : bool OGRGeometry::IsRectangle() const
    9296             : {
    9297       53030 :     if (wkbFlatten(getGeometryType()) != wkbPolygon)
    9298         352 :         return false;
    9299             : 
    9300       52678 :     const OGRPolygon *poPoly = toPolygon();
    9301             : 
    9302       52678 :     if (poPoly->getNumInteriorRings() != 0)
    9303          27 :         return false;
    9304             : 
    9305       52651 :     const OGRLinearRing *poRing = poPoly->getExteriorRing();
    9306       52651 :     if (!poRing)
    9307           4 :         return false;
    9308             : 
    9309       52647 :     if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
    9310         206 :         return false;
    9311             : 
    9312             :     // If the ring has 5 points, the last should be the first.
    9313      104825 :     if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
    9314       52384 :                                         poRing->getY(0) != poRing->getY(4)))
    9315           1 :         return false;
    9316             : 
    9317             :     // Polygon with first segment in "y" direction.
    9318      104163 :     if (poRing->getX(0) == poRing->getX(1) &&
    9319      103445 :         poRing->getY(1) == poRing->getY(2) &&
    9320      155885 :         poRing->getX(2) == poRing->getX(3) &&
    9321       51722 :         poRing->getY(3) == poRing->getY(0))
    9322       51722 :         return true;
    9323             : 
    9324             :     // Polygon with first segment in "x" direction.
    9325        1355 :     if (poRing->getY(0) == poRing->getY(1) &&
    9326        1274 :         poRing->getX(1) == poRing->getX(2) &&
    9327        1992 :         poRing->getY(2) == poRing->getY(3) &&
    9328         637 :         poRing->getX(3) == poRing->getX(0))
    9329         637 :         return true;
    9330             : 
    9331          81 :     return false;
    9332             : }
    9333             : 
    9334             : /************************************************************************/
    9335             : /*                           hasEmptyParts()                            */
    9336             : /************************************************************************/
    9337             : 
    9338             : /**
    9339             :  * \brief Returns whether a geometry has empty parts/rings.
    9340             :  *
    9341             :  * Returns true if removeEmptyParts() will modify the geometry.
    9342             :  *
    9343             :  * This is different from IsEmpty().
    9344             :  *
    9345             :  * @since GDAL 3.10
    9346             :  */
    9347         103 : bool OGRGeometry::hasEmptyParts() const
    9348             : {
    9349         103 :     return false;
    9350             : }
    9351             : 
    9352             : /************************************************************************/
    9353             : /*                          removeEmptyParts()                          */
    9354             : /************************************************************************/
    9355             : 
    9356             : /**
    9357             :  * \brief Remove empty parts/rings from this geometry.
    9358             :  *
    9359             :  * @since GDAL 3.10
    9360             :  */
    9361          17 : void OGRGeometry::removeEmptyParts()
    9362             : {
    9363          17 : }
    9364             : 
    9365             : /************************************************************************/
    9366             : /*                        ~IOGRGeometryVisitor()                        */
    9367             : /************************************************************************/
    9368             : 
    9369             : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
    9370             : 
    9371             : /************************************************************************/
    9372             : /*                     ~IOGRConstGeometryVisitor()                      */
    9373             : /************************************************************************/
    9374             : 
    9375             : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;

Generated by: LCOV version 1.14