LCOV - code coverage report
Current view: top level - ogr - ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1788 2102 85.1 %
Date: 2025-08-19 18:03:11 Functions: 214 238 89.9 %

          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 <stdexcept>
      26             : #include <string>
      27             : 
      28             : #include "cpl_conv.h"
      29             : #include "cpl_error.h"
      30             : #include "cpl_multiproc.h"
      31             : #include "cpl_string.h"
      32             : #include "ogr_api.h"
      33             : #include "ogr_core.h"
      34             : #include "ogr_geos.h"
      35             : #include "ogr_sfcgal.h"
      36             : #include "ogr_libs.h"
      37             : #include "ogr_p.h"
      38             : #include "ogr_spatialref.h"
      39             : #include "ogr_srs_api.h"
      40             : #include "ogr_wkb.h"
      41             : 
      42             : #define SFCGAL_MAKE_VERSION(major, minor, patch)                               \
      43             :     ((major)*10000 + (minor)*100 + (patch))
      44             : #define SFCGAL_VERSION                                                         \
      45             :     SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR,            \
      46             :                         SFCGAL_VERSION_PATCH)
      47             : 
      48             : #ifndef HAVE_GEOS
      49             : #define UNUSED_IF_NO_GEOS CPL_UNUSED
      50             : #else
      51             : #define UNUSED_IF_NO_GEOS
      52             : #endif
      53             : 
      54             : //! @cond Doxygen_Suppress
      55             : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
      56             : //! @endcond
      57             : 
      58             : #ifdef HAVE_GEOS
      59         107 : static void OGRGEOSErrorHandler(const char *fmt, ...)
      60             : {
      61             :     va_list args;
      62             : 
      63         107 :     va_start(args, fmt);
      64         107 :     CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
      65         107 :     va_end(args);
      66         107 : }
      67             : 
      68         102 : static void OGRGEOSWarningHandler(const char *fmt, ...)
      69             : {
      70             :     va_list args;
      71             : 
      72         102 :     va_start(args, fmt);
      73         102 :     CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
      74         102 :     va_end(args);
      75         102 : }
      76             : #endif
      77             : 
      78             : /************************************************************************/
      79             : /*                            OGRWktOptions()                             */
      80             : /************************************************************************/
      81             : 
      82       11142 : int OGRWktOptions::getDefaultPrecision()
      83             : {
      84       11142 :     return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
      85             : }
      86             : 
      87       11238 : bool OGRWktOptions::getDefaultRound()
      88             : {
      89       11238 :     return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                            OGRGeometry()                             */
      94             : /************************************************************************/
      95             : 
      96             : OGRGeometry::OGRGeometry() = default;
      97             : 
      98             : /************************************************************************/
      99             : /*                   OGRGeometry( const OGRGeometry& )                  */
     100             : /************************************************************************/
     101             : 
     102             : /**
     103             :  * \brief Copy constructor.
     104             :  *
     105             :  * Note: before GDAL 2.1, only the default implementation of the constructor
     106             :  * existed, which could be unsafe to use.
     107             :  *
     108             :  * @since GDAL 2.1
     109             :  */
     110             : 
     111     2135120 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
     112     2135120 :     : poSRS(other.poSRS), flags(other.flags)
     113             : {
     114     2135120 :     if (poSRS != nullptr)
     115      486250 :         const_cast<OGRSpatialReference *>(poSRS)->Reference();
     116     2135120 : }
     117             : 
     118             : /************************************************************************/
     119             : /*                   OGRGeometry( OGRGeometry&& )                       */
     120             : /************************************************************************/
     121             : 
     122             : /**
     123             :  * \brief Move constructor.
     124             :  *
     125             :  * @since GDAL 3.11
     126             :  */
     127             : 
     128      112145 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
     129      112145 :     : poSRS(other.poSRS), flags(other.flags)
     130             : {
     131      112145 :     other.poSRS = nullptr;
     132      112145 : }
     133             : 
     134             : /************************************************************************/
     135             : /*                            ~OGRGeometry()                            */
     136             : /************************************************************************/
     137             : 
     138    25615900 : OGRGeometry::~OGRGeometry()
     139             : 
     140             : {
     141    12807900 :     if (poSRS != nullptr)
     142     3988420 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     143    12807900 : }
     144             : 
     145             : /************************************************************************/
     146             : /*                    operator=( const OGRGeometry&)                    */
     147             : /************************************************************************/
     148             : 
     149             : /**
     150             :  * \brief Assignment operator.
     151             :  *
     152             :  * Note: before GDAL 2.1, only the default implementation of the operator
     153             :  * existed, which could be unsafe to use.
     154             :  *
     155             :  * @since GDAL 2.1
     156             :  */
     157             : 
     158        1217 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
     159             : {
     160        1217 :     if (this != &other)
     161             :     {
     162        1217 :         empty();
     163        1217 :         assignSpatialReference(other.getSpatialReference());
     164        1217 :         flags = other.flags;
     165             :     }
     166        1217 :     return *this;
     167             : }
     168             : 
     169             : /************************************************************************/
     170             : /*                    operator=( OGRGeometry&&)                         */
     171             : /************************************************************************/
     172             : 
     173             : /**
     174             :  * \brief Move assignment operator.
     175             :  *
     176             :  * @since GDAL 3.11
     177             :  */
     178             : 
     179       85555 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
     180             : {
     181       85555 :     if (this != &other)
     182             :     {
     183       85555 :         poSRS = other.poSRS;
     184       85555 :         other.poSRS = nullptr;
     185       85555 :         flags = other.flags;
     186             :     }
     187       85555 :     return *this;
     188             : }
     189             : 
     190             : /************************************************************************/
     191             : /*                            dumpReadable()                            */
     192             : /************************************************************************/
     193             : 
     194             : /**
     195             :  * \brief Dump geometry in well known text format to indicated output file.
     196             :  *
     197             :  * A few options can be defined to change the default dump :
     198             :  * <ul>
     199             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     200             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     201             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     202             :  * </ul>
     203             :  *
     204             :  * This method is the same as the C function OGR_G_DumpReadable().
     205             :  *
     206             :  * @param fp the text file to write the geometry to.
     207             :  * @param pszPrefix the prefix to put on each line of output.
     208             :  * @param papszOptions NULL terminated list of options (may be NULL)
     209             :  */
     210             : 
     211           0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
     212             :                                CSLConstList papszOptions) const
     213             : 
     214             : {
     215           0 :     if (fp == nullptr)
     216           0 :         fp = stdout;
     217             : 
     218           0 :     const auto osStr = dumpReadable(pszPrefix, papszOptions);
     219           0 :     fprintf(fp, "%s", osStr.c_str());
     220           0 : }
     221             : 
     222             : /************************************************************************/
     223             : /*                            dumpReadable()                            */
     224             : /************************************************************************/
     225             : 
     226             : /**
     227             :  * \brief Dump geometry in well known text format to indicated output file.
     228             :  *
     229             :  * A few options can be defined to change the default dump :
     230             :  * <ul>
     231             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     232             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     233             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     234             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
     235             :  * in WKT (added in GDAL 3.9)</li>
     236             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
     237             :  * WKT (added in GDAL 3.9)</li>
     238             :  * </ul>
     239             :  *
     240             :  * @param pszPrefix the prefix to put on each line of output.
     241             :  * @param papszOptions NULL terminated list of options (may be NULL)
     242             :  * @return a string with the geometry representation.
     243             :  * @since GDAL 3.7
     244             :  */
     245             : 
     246         303 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
     247             :                                       CSLConstList papszOptions) const
     248             : 
     249             : {
     250         303 :     if (pszPrefix == nullptr)
     251         303 :         pszPrefix = "";
     252             : 
     253         303 :     std::string osRet;
     254             : 
     255             :     const auto exportToWktWithOpts =
     256        2044 :         [this, pszPrefix, papszOptions, &osRet](bool bIso)
     257             :     {
     258         292 :         OGRErr err(OGRERR_NONE);
     259         292 :         OGRWktOptions opts;
     260         292 :         if (const char *pszXYPrecision =
     261         292 :                 CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
     262             :         {
     263           1 :             opts.format = OGRWktFormat::F;
     264           1 :             opts.xyPrecision = atoi(pszXYPrecision);
     265             :         }
     266         292 :         if (const char *pszZPrecision =
     267         292 :                 CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
     268             :         {
     269           1 :             opts.format = OGRWktFormat::F;
     270           1 :             opts.zPrecision = atoi(pszZPrecision);
     271             :         }
     272         292 :         if (bIso)
     273         292 :             opts.variant = wkbVariantIso;
     274         584 :         std::string wkt = exportToWkt(opts, &err);
     275         292 :         if (err == OGRERR_NONE)
     276             :         {
     277         292 :             osRet = pszPrefix;
     278         292 :             osRet += wkt.data();
     279         292 :             osRet += '\n';
     280             :         }
     281         292 :     };
     282             : 
     283             :     const char *pszDisplayGeometry =
     284         303 :         CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
     285         303 :     if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
     286             :     {
     287          11 :         osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
     288          11 :         switch (getGeometryType())
     289             :         {
     290           1 :             case wkbUnknown:
     291             :             case wkbNone:
     292             :             case wkbPoint:
     293             :             case wkbPoint25D:
     294             :             case wkbPointM:
     295             :             case wkbPointZM:
     296           1 :                 break;
     297           0 :             case wkbPolyhedralSurface:
     298             :             case wkbTIN:
     299             :             case wkbPolyhedralSurfaceZ:
     300             :             case wkbTINZ:
     301             :             case wkbPolyhedralSurfaceM:
     302             :             case wkbTINM:
     303             :             case wkbPolyhedralSurfaceZM:
     304             :             case wkbTINZM:
     305             :             {
     306           0 :                 const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
     307             :                 osRet +=
     308           0 :                     CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
     309           0 :                 for (auto &&poSubGeom : *poPS)
     310             :                 {
     311           0 :                     osRet += pszPrefix;
     312           0 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     313             :                 }
     314           0 :                 break;
     315             :             }
     316           0 :             case wkbLineString:
     317             :             case wkbLineString25D:
     318             :             case wkbLineStringM:
     319             :             case wkbLineStringZM:
     320             :             case wkbCircularString:
     321             :             case wkbCircularStringZ:
     322             :             case wkbCircularStringM:
     323             :             case wkbCircularStringZM:
     324             :             {
     325           0 :                 const OGRSimpleCurve *poSC = toSimpleCurve();
     326           0 :                 osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
     327           0 :                 break;
     328             :             }
     329          10 :             case wkbPolygon:
     330             :             case wkbTriangle:
     331             :             case wkbTriangleZ:
     332             :             case wkbTriangleM:
     333             :             case wkbTriangleZM:
     334             :             case wkbPolygon25D:
     335             :             case wkbPolygonM:
     336             :             case wkbPolygonZM:
     337             :             case wkbCurvePolygon:
     338             :             case wkbCurvePolygonZ:
     339             :             case wkbCurvePolygonM:
     340             :             case wkbCurvePolygonZM:
     341             :             {
     342          10 :                 const OGRCurvePolygon *poPoly = toCurvePolygon();
     343          10 :                 const OGRCurve *poRing = poPoly->getExteriorRingCurve();
     344          10 :                 const int nRings = poPoly->getNumInteriorRings();
     345          10 :                 if (poRing == nullptr)
     346             :                 {
     347           0 :                     osRet += "empty";
     348             :                 }
     349             :                 else
     350             :                 {
     351          10 :                     osRet += CPLOPrintf("%d points", poRing->getNumPoints());
     352          10 :                     if (wkbFlatten(poRing->getGeometryType()) ==
     353             :                         wkbCompoundCurve)
     354             :                     {
     355           0 :                         osRet += " (";
     356           0 :                         osRet += poRing->dumpReadable(nullptr, papszOptions);
     357           0 :                         osRet += ")";
     358             :                     }
     359          10 :                     if (nRings)
     360             :                     {
     361           0 :                         osRet += CPLOPrintf(", %d inner rings (", nRings);
     362           0 :                         for (int ir = 0; ir < nRings; ir++)
     363             :                         {
     364           0 :                             poRing = poPoly->getInteriorRingCurve(ir);
     365           0 :                             if (ir)
     366           0 :                                 osRet += ", ";
     367             :                             osRet +=
     368           0 :                                 CPLOPrintf("%d points", poRing->getNumPoints());
     369           0 :                             if (wkbFlatten(poRing->getGeometryType()) ==
     370             :                                 wkbCompoundCurve)
     371             :                             {
     372           0 :                                 osRet += " (";
     373             :                                 osRet +=
     374           0 :                                     poRing->dumpReadable(nullptr, papszOptions);
     375           0 :                                 osRet += ")";
     376             :                             }
     377             :                         }
     378           0 :                         osRet += ")";
     379             :                     }
     380             :                 }
     381          10 :                 osRet += "\n";
     382          10 :                 break;
     383             :             }
     384           0 :             case wkbCompoundCurve:
     385             :             case wkbCompoundCurveZ:
     386             :             case wkbCompoundCurveM:
     387             :             case wkbCompoundCurveZM:
     388             :             {
     389           0 :                 const OGRCompoundCurve *poCC = toCompoundCurve();
     390           0 :                 if (poCC->getNumCurves() == 0)
     391             :                 {
     392           0 :                     osRet += "empty";
     393             :                 }
     394             :                 else
     395             :                 {
     396           0 :                     for (int i = 0; i < poCC->getNumCurves(); i++)
     397             :                     {
     398           0 :                         if (i)
     399           0 :                             osRet += ", ";
     400             :                         osRet +=
     401           0 :                             CPLOPrintf("%s (%d points)",
     402           0 :                                        poCC->getCurve(i)->getGeometryName(),
     403           0 :                                        poCC->getCurve(i)->getNumPoints());
     404             :                     }
     405             :                 }
     406           0 :                 break;
     407             :             }
     408             : 
     409           0 :             case wkbMultiPoint:
     410             :             case wkbMultiLineString:
     411             :             case wkbMultiPolygon:
     412             :             case wkbMultiCurve:
     413             :             case wkbMultiSurface:
     414             :             case wkbGeometryCollection:
     415             :             case wkbMultiPoint25D:
     416             :             case wkbMultiLineString25D:
     417             :             case wkbMultiPolygon25D:
     418             :             case wkbMultiCurveZ:
     419             :             case wkbMultiSurfaceZ:
     420             :             case wkbGeometryCollection25D:
     421             :             case wkbMultiPointM:
     422             :             case wkbMultiLineStringM:
     423             :             case wkbMultiPolygonM:
     424             :             case wkbMultiCurveM:
     425             :             case wkbMultiSurfaceM:
     426             :             case wkbGeometryCollectionM:
     427             :             case wkbMultiPointZM:
     428             :             case wkbMultiLineStringZM:
     429             :             case wkbMultiPolygonZM:
     430             :             case wkbMultiCurveZM:
     431             :             case wkbMultiSurfaceZM:
     432             :             case wkbGeometryCollectionZM:
     433             :             {
     434           0 :                 const OGRGeometryCollection *poColl = toGeometryCollection();
     435             :                 osRet +=
     436           0 :                     CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
     437           0 :                 for (auto &&poSubGeom : *poColl)
     438             :                 {
     439           0 :                     osRet += pszPrefix;
     440           0 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     441             :                 }
     442           0 :                 break;
     443             :             }
     444           0 :             case wkbLinearRing:
     445             :             case wkbCurve:
     446             :             case wkbSurface:
     447             :             case wkbCurveZ:
     448             :             case wkbSurfaceZ:
     449             :             case wkbCurveM:
     450             :             case wkbSurfaceM:
     451             :             case wkbCurveZM:
     452             :             case wkbSurfaceZM:
     453           0 :                 break;
     454          11 :         }
     455             :     }
     456         292 :     else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
     457             :     {
     458           0 :         exportToWktWithOpts(/* bIso=*/false);
     459             :     }
     460         292 :     else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
     461           0 :              EQUAL(pszDisplayGeometry, "ISO_WKT"))
     462             :     {
     463         292 :         exportToWktWithOpts(/* bIso=*/true);
     464             :     }
     465             : 
     466         606 :     return osRet;
     467             : }
     468             : 
     469             : /************************************************************************/
     470             : /*                         OGR_G_DumpReadable()                         */
     471             : /************************************************************************/
     472             : /**
     473             :  * \brief Dump geometry in well known text format to indicated output file.
     474             :  *
     475             :  * This method is the same as the CPP method OGRGeometry::dumpReadable.
     476             :  *
     477             :  * @param hGeom handle on the geometry to dump.
     478             :  * @param fp the text file to write the geometry to.
     479             :  * @param pszPrefix the prefix to put on each line of output.
     480             :  */
     481             : 
     482           0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
     483             : 
     484             : {
     485           0 :     VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
     486             : 
     487           0 :     OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
     488             : }
     489             : 
     490             : /************************************************************************/
     491             : /*                       assignSpatialReference()                       */
     492             : /************************************************************************/
     493             : 
     494             : /**
     495             :  * \brief Assign spatial reference to this object.
     496             :  *
     497             :  * Any existing spatial reference
     498             :  * is replaced, but under no circumstances does this result in the object
     499             :  * being reprojected.  It is just changing the interpretation of the existing
     500             :  * geometry.  Note that assigning a spatial reference increments the
     501             :  * reference count on the OGRSpatialReference, but does not copy it.
     502             :  *
     503             :  * Starting with GDAL 2.3, this will also assign the spatial reference to
     504             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     505             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     506             :  * derived classes).
     507             :  *
     508             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     509             :  *
     510             :  * This method is the same as the C function OGR_G_AssignSpatialReference().
     511             :  *
     512             :  * @param poSR new spatial reference system to apply.
     513             :  */
     514             : 
     515     5474460 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
     516             : 
     517             : {
     518             :     // Do in that order to properly handle poSR == poSRS
     519     5474460 :     if (poSR != nullptr)
     520     3531150 :         const_cast<OGRSpatialReference *>(poSR)->Reference();
     521     5474460 :     if (poSRS != nullptr)
     522       28740 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     523             : 
     524     5474460 :     poSRS = poSR;
     525     5474460 : }
     526             : 
     527             : /************************************************************************/
     528             : /*                    OGR_G_AssignSpatialReference()                    */
     529             : /************************************************************************/
     530             : /**
     531             :  * \brief Assign spatial reference to this object.
     532             :  *
     533             :  * Any existing spatial reference
     534             :  * is replaced, but under no circumstances does this result in the object
     535             :  * being reprojected.  It is just changing the interpretation of the existing
     536             :  * geometry.  Note that assigning a spatial reference increments the
     537             :  * reference count on the OGRSpatialReference, but does not copy it.
     538             :  *
     539             :  * Starting with GDAL 2.3, this will also assign the spatial reference to
     540             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     541             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     542             :  * derived classes).
     543             :  *
     544             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     545             :  *
     546             :  * This function is the same as the CPP method
     547             :  * OGRGeometry::assignSpatialReference.
     548             :  *
     549             :  * @param hGeom handle on the geometry to apply the new spatial reference
     550             :  * system.
     551             :  * @param hSRS handle on the new spatial reference system to apply.
     552             :  */
     553             : 
     554          74 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     555             : 
     556             : {
     557          74 :     VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
     558             : 
     559         148 :     OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
     560          74 :         OGRSpatialReference::FromHandle(hSRS));
     561             : }
     562             : 
     563             : /************************************************************************/
     564             : /*                             Intersects()                             */
     565             : /************************************************************************/
     566             : 
     567             : /**
     568             :  * \brief Do these features intersect?
     569             :  *
     570             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     571             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     572             :  * envelopes (bounding boxes) of the two geometries overlap.
     573             :  *
     574             :  * The poOtherGeom argument may be safely NULL, but in this case the method
     575             :  * will always return TRUE.   That is, a NULL geometry is treated as being
     576             :  * everywhere.
     577             :  *
     578             :  * This method is the same as the C function OGR_G_Intersects().
     579             :  *
     580             :  * @param poOtherGeom the other geometry to test against.
     581             :  *
     582             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     583             :  */
     584             : 
     585          35 : OGRBoolean OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
     586             : 
     587             : {
     588          35 :     if (poOtherGeom == nullptr)
     589           0 :         return TRUE;
     590             : 
     591          35 :     OGREnvelope oEnv1;
     592          35 :     getEnvelope(&oEnv1);
     593             : 
     594          35 :     OGREnvelope oEnv2;
     595          35 :     poOtherGeom->getEnvelope(&oEnv2);
     596             : 
     597          35 :     if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
     598          19 :         oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
     599          16 :         return FALSE;
     600             : 
     601             : #ifndef HAVE_GEOS
     602             :     // Without GEOS we assume that envelope overlap is equivalent to
     603             :     // actual intersection.
     604             :     return TRUE;
     605             : #else
     606             : 
     607          19 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
     608          19 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
     609          19 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
     610             : 
     611          19 :     OGRBoolean bResult = FALSE;
     612          19 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
     613             :     {
     614          19 :         bResult =
     615          19 :             GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) != 0;
     616             :     }
     617             : 
     618          19 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
     619          19 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
     620          19 :     freeGEOSContext(hGEOSCtxt);
     621             : 
     622          19 :     return bResult;
     623             : #endif  // HAVE_GEOS
     624             : }
     625             : 
     626             : // Old API compatibility function.
     627             : 
     628             : //! @cond Doxygen_Suppress
     629           0 : OGRBoolean OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
     630             : 
     631             : {
     632           0 :     return Intersects(poOtherGeom);
     633             : }
     634             : 
     635             : //! @endcond
     636             : 
     637             : /************************************************************************/
     638             : /*                          OGR_G_Intersects()                          */
     639             : /************************************************************************/
     640             : /**
     641             :  * \brief Do these features intersect?
     642             :  *
     643             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     644             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     645             :  * envelopes (bounding boxes) of the two geometries overlap.
     646             :  *
     647             :  * This function is the same as the CPP method OGRGeometry::Intersects.
     648             :  *
     649             :  * @param hGeom handle on the first geometry.
     650             :  * @param hOtherGeom handle on the other geometry to test against.
     651             :  *
     652             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     653             :  */
     654             : 
     655          11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     656             : 
     657             : {
     658          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
     659          11 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
     660             : 
     661          22 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     662          11 :         OGRGeometry::FromHandle(hOtherGeom));
     663             : }
     664             : 
     665             : //! @cond Doxygen_Suppress
     666           0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     667             : 
     668             : {
     669           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
     670           0 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
     671             : 
     672           0 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     673           0 :         OGRGeometry::FromHandle(hOtherGeom));
     674             : }
     675             : 
     676             : //! @endcond
     677             : 
     678             : /************************************************************************/
     679             : /*                            transformTo()                             */
     680             : /************************************************************************/
     681             : 
     682             : /**
     683             :  * \brief Transform geometry to new spatial reference system.
     684             :  *
     685             :  * This method will transform the coordinates of a geometry from
     686             :  * their current spatial reference system to a new target spatial
     687             :  * reference system.  Normally this means reprojecting the vectors,
     688             :  * but it could include datum shifts, and changes of units.
     689             :  *
     690             :  * This method will only work if the geometry already has an assigned
     691             :  * spatial reference system, and if it is transformable to the target
     692             :  * coordinate system.
     693             :  *
     694             :  * Because this method requires internal creation and initialization of an
     695             :  * OGRCoordinateTransformation object it is significantly more expensive to
     696             :  * use this method to transform many geometries than it is to create the
     697             :  * OGRCoordinateTransformation in advance, and call transform() with that
     698             :  * transformation.  This method exists primarily for convenience when only
     699             :  * transforming a single geometry.
     700             :  *
     701             :  * This method is the same as the C function OGR_G_TransformTo().
     702             :  *
     703             :  * @param poSR spatial reference system to transform to.
     704             :  *
     705             :  * @return OGRERR_NONE on success, or an error code.
     706             :  */
     707             : 
     708          27 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
     709             : 
     710             : {
     711          27 :     if (getSpatialReference() == nullptr)
     712             :     {
     713           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
     714           1 :         return OGRERR_FAILURE;
     715             :     }
     716             : 
     717          26 :     if (poSR == nullptr)
     718             :     {
     719           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
     720           0 :         return OGRERR_FAILURE;
     721             :     }
     722             : 
     723             :     OGRCoordinateTransformation *poCT =
     724          26 :         OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
     725          26 :     if (poCT == nullptr)
     726           0 :         return OGRERR_FAILURE;
     727             : 
     728          26 :     const OGRErr eErr = transform(poCT);
     729             : 
     730          26 :     delete poCT;
     731             : 
     732          26 :     return eErr;
     733             : }
     734             : 
     735             : /************************************************************************/
     736             : /*                         OGR_G_TransformTo()                          */
     737             : /************************************************************************/
     738             : /**
     739             :  * \brief Transform geometry to new spatial reference system.
     740             :  *
     741             :  * This function will transform the coordinates of a geometry from
     742             :  * their current spatial reference system to a new target spatial
     743             :  * reference system.  Normally this means reprojecting the vectors,
     744             :  * but it could include datum shifts, and changes of units.
     745             :  *
     746             :  * This function will only work if the geometry already has an assigned
     747             :  * spatial reference system, and if it is transformable to the target
     748             :  * coordinate system.
     749             :  *
     750             :  * Because this function requires internal creation and initialization of an
     751             :  * OGRCoordinateTransformation object it is significantly more expensive to
     752             :  * use this function to transform many geometries than it is to create the
     753             :  * OGRCoordinateTransformation in advance, and call transform() with that
     754             :  * transformation.  This function exists primarily for convenience when only
     755             :  * transforming a single geometry.
     756             :  *
     757             :  * This function is the same as the CPP method OGRGeometry::transformTo.
     758             :  *
     759             :  * @param hGeom handle on the geometry to apply the transform to.
     760             :  * @param hSRS handle on the spatial reference system to apply.
     761             :  *
     762             :  * @return OGRERR_NONE on success, or an error code.
     763             :  */
     764             : 
     765           9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     766             : 
     767             : {
     768           9 :     VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
     769             : 
     770          18 :     return OGRGeometry::FromHandle(hGeom)->transformTo(
     771          18 :         OGRSpatialReference::FromHandle(hSRS));
     772             : }
     773             : 
     774             : /**
     775             :  * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
     776             :  *
     777             :  * \brief Apply arbitrary coordinate transformation to geometry.
     778             :  *
     779             :  * This method will transform the coordinates of a geometry from
     780             :  * their current spatial reference system to a new target spatial
     781             :  * reference system.  Normally this means reprojecting the vectors,
     782             :  * but it could include datum shifts, and changes of units.
     783             :  *
     784             :  * Note that this method does not require that the geometry already
     785             :  * have a spatial reference system.  It will be assumed that they can
     786             :  * be treated as having the source spatial reference system of the
     787             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     788             :  * will be ignored.  On successful completion the output OGRSpatialReference
     789             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     790             :  *
     791             :  * This method only does reprojection on a point-by-point basis. It does not
     792             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     793             :  * For that, use the OGRGeometryFactory::transformWithOptions() method.
     794             :  *
     795             :  * This method is the same as the C function OGR_G_Transform().
     796             :  *
     797             :  * @param poCT the transformation to apply.
     798             :  *
     799             :  * @return OGRERR_NONE on success or an error code.
     800             :  */
     801             : 
     802             : /************************************************************************/
     803             : /*                          OGR_G_Transform()                           */
     804             : /************************************************************************/
     805             : /**
     806             :  * \brief Apply arbitrary coordinate transformation to geometry.
     807             :  *
     808             :  * This function will transform the coordinates of a geometry from
     809             :  * their current spatial reference system to a new target spatial
     810             :  * reference system.  Normally this means reprojecting the vectors,
     811             :  * but it could include datum shifts, and changes of units.
     812             :  *
     813             :  * Note that this function does not require that the geometry already
     814             :  * have a spatial reference system.  It will be assumed that they can
     815             :  * be treated as having the source spatial reference system of the
     816             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     817             :  * will be ignored.  On successful completion the output OGRSpatialReference
     818             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     819             :  *
     820             :  * This function only does reprojection on a point-by-point basis. It does not
     821             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     822             :  * For that, use the OGR_GeomTransformer_Create() and
     823             :  * OGR_GeomTransformer_Transform() functions.
     824             :  *
     825             :  * This function is the same as the CPP method OGRGeometry::transform.
     826             :  *
     827             :  * @param hGeom handle on the geometry to apply the transform to.
     828             :  * @param hTransform handle on the transformation to apply.
     829             :  *
     830             :  * @return OGRERR_NONE on success or an error code.
     831             :  */
     832             : 
     833          17 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
     834             :                        OGRCoordinateTransformationH hTransform)
     835             : 
     836             : {
     837          17 :     VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
     838             : 
     839          34 :     return OGRGeometry::FromHandle(hGeom)->transform(
     840          17 :         OGRCoordinateTransformation::FromHandle(hTransform));
     841             : }
     842             : 
     843             : /**
     844             :  * \fn int OGRGeometry::getDimension() const;
     845             :  *
     846             :  * \brief Get the dimension of this object.
     847             :  *
     848             :  * This method corresponds to the SFCOM IGeometry::GetDimension() method.
     849             :  * It indicates the dimension of the object, but does not indicate the
     850             :  * dimension of the underlying space (as indicated by
     851             :  * OGRGeometry::getCoordinateDimension()).
     852             :  *
     853             :  * This method is the same as the C function OGR_G_GetDimension().
     854             :  *
     855             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     856             :  */
     857             : 
     858             : /**
     859             :  * \brief Get the geometry type that conforms with ISO SQL/MM Part3
     860             :  *
     861             :  * @return the geometry type that conforms with ISO SQL/MM Part3
     862             :  */
     863      692980 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
     864             : {
     865      692980 :     OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
     866             : 
     867      692906 :     if (flags & OGR_G_3D)
     868      212663 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
     869      692906 :     if (flags & OGR_G_MEASURED)
     870       26023 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
     871             : 
     872      692906 :     return nGType;
     873             : }
     874             : 
     875             : /************************************************************************/
     876             : /*                  OGRGeometry::segmentize()                           */
     877             : /************************************************************************/
     878             : /**
     879             :  *
     880             :  * \brief Modify the geometry such it has no segment longer then the
     881             :  * given distance.
     882             :  *
     883             :  * This method modifies the geometry to add intermediate vertices if necessary
     884             :  * so that the maximum length between 2 consecutive vertices is lower than
     885             :  * dfMaxLength.
     886             :  *
     887             :  * Interpolated points will have Z and M values (if needed) set to 0.
     888             :  * Distance computation is performed in 2d only
     889             :  *
     890             :  * This function is the same as the C function OGR_G_Segmentize()
     891             :  *
     892             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     893             :  * @return (since 3.10) true in case of success, false in case of error.
     894             :  */
     895             : 
     896           0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
     897             : {
     898             :     // Do nothing.
     899           0 :     return true;
     900             : }
     901             : 
     902             : /************************************************************************/
     903             : /*                         OGR_G_Segmentize()                           */
     904             : /************************************************************************/
     905             : 
     906             : /**
     907             :  *
     908             :  * \brief Modify the geometry such it has no segment longer then the given
     909             :  * distance.
     910             :  *
     911             :  * Interpolated points will have Z and M values (if needed) set to 0.
     912             :  * Distance computation is performed in 2d only.
     913             :  *
     914             :  * This function is the same as the CPP method OGRGeometry::segmentize().
     915             :  *
     916             :  * @param hGeom handle on the geometry to segmentize
     917             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     918             :  */
     919             : 
     920          21 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
     921             : {
     922          21 :     VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
     923             : 
     924          21 :     if (dfMaxLength <= 0)
     925             :     {
     926           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     927             :                  "dfMaxLength must be strictly positive");
     928           0 :         return;
     929             :     }
     930          21 :     OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
     931             : }
     932             : 
     933             : /************************************************************************/
     934             : /*                         OGR_G_GetDimension()                         */
     935             : /************************************************************************/
     936             : /**
     937             :  *
     938             :  * \brief Get the dimension of this geometry.
     939             :  *
     940             :  * This function corresponds to the SFCOM IGeometry::GetDimension() method.
     941             :  * It indicates the dimension of the geometry, but does not indicate the
     942             :  * dimension of the underlying space (as indicated by
     943             :  * OGR_G_GetCoordinateDimension() function).
     944             :  *
     945             :  * This function is the same as the CPP method OGRGeometry::getDimension().
     946             :  *
     947             :  * @param hGeom handle on the geometry to get the dimension from.
     948             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     949             :  */
     950             : 
     951          21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
     952             : 
     953             : {
     954          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
     955             : 
     956          21 :     return OGRGeometry::FromHandle(hGeom)->getDimension();
     957             : }
     958             : 
     959             : /************************************************************************/
     960             : /*                       getCoordinateDimension()                       */
     961             : /************************************************************************/
     962             : /**
     963             :  * \brief Get the dimension of the coordinates in this object.
     964             :  *
     965             :  * This method is the same as the C function OGR_G_GetCoordinateDimension().
     966             :  *
     967             :  * @deprecated use CoordinateDimension().
     968             :  *
     969             :  * @return this will return 2 or 3.
     970             :  */
     971             : 
     972     1019800 : int OGRGeometry::getCoordinateDimension() const
     973             : 
     974             : {
     975     1019800 :     return (flags & OGR_G_3D) ? 3 : 2;
     976             : }
     977             : 
     978             : /************************************************************************/
     979             : /*                        CoordinateDimension()                         */
     980             : /************************************************************************/
     981             : /**
     982             :  * \brief Get the dimension of the coordinates in this object.
     983             :  *
     984             :  * This method is the same as the C function OGR_G_CoordinateDimension().
     985             :  *
     986             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
     987             :  *
     988             :  * @since GDAL 2.1
     989             :  */
     990             : 
     991      100581 : int OGRGeometry::CoordinateDimension() const
     992             : 
     993             : {
     994      100581 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     995        7297 :         return 4;
     996       93284 :     else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
     997        5932 :         return 3;
     998             :     else
     999       87352 :         return 2;
    1000             : }
    1001             : 
    1002             : /************************************************************************/
    1003             : /*                    OGR_G_GetCoordinateDimension()                    */
    1004             : /************************************************************************/
    1005             : /**
    1006             :  *
    1007             :  * \brief Get the dimension of the coordinates in this geometry.
    1008             :  *
    1009             :  * This function is the same as the CPP method
    1010             :  * OGRGeometry::getCoordinateDimension().
    1011             :  *
    1012             :  * @param hGeom handle on the geometry to get the dimension of the
    1013             :  * coordinates from.
    1014             :  *
    1015             :  * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
    1016             :  * OGR_G_IsMeasured().
    1017             :  *
    1018             :  * @return this will return 2 or 3.
    1019             :  */
    1020             : 
    1021         706 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
    1022             : 
    1023             : {
    1024         706 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
    1025             : 
    1026         706 :     return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
    1027             : }
    1028             : 
    1029             : /************************************************************************/
    1030             : /*                    OGR_G_CoordinateDimension()                       */
    1031             : /************************************************************************/
    1032             : /**
    1033             :  *
    1034             :  * \brief Get the dimension of the coordinates in this geometry.
    1035             :  *
    1036             :  * This function is the same as the CPP method
    1037             :  * OGRGeometry::CoordinateDimension().
    1038             :  *
    1039             :  * @param hGeom handle on the geometry to get the dimension of the
    1040             :  * coordinates from.
    1041             :  *
    1042             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
    1043             :  *
    1044             :  * @since GDAL 2.1
    1045             :  */
    1046             : 
    1047           4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
    1048             : 
    1049             : {
    1050           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
    1051             : 
    1052           4 :     return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
    1053             : }
    1054             : 
    1055             : /**
    1056             :  *
    1057             :  * \brief See whether this geometry has Z coordinates.
    1058             :  *
    1059             :  * This function is the same as the CPP method
    1060             :  * OGRGeometry::Is3D().
    1061             :  *
    1062             :  * @param hGeom handle on the geometry to check whether it has Z coordinates.
    1063             :  *
    1064             :  * @return TRUE if the geometry has Z coordinates.
    1065             :  * @since GDAL 2.1
    1066             :  */
    1067             : 
    1068       35163 : int OGR_G_Is3D(OGRGeometryH hGeom)
    1069             : 
    1070             : {
    1071       35163 :     VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
    1072             : 
    1073       35163 :     return OGRGeometry::FromHandle(hGeom)->Is3D();
    1074             : }
    1075             : 
    1076             : /**
    1077             :  *
    1078             :  * \brief See whether this geometry is measured.
    1079             :  *
    1080             :  * This function is the same as the CPP method
    1081             :  * OGRGeometry::IsMeasured().
    1082             :  *
    1083             :  * @param hGeom handle on the geometry to check whether it is measured.
    1084             :  *
    1085             :  * @return TRUE if the geometry has M coordinates.
    1086             :  * @since GDAL 2.1
    1087             :  */
    1088             : 
    1089       38044 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
    1090             : 
    1091             : {
    1092       38044 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
    1093             : 
    1094       38044 :     return OGRGeometry::FromHandle(hGeom)->IsMeasured();
    1095             : }
    1096             : 
    1097             : /************************************************************************/
    1098             : /*                       setCoordinateDimension()                       */
    1099             : /************************************************************************/
    1100             : 
    1101             : /**
    1102             :  * \brief Set the coordinate dimension.
    1103             :  *
    1104             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1105             :  * dimension of a geometry to 2 should zero out any existing Z values.  Setting
    1106             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1107             :  * will affect the children geometries.
    1108             :  * This will also remove the M dimension if present before this call.
    1109             :  *
    1110             :  * @deprecated use set3D() or setMeasured().
    1111             :  *
    1112             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1113             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1114             :  */
    1115             : 
    1116       65155 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
    1117             : 
    1118             : {
    1119       65155 :     if (nNewDimension == 2)
    1120       64667 :         flags &= ~OGR_G_3D;
    1121             :     else
    1122         488 :         flags |= OGR_G_3D;
    1123       65155 :     return setMeasured(FALSE);
    1124             : }
    1125             : 
    1126             : /**
    1127             :  * \brief Add or remove the Z coordinate dimension.
    1128             :  *
    1129             :  * This method adds or removes the explicit Z coordinate dimension.
    1130             :  * Removing the Z coordinate dimension of a geometry will remove any
    1131             :  * existing Z values.  Adding the Z dimension to a geometry
    1132             :  * collection, a compound curve, a polygon, etc.  will affect the
    1133             :  * children geometries.
    1134             :  *
    1135             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1136             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1137             :  * @since GDAL 2.1
    1138             :  */
    1139             : 
    1140     1581470 : bool OGRGeometry::set3D(OGRBoolean bIs3D)
    1141             : 
    1142             : {
    1143     1581470 :     if (bIs3D)
    1144     1576380 :         flags |= OGR_G_3D;
    1145             :     else
    1146        5090 :         flags &= ~OGR_G_3D;
    1147     1581470 :     return true;
    1148             : }
    1149             : 
    1150             : /**
    1151             :  * \brief Add or remove the M coordinate dimension.
    1152             :  *
    1153             :  * This method adds or removes the explicit M coordinate dimension.
    1154             :  * Removing the M coordinate dimension of a geometry will remove any
    1155             :  * existing M values.  Adding the M dimension to a geometry
    1156             :  * collection, a compound curve, a polygon, etc.  will affect the
    1157             :  * children geometries.
    1158             :  *
    1159             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1160             :  * TRUE or FALSE.
    1161             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1162             :  * @since GDAL 2.1
    1163             :  */
    1164             : 
    1165      409557 : bool OGRGeometry::setMeasured(OGRBoolean bIsMeasured)
    1166             : 
    1167             : {
    1168      409557 :     if (bIsMeasured)
    1169      137610 :         flags |= OGR_G_MEASURED;
    1170             :     else
    1171      271947 :         flags &= ~OGR_G_MEASURED;
    1172      409557 :     return true;
    1173             : }
    1174             : 
    1175             : /************************************************************************/
    1176             : /*                    OGR_G_SetCoordinateDimension()                    */
    1177             : /************************************************************************/
    1178             : 
    1179             : /**
    1180             :  * \brief Set the coordinate dimension.
    1181             :  *
    1182             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1183             :  * dimension of a geometry to 2 should zero out any existing Z values. Setting
    1184             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1185             :  * will affect the children geometries.
    1186             :  * This will also remove the M dimension if present before this call.
    1187             :  *
    1188             :  * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
    1189             :  *
    1190             :  * @param hGeom handle on the geometry to set the dimension of the
    1191             :  * coordinates.
    1192             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1193             :  */
    1194             : 
    1195          56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
    1196             : 
    1197             : {
    1198          56 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
    1199             : 
    1200          56 :     OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
    1201             : }
    1202             : 
    1203             : /************************************************************************/
    1204             : /*                    OGR_G_Set3D()                                     */
    1205             : /************************************************************************/
    1206             : 
    1207             : /**
    1208             :  * \brief Add or remove the Z coordinate dimension.
    1209             :  *
    1210             :  * This method adds or removes the explicit Z coordinate dimension.
    1211             :  * Removing the Z coordinate dimension of a geometry will remove any
    1212             :  * existing Z values.  Adding the Z dimension to a geometry
    1213             :  * collection, a compound curve, a polygon, etc.  will affect the
    1214             :  * children geometries.
    1215             :  *
    1216             :  * @param hGeom handle on the geometry to set or unset the Z dimension.
    1217             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1218             :  * @since GDAL 2.1
    1219             :  */
    1220             : 
    1221         154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
    1222             : 
    1223             : {
    1224         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
    1225             : 
    1226         154 :     OGRGeometry::FromHandle(hGeom)->set3D(bIs3D);
    1227             : }
    1228             : 
    1229             : /************************************************************************/
    1230             : /*                    OGR_G_SetMeasured()                               */
    1231             : /************************************************************************/
    1232             : 
    1233             : /**
    1234             :  * \brief Add or remove the M coordinate dimension.
    1235             :  *
    1236             :  * This method adds or removes the explicit M coordinate dimension.
    1237             :  * Removing the M coordinate dimension of a geometry will remove any
    1238             :  * existing M values.  Adding the M dimension to a geometry
    1239             :  * collection, a compound curve, a polygon, etc.  will affect the
    1240             :  * children geometries.
    1241             :  *
    1242             :  * @param hGeom handle on the geometry to set or unset the M dimension.
    1243             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1244             :  * TRUE or FALSE.
    1245             :  * @since GDAL 2.1
    1246             :  */
    1247             : 
    1248         154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
    1249             : 
    1250             : {
    1251         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
    1252             : 
    1253         154 :     OGRGeometry::FromHandle(hGeom)->setMeasured(bIsMeasured);
    1254             : }
    1255             : 
    1256             : /**
    1257             :  * \fn int OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
    1258             :  *
    1259             :  * \brief Returns TRUE if two geometries are equivalent.
    1260             :  *
    1261             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1262             :  *
    1263             :  * The comparison is done in a structural way, that is to say that the geometry
    1264             :  * types must be identical, as well as the number and ordering of sub-geometries
    1265             :  * and vertices.
    1266             :  * Or equivalently, two geometries are considered equal by this method if their
    1267             :  * WKT/WKB representation is equal.
    1268             :  * Note: this must be distinguished for equality in a spatial way (which is
    1269             :  * the purpose of the ST_Equals() operation).
    1270             :  *
    1271             :  * This method is the same as the C function OGR_G_Equals().
    1272             :  *
    1273             :  * @return TRUE if equivalent or FALSE otherwise.
    1274             :  */
    1275             : 
    1276             : // Backward compatibility method.
    1277             : 
    1278             : //! @cond Doxygen_Suppress
    1279           0 : int OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
    1280             : {
    1281           0 :     return Equals(poOtherGeom);
    1282             : }
    1283             : 
    1284             : //! @endcond
    1285             : 
    1286             : /************************************************************************/
    1287             : /*                            OGR_G_Equals()                            */
    1288             : /************************************************************************/
    1289             : 
    1290             : /**
    1291             :  * \brief Returns TRUE if two geometries are equivalent.
    1292             :  *
    1293             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1294             :  *
    1295             :  * The comparison is done in a structural way, that is to say that the geometry
    1296             :  * types must be identical, as well as the number and ordering of sub-geometries
    1297             :  * and vertices.
    1298             :  * Or equivalently, two geometries are considered equal by this method if their
    1299             :  * WKT/WKB representation is equal.
    1300             :  * Note: this must be distinguished for equality in a spatial way (which is
    1301             :  * the purpose of the ST_Equals() operation).
    1302             :  *
    1303             :  * This function is the same as the CPP method OGRGeometry::Equals() method.
    1304             :  *
    1305             :  * @param hGeom handle on the first geometry.
    1306             :  * @param hOther handle on the other geometry to test against.
    1307             :  * @return TRUE if equivalent or FALSE otherwise.
    1308             :  */
    1309             : 
    1310       28188 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
    1311             : 
    1312             : {
    1313       28188 :     VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
    1314             : 
    1315       28188 :     if (hOther == nullptr)
    1316             :     {
    1317           0 :         CPLError(CE_Failure, CPLE_ObjectNull,
    1318             :                  "hOther was NULL in OGR_G_Equals");
    1319           0 :         return 0;
    1320             :     }
    1321             : 
    1322       56376 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1323       28188 :         OGRGeometry::FromHandle(hOther));
    1324             : }
    1325             : 
    1326             : //! @cond Doxygen_Suppress
    1327           0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
    1328             : 
    1329             : {
    1330           0 :     if (hGeom == nullptr)
    1331             :     {
    1332           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
    1333           0 :         return 0;
    1334             :     }
    1335             : 
    1336           0 :     if (hOther == nullptr)
    1337             :     {
    1338           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
    1339           0 :         return 0;
    1340             :     }
    1341             : 
    1342           0 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1343           0 :         OGRGeometry::FromHandle(hOther));
    1344             : }
    1345             : 
    1346             : //! @endcond
    1347             : 
    1348             : /**
    1349             :  * \fn int OGRGeometry::WkbSize() const;
    1350             :  *
    1351             :  * \brief Returns size of related binary representation.
    1352             :  *
    1353             :  * This method returns the exact number of bytes required to hold the
    1354             :  * well known binary representation of this geometry object.  Its computation
    1355             :  * may be slightly expensive for complex geometries.
    1356             :  *
    1357             :  * This method relates to the SFCOM IWks::WkbSize() method.
    1358             :  *
    1359             :  * This method is the same as the C function OGR_G_WkbSize().
    1360             :  *
    1361             :  * @return size of binary representation in bytes.
    1362             :  */
    1363             : 
    1364             : /************************************************************************/
    1365             : /*                           OGR_G_WkbSize()                            */
    1366             : /************************************************************************/
    1367             : /**
    1368             :  * \brief Returns size of related binary representation.
    1369             :  *
    1370             :  * This function returns the exact number of bytes required to hold the
    1371             :  * well known binary representation of this geometry object.  Its computation
    1372             :  * may be slightly expensive for complex geometries.
    1373             :  *
    1374             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1375             :  *
    1376             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1377             :  *
    1378             :  * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
    1379             :  *
    1380             :  * @param hGeom handle on the geometry to get the binary size from.
    1381             :  * @return size of binary representation in bytes.
    1382             :  */
    1383             : 
    1384           1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
    1385             : 
    1386             : {
    1387           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
    1388             : 
    1389           1 :     const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
    1390           1 :     if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
    1391             :     {
    1392           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1393             :                  "OGR_G_WkbSize() would return a value beyond int range. "
    1394             :                  "Use OGR_G_WkbSizeEx() instead");
    1395           0 :         return 0;
    1396             :     }
    1397           1 :     return static_cast<int>(nSize);
    1398             : }
    1399             : 
    1400             : /************************************************************************/
    1401             : /*                         OGR_G_WkbSizeEx()                            */
    1402             : /************************************************************************/
    1403             : /**
    1404             :  * \brief Returns size of related binary representation.
    1405             :  *
    1406             :  * This function returns the exact number of bytes required to hold the
    1407             :  * well known binary representation of this geometry object.  Its computation
    1408             :  * may be slightly expensive for complex geometries.
    1409             :  *
    1410             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1411             :  *
    1412             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1413             :  *
    1414             :  * @param hGeom handle on the geometry to get the binary size from.
    1415             :  * @return size of binary representation in bytes.
    1416             :  * @since GDAL 3.3
    1417             :  */
    1418             : 
    1419       10675 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
    1420             : 
    1421             : {
    1422       10675 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
    1423             : 
    1424       10675 :     return OGRGeometry::FromHandle(hGeom)->WkbSize();
    1425             : }
    1426             : 
    1427             : /**
    1428             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
    1429             :  *
    1430             :  * \brief Computes and returns the bounding envelope for this geometry
    1431             :  * in the passed psEnvelope structure.
    1432             :  *
    1433             :  * This method is the same as the C function OGR_G_GetEnvelope().
    1434             :  *
    1435             :  * @param psEnvelope the structure in which to place the results.
    1436             :  */
    1437             : 
    1438             : /************************************************************************/
    1439             : /*                         OGR_G_GetEnvelope()                          */
    1440             : /************************************************************************/
    1441             : /**
    1442             :  * \brief Computes and returns the bounding envelope for this geometry
    1443             :  * in the passed psEnvelope structure.
    1444             :  *
    1445             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1446             :  *
    1447             :  * @param hGeom handle of the geometry to get envelope from.
    1448             :  * @param psEnvelope the structure in which to place the results.
    1449             :  */
    1450             : 
    1451       13344 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
    1452             : 
    1453             : {
    1454       13344 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
    1455             : 
    1456       13344 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1457             : }
    1458             : 
    1459             : /**
    1460             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
    1461             :  *
    1462             :  * \brief Computes and returns the bounding envelope (3D) for this
    1463             :  * geometry in the passed psEnvelope structure.
    1464             :  *
    1465             :  * This method is the same as the C function OGR_G_GetEnvelope3D().
    1466             :  *
    1467             :  * @param psEnvelope the structure in which to place the results.
    1468             :  *
    1469             :  * @since OGR 1.9.0
    1470             :  */
    1471             : 
    1472             : /************************************************************************/
    1473             : /*                        OGR_G_GetEnvelope3D()                         */
    1474             : /************************************************************************/
    1475             : /**
    1476             :  * \brief Computes and returns the bounding envelope (3D) for this
    1477             :  * geometry in the passed psEnvelope structure.
    1478             :  *
    1479             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1480             :  *
    1481             :  * @param hGeom handle of the geometry to get envelope from.
    1482             :  * @param psEnvelope the structure in which to place the results.
    1483             :  *
    1484             :  * @since OGR 1.9.0
    1485             :  */
    1486             : 
    1487          10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
    1488             : 
    1489             : {
    1490          10 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
    1491             : 
    1492          10 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1493             : }
    1494             : 
    1495             : /************************************************************************/
    1496             : /*                        importFromWkb()                               */
    1497             : /************************************************************************/
    1498             : 
    1499             : /**
    1500             :  * \brief Assign geometry from well known binary data.
    1501             :  *
    1502             :  * The object must have already been instantiated as the correct derived
    1503             :  * type of geometry object to match the binaries type.  This method is used
    1504             :  * by the OGRGeometryFactory class, but not normally called by application
    1505             :  * code.
    1506             :  *
    1507             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1508             :  *
    1509             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1510             :  *
    1511             :  * @param pabyData the binary input data.
    1512             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1513             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1514             :  * done for curve geometries code
    1515             :  *
    1516             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1517             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1518             :  * OGRERR_CORRUPT_DATA may be returned.
    1519             :  */
    1520             : 
    1521         492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
    1522             :                                   OGRwkbVariant eWkbVariant)
    1523             : {
    1524         492 :     size_t nBytesConsumedOutIgnored = 0;
    1525         492 :     return importFromWkb(pabyData, nSize, eWkbVariant,
    1526         984 :                          nBytesConsumedOutIgnored);
    1527             : }
    1528             : 
    1529             : /**
    1530             :  * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
    1531             :  * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
    1532             :  *
    1533             :  * \brief Assign geometry from well known binary data.
    1534             :  *
    1535             :  * The object must have already been instantiated as the correct derived
    1536             :  * type of geometry object to match the binaries type.  This method is used
    1537             :  * by the OGRGeometryFactory class, but not normally called by application
    1538             :  * code.
    1539             :  *
    1540             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1541             :  *
    1542             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1543             :  *
    1544             :  * @param pabyData the binary input data.
    1545             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1546             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1547             :  * done for curve geometries code
    1548             :  * @param nBytesConsumedOut output parameter. Number of bytes consumed.
    1549             :  *
    1550             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1551             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1552             :  * OGRERR_CORRUPT_DATA may be returned.
    1553             :  *
    1554             :  * @since GDAL 2.3
    1555             :  */
    1556             : 
    1557             : /************************************************************************/
    1558             : /*                        OGR_G_ImportFromWkb()                         */
    1559             : /************************************************************************/
    1560             : /**
    1561             :  * \brief Assign geometry from well known binary data.
    1562             :  *
    1563             :  * The object must have already been instantiated as the correct derived
    1564             :  * type of geometry object to match the binaries type.
    1565             :  *
    1566             :  * This function relates to the SFCOM IWks::ImportFromWKB() method.
    1567             :  *
    1568             :  * This function is the same as the CPP method OGRGeometry::importFromWkb().
    1569             :  *
    1570             :  * @param hGeom handle on the geometry to assign the well know binary data to.
    1571             :  * @param pabyData the binary input data.
    1572             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1573             :  *
    1574             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1575             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1576             :  * OGRERR_CORRUPT_DATA may be returned.
    1577             :  */
    1578             : 
    1579           0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
    1580             : 
    1581             : {
    1582           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
    1583             : 
    1584           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkb(
    1585           0 :         static_cast<const GByte *>(pabyData), nSize);
    1586             : }
    1587             : 
    1588             : /************************************************************************/
    1589             : /*                       OGRGeometry::exportToWkb()                     */
    1590             : /************************************************************************/
    1591             : 
    1592             : /* clang-format off */
    1593             : /**
    1594             :  * \brief Convert a geometry into well known binary format.
    1595             :  *
    1596             :  * This method relates to the SFCOM IWks::ExportToWKB() method.
    1597             :  *
    1598             :  * This method is the same as the C function OGR_G_ExportToWkb() or
    1599             :  * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
    1600             :  *
    1601             :  * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1602             :  *               respectively.
    1603             :  * @param pabyData a buffer into which the binary representation is
    1604             :  *                      written.  This buffer must be at least
    1605             :  *                      OGRGeometry::WkbSize() byte in size.
    1606             :  * @param eWkbVariant What standard to use when exporting geometries
    1607             :  *                      with three dimensions (or more). The default
    1608             :  *                      wkbVariantOldOgc is the historical OGR
    1609             :  *                      variant. wkbVariantIso is the variant defined
    1610             :  *                      in ISO SQL/MM and adopted by OGC for SFSQL
    1611             :  *                      1.2.
    1612             :  *
    1613             :  * @return Currently OGRERR_NONE is always returned.
    1614             :  */
    1615             : /* clang-format on */
    1616      259254 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
    1617             :                                 unsigned char *pabyData,
    1618             :                                 OGRwkbVariant eWkbVariant) const
    1619             : {
    1620      259254 :     OGRwkbExportOptions sOptions;
    1621      259254 :     sOptions.eByteOrder = eByteOrder;
    1622      259254 :     sOptions.eWkbVariant = eWkbVariant;
    1623      518433 :     return exportToWkb(pabyData, &sOptions);
    1624             : }
    1625             : 
    1626             : /************************************************************************/
    1627             : /*                         OGR_G_ExportToWkb()                          */
    1628             : /************************************************************************/
    1629             : /**
    1630             :  * \brief Convert a geometry well known binary format
    1631             :  *
    1632             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1633             :  *
    1634             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1635             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1636             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1637             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
    1638             :  *
    1639             :  * This function is the same as the CPP method
    1640             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
    1641             :  * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
    1642             :  *
    1643             :  * @param hGeom handle on the geometry to convert to a well know binary
    1644             :  * data from.
    1645             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1646             :  *               respectively.
    1647             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1648             :  *                      written.  This buffer must be at least
    1649             :  *                      OGR_G_WkbSize() byte in size.
    1650             :  *
    1651             :  * @return Currently OGRERR_NONE is always returned.
    1652             :  */
    1653             : 
    1654         105 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1655             :                          unsigned char *pabyDstBuffer)
    1656             : 
    1657             : {
    1658         105 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
    1659             : 
    1660         105 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
    1661             : }
    1662             : 
    1663             : /************************************************************************/
    1664             : /*                        OGR_G_ExportToIsoWkb()                        */
    1665             : /************************************************************************/
    1666             : /**
    1667             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
    1668             :  * binary format
    1669             :  *
    1670             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1671             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
    1672             :  * types.
    1673             :  *
    1674             :  * This function is the same as the CPP method
    1675             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
    1676             :  * with eWkbVariant = wkbVariantIso.
    1677             :  *
    1678             :  * @param hGeom handle on the geometry to convert to a well know binary
    1679             :  * data from.
    1680             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1681             :  *               respectively.
    1682             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1683             :  *                      written.  This buffer must be at least
    1684             :  *                      OGR_G_WkbSize() byte in size.
    1685             :  *
    1686             :  * @return Currently OGRERR_NONE is always returned.
    1687             :  *
    1688             :  * @since GDAL 2.0
    1689             :  */
    1690             : 
    1691       10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1692             :                             unsigned char *pabyDstBuffer)
    1693             : 
    1694             : {
    1695       10571 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
    1696             : 
    1697       10571 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
    1698       10571 :                                                        wkbVariantIso);
    1699             : }
    1700             : 
    1701             : /************************************************************************/
    1702             : /*                        OGR_G_ExportToWkbEx()                         */
    1703             : /************************************************************************/
    1704             : 
    1705             : /* clang-format off */
    1706             : /**
    1707             :  * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
    1708             :  *
    1709             :  * \brief Convert a geometry into well known binary format
    1710             :  *
    1711             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1712             :  *
    1713             :  * This function is the same as the C function OGR_G_ExportToWkbEx().
    1714             :  *
    1715             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1716             :  *                      written.  This buffer must be at least
    1717             :  *                      OGR_G_WkbSize() byte in size.
    1718             :  * @param psOptions WKB export options.
    1719             : 
    1720             :  * @return Currently OGRERR_NONE is always returned.
    1721             :  *
    1722             :  * @since GDAL 3.9
    1723             :  */
    1724             : /* clang-format on */
    1725             : 
    1726             : /**
    1727             :  * \brief Convert a geometry into well known binary format
    1728             :  *
    1729             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1730             :  *
    1731             :  * This function is the same as the CPP method
    1732             :  * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
    1733             :  *
    1734             :  * @param hGeom handle on the geometry to convert to a well know binary
    1735             :  * data from.
    1736             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1737             :  *                      written.  This buffer must be at least
    1738             :  *                      OGR_G_WkbSize() byte in size.
    1739             :  * @param psOptions WKB export options.
    1740             : 
    1741             :  * @return Currently OGRERR_NONE is always returned.
    1742             :  *
    1743             :  * @since GDAL 3.9
    1744             :  */
    1745             : 
    1746           2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
    1747             :                            const OGRwkbExportOptions *psOptions)
    1748             : {
    1749           2 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
    1750             : 
    1751           4 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
    1752           2 :                                                        psOptions);
    1753             : }
    1754             : 
    1755             : /**
    1756             :  * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
    1757             :  *
    1758             :  * \brief Assign geometry from well known text data.
    1759             :  *
    1760             :  * The object must have already been instantiated as the correct derived
    1761             :  * type of geometry object to match the text type.  This method is used
    1762             :  * by the OGRGeometryFactory class, but not normally called by application
    1763             :  * code.
    1764             :  *
    1765             :  * This method relates to the SFCOM IWks::ImportFromWKT() method.
    1766             :  *
    1767             :  * This method is the same as the C function OGR_G_ImportFromWkt().
    1768             :  *
    1769             :  * @param ppszInput pointer to a pointer to the source text.  The pointer is
    1770             :  *                    updated to pointer after the consumed text.
    1771             :  *
    1772             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1773             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1774             :  * OGRERR_CORRUPT_DATA may be returned.
    1775             :  */
    1776             : 
    1777             : /************************************************************************/
    1778             : /*                        OGR_G_ImportFromWkt()                         */
    1779             : /************************************************************************/
    1780             : /**
    1781             :  * \brief Assign geometry from well known text data.
    1782             :  *
    1783             :  * The object must have already been instantiated as the correct derived
    1784             :  * type of geometry object to match the text type.
    1785             :  *
    1786             :  * This function relates to the SFCOM IWks::ImportFromWKT() method.
    1787             :  *
    1788             :  * This function is the same as the CPP method OGRGeometry::importFromWkt().
    1789             :  *
    1790             :  * @param hGeom handle on the geometry to assign well know text data to.
    1791             :  * @param ppszSrcText pointer to a pointer to the source text.  The pointer is
    1792             :  *                    updated to pointer after the consumed text.
    1793             :  *
    1794             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1795             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1796             :  * OGRERR_CORRUPT_DATA may be returned.
    1797             :  */
    1798             : 
    1799           0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
    1800             : 
    1801             : {
    1802           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
    1803             : 
    1804           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkt(
    1805           0 :         const_cast<const char **>(ppszSrcText));
    1806             : }
    1807             : 
    1808             : /************************************************************************/
    1809             : /*                        importPreambleFromWkt()                      */
    1810             : /************************************************************************/
    1811             : 
    1812             : // Returns -1 if processing must continue.
    1813             : //! @cond Doxygen_Suppress
    1814      122682 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
    1815             :                                           int *pbHasM, bool *pbIsEmpty)
    1816             : {
    1817      122682 :     const char *pszInput = *ppszInput;
    1818             : 
    1819             :     /* -------------------------------------------------------------------- */
    1820             :     /*      Clear existing Geoms.                                           */
    1821             :     /* -------------------------------------------------------------------- */
    1822      122682 :     empty();
    1823      122682 :     *pbIsEmpty = false;
    1824             : 
    1825             :     /* -------------------------------------------------------------------- */
    1826             :     /*      Read and verify the type keyword, and ensure it matches the     */
    1827             :     /*      actual type of this container.                                  */
    1828             :     /* -------------------------------------------------------------------- */
    1829      122682 :     bool bHasM = false;
    1830      122682 :     bool bHasZ = false;
    1831      122682 :     bool bAlreadyGotDimension = false;
    1832             : 
    1833      122682 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
    1834      122682 :     pszInput = OGRWktReadToken(pszInput, szToken);
    1835      122682 :     if (szToken[0] != '\0')
    1836             :     {
    1837             :         // Postgis EWKT: POINTM instead of POINT M.
    1838             :         // Current QGIS versions (at least <= 3.38) also export POINTZ.
    1839      122682 :         const size_t nTokenLen = strlen(szToken);
    1840      122682 :         if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
    1841             :         {
    1842          11 :             szToken[nTokenLen - 1] = '\0';
    1843          11 :             bHasM = true;
    1844          11 :             bAlreadyGotDimension = true;
    1845             : 
    1846          11 :             if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
    1847           9 :                                   szToken[nTokenLen - 2] == 'z'))
    1848             :             {
    1849           4 :                 bHasZ = true;
    1850           4 :                 szToken[nTokenLen - 2] = '\0';
    1851             :             }
    1852             :         }
    1853      122671 :         else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
    1854             :         {
    1855           5 :             szToken[nTokenLen - 1] = '\0';
    1856           5 :             bHasZ = true;
    1857           5 :             bAlreadyGotDimension = true;
    1858             :         }
    1859             :     }
    1860             : 
    1861      122682 :     if (!EQUAL(szToken, getGeometryName()))
    1862           0 :         return OGRERR_CORRUPT_DATA;
    1863             : 
    1864             :     /* -------------------------------------------------------------------- */
    1865             :     /*      Check for Z, M or ZM                                            */
    1866             :     /* -------------------------------------------------------------------- */
    1867      122682 :     if (!bAlreadyGotDimension)
    1868             :     {
    1869      122666 :         const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1870      122666 :         if (EQUAL(szToken, "Z"))
    1871             :         {
    1872        1359 :             pszInput = pszNewInput;
    1873        1359 :             bHasZ = true;
    1874             :         }
    1875      121307 :         else if (EQUAL(szToken, "M"))
    1876             :         {
    1877         321 :             pszInput = pszNewInput;
    1878         321 :             bHasM = true;
    1879             :         }
    1880      120986 :         else if (EQUAL(szToken, "ZM"))
    1881             :         {
    1882         478 :             pszInput = pszNewInput;
    1883         478 :             bHasZ = true;
    1884         478 :             bHasM = true;
    1885             :         }
    1886             :     }
    1887      122682 :     *pbHasZ = bHasZ;
    1888      122682 :     *pbHasM = bHasM;
    1889             : 
    1890             :     /* -------------------------------------------------------------------- */
    1891             :     /*      Check for EMPTY ...                                             */
    1892             :     /* -------------------------------------------------------------------- */
    1893      122682 :     const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1894      122682 :     if (EQUAL(szToken, "EMPTY"))
    1895             :     {
    1896        1542 :         *ppszInput = pszNewInput;
    1897        1542 :         *pbIsEmpty = true;
    1898        1542 :         if (bHasZ)
    1899         130 :             set3D(TRUE);
    1900        1542 :         if (bHasM)
    1901          84 :             setMeasured(TRUE);
    1902        1542 :         return OGRERR_NONE;
    1903             :     }
    1904             : 
    1905      121140 :     if (!EQUAL(szToken, "("))
    1906          35 :         return OGRERR_CORRUPT_DATA;
    1907             : 
    1908      121105 :     if (!bHasZ && !bHasM)
    1909             :     {
    1910             :         // Test for old-style XXXXXXXXX(EMPTY).
    1911      119104 :         pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1912      119104 :         if (EQUAL(szToken, "EMPTY"))
    1913             :         {
    1914          69 :             pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1915             : 
    1916          69 :             if (EQUAL(szToken, ","))
    1917             :             {
    1918             :                 // This is OK according to SFSQL SPEC.
    1919             :             }
    1920          44 :             else if (!EQUAL(szToken, ")"))
    1921             :             {
    1922           9 :                 return OGRERR_CORRUPT_DATA;
    1923             :             }
    1924             :             else
    1925             :             {
    1926          35 :                 *ppszInput = pszNewInput;
    1927          35 :                 empty();
    1928          35 :                 *pbIsEmpty = true;
    1929          35 :                 return OGRERR_NONE;
    1930             :             }
    1931             :         }
    1932             :     }
    1933             : 
    1934      121061 :     *ppszInput = pszInput;
    1935             : 
    1936      121061 :     return OGRERR_NONE;
    1937             : }
    1938             : 
    1939             : //! @endcond
    1940             : 
    1941             : /************************************************************************/
    1942             : /*                           wktTypeString()                            */
    1943             : /************************************************************************/
    1944             : 
    1945             : //! @cond Doxygen_Suppress
    1946             : /** Get a type string for WKT, padded with a space at the end.
    1947             :  *
    1948             :  * @param variant  OGR type variant
    1949             :  * @return  "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
    1950             :  */
    1951       14136 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
    1952             : {
    1953       14136 :     std::string s(" ");
    1954             : 
    1955       14136 :     if (variant == wkbVariantIso)
    1956             :     {
    1957        9108 :         if (flags & OGR_G_3D)
    1958        1852 :             s += "Z";
    1959        9108 :         if (flags & OGR_G_MEASURED)
    1960        1088 :             s += "M";
    1961             :     }
    1962       14136 :     if (s.size() > 1)
    1963        2359 :         s += " ";
    1964       14136 :     return s;
    1965             : }
    1966             : 
    1967             : //! @endcond
    1968             : 
    1969             : /**
    1970             :  * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
    1971             :  * OGRwkbVariant variant = wkbVariantOldOgc ) const;
    1972             :  *
    1973             :  * \brief Convert a geometry into well known text format.
    1974             :  *
    1975             :  * This method relates to the SFCOM IWks::ExportToWKT() method.
    1976             :  *
    1977             :  * This method is the same as the C function OGR_G_ExportToWkt().
    1978             :  *
    1979             :  * @param ppszDstText a text buffer is allocated by the program, and assigned
    1980             :  *                    to the passed pointer. After use, *ppszDstText should be
    1981             :  *                    freed with CPLFree().
    1982             :  * @param variant the specification that must be conformed too :
    1983             :  *                    - wkbVariantOgc for old-style 99-402 extended
    1984             :  *                      dimension (Z) WKB types
    1985             :  *                    - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
    1986             :  *
    1987             :  * @return Currently OGRERR_NONE is always returned.
    1988             :  */
    1989        8843 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
    1990             : {
    1991        8843 :     OGRWktOptions opts;
    1992        8843 :     opts.variant = variant;
    1993        8843 :     OGRErr err(OGRERR_NONE);
    1994             : 
    1995        8843 :     std::string wkt = exportToWkt(opts, &err);
    1996        8843 :     *ppszDstText = CPLStrdup(wkt.data());
    1997       17686 :     return err;
    1998             : }
    1999             : 
    2000             : /************************************************************************/
    2001             : /*                         OGR_G_ExportToWkt()                          */
    2002             : /************************************************************************/
    2003             : 
    2004             : /**
    2005             :  * \brief Convert a geometry into well known text format.
    2006             :  *
    2007             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    2008             :  *
    2009             :  * For backward compatibility purposes, it exports the Old-style 99-402
    2010             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    2011             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    2012             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
    2013             :  *
    2014             :  * This function is the same as the CPP method OGRGeometry::exportToWkt().
    2015             :  *
    2016             :  * @param hGeom handle on the geometry to convert to a text format from.
    2017             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    2018             :  *                    to the passed pointer. After use, *ppszDstText should be
    2019             :  *                    freed with CPLFree().
    2020             :  *
    2021             :  * @return Currently OGRERR_NONE is always returned.
    2022             :  */
    2023             : 
    2024        2479 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2025             : 
    2026             : {
    2027        2479 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
    2028             : 
    2029        2479 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
    2030             : }
    2031             : 
    2032             : /************************************************************************/
    2033             : /*                      OGR_G_ExportToIsoWkt()                          */
    2034             : /************************************************************************/
    2035             : 
    2036             : /**
    2037             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
    2038             :  * known text format.
    2039             :  *
    2040             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    2041             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
    2042             :  * (Z&M) WKB types.
    2043             :  *
    2044             :  * This function is the same as the CPP method
    2045             :  * OGRGeometry::exportToWkt(wkbVariantIso).
    2046             :  *
    2047             :  * @param hGeom handle on the geometry to convert to a text format from.
    2048             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    2049             :  *                    to the passed pointer. After use, *ppszDstText should be
    2050             :  *                    freed with CPLFree().
    2051             :  *
    2052             :  * @return Currently OGRERR_NONE is always returned.
    2053             :  *
    2054             :  * @since GDAL 2.0
    2055             :  */
    2056             : 
    2057        5415 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2058             : 
    2059             : {
    2060        5415 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
    2061             : 
    2062        5415 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
    2063        5415 :                                                        wkbVariantIso);
    2064             : }
    2065             : 
    2066             : /**
    2067             :  * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
    2068             :  *
    2069             :  * \brief Fetch geometry type.
    2070             :  *
    2071             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2072             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2073             :  * to the return result.
    2074             :  *
    2075             :  * This method is the same as the C function OGR_G_GetGeometryType().
    2076             :  *
    2077             :  * @return the geometry type code.
    2078             :  */
    2079             : 
    2080             : /************************************************************************/
    2081             : /*                       OGR_G_GetGeometryType()                        */
    2082             : /************************************************************************/
    2083             : /**
    2084             :  * \brief Fetch geometry type.
    2085             :  *
    2086             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2087             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2088             :  * to the return result.
    2089             :  *
    2090             :  * This function is the same as the CPP method OGRGeometry::getGeometryType().
    2091             :  *
    2092             :  * @param hGeom handle on the geometry to get type from.
    2093             :  * @return the geometry type code.
    2094             :  */
    2095             : 
    2096        5230 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
    2097             : 
    2098             : {
    2099        5230 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
    2100             : 
    2101        5230 :     return OGRGeometry::FromHandle(hGeom)->getGeometryType();
    2102             : }
    2103             : 
    2104             : /**
    2105             :  * \fn const char * OGRGeometry::getGeometryName() const;
    2106             :  *
    2107             :  * \brief Fetch WKT name for geometry type.
    2108             :  *
    2109             :  * There is no SFCOM analog to this method.
    2110             :  *
    2111             :  * This method is the same as the C function OGR_G_GetGeometryName().
    2112             :  *
    2113             :  * @return name used for this geometry type in well known text format.  The
    2114             :  * returned pointer is to a static internal string and should not be modified
    2115             :  * or freed.
    2116             :  */
    2117             : 
    2118             : /************************************************************************/
    2119             : /*                       OGR_G_GetGeometryName()                        */
    2120             : /************************************************************************/
    2121             : /**
    2122             :  * \brief Fetch WKT name for geometry type.
    2123             :  *
    2124             :  * There is no SFCOM analog to this function.
    2125             :  *
    2126             :  * This function is the same as the CPP method OGRGeometry::getGeometryName().
    2127             :  *
    2128             :  * @param hGeom handle on the geometry to get name from.
    2129             :  * @return name used for this geometry type in well known text format.
    2130             :  */
    2131             : 
    2132       16689 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
    2133             : 
    2134             : {
    2135       16689 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
    2136             : 
    2137       16689 :     return OGRGeometry::FromHandle(hGeom)->getGeometryName();
    2138             : }
    2139             : 
    2140             : /**
    2141             :  * \fn OGRGeometry *OGRGeometry::clone() const;
    2142             :  *
    2143             :  * \brief Make a copy of this object.
    2144             :  *
    2145             :  * This method relates to the SFCOM IGeometry::clone() method.
    2146             :  *
    2147             :  * This method is the same as the C function OGR_G_Clone().
    2148             :  *
    2149             :  * @return a new object instance with the same geometry, and spatial
    2150             :  * reference system as the original.
    2151             :  */
    2152             : 
    2153             : /************************************************************************/
    2154             : /*                            OGR_G_Clone()                             */
    2155             : /************************************************************************/
    2156             : /**
    2157             :  * \brief Make a copy of this object.
    2158             :  *
    2159             :  * This function relates to the SFCOM IGeometry::clone() method.
    2160             :  *
    2161             :  * This function is the same as the CPP method OGRGeometry::clone().
    2162             :  *
    2163             :  * @param hGeom handle on the geometry to clone from.
    2164             :  * @return a handle on the copy of the geometry with the spatial
    2165             :  * reference system as the original.
    2166             :  */
    2167             : 
    2168       13516 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
    2169             : 
    2170             : {
    2171       13516 :     VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
    2172             : 
    2173       13516 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
    2174             : }
    2175             : 
    2176             : /**
    2177             :  * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
    2178             :  *
    2179             :  * \brief Returns spatial reference system for object.
    2180             :  *
    2181             :  * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
    2182             :  *
    2183             :  * This method is the same as the C function OGR_G_GetSpatialReference().
    2184             :  *
    2185             :  * @return a reference to the spatial reference object.  The object may be
    2186             :  * shared with many geometry objects, and should not be modified.
    2187             :  */
    2188             : 
    2189             : /************************************************************************/
    2190             : /*                     OGR_G_GetSpatialReference()                      */
    2191             : /************************************************************************/
    2192             : /**
    2193             :  * \brief Returns spatial reference system for geometry.
    2194             :  *
    2195             :  * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
    2196             :  *
    2197             :  * This function is the same as the CPP method
    2198             :  * OGRGeometry::getSpatialReference().
    2199             :  *
    2200             :  * @param hGeom handle on the geometry to get spatial reference from.
    2201             :  * @return a reference to the spatial reference geometry, which should not be
    2202             :  * modified.
    2203             :  */
    2204             : 
    2205          63 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
    2206             : 
    2207             : {
    2208          63 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
    2209             : 
    2210          63 :     return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
    2211         126 :         OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
    2212             : }
    2213             : 
    2214             : /**
    2215             :  * \fn void OGRGeometry::empty();
    2216             :  *
    2217             :  * \brief Clear geometry information.
    2218             :  * This restores the geometry to its initial
    2219             :  * state after construction, and before assignment of actual geometry.
    2220             :  *
    2221             :  * This method relates to the SFCOM IGeometry::Empty() method.
    2222             :  *
    2223             :  * This method is the same as the C function OGR_G_Empty().
    2224             :  */
    2225             : 
    2226             : /************************************************************************/
    2227             : /*                            OGR_G_Empty()                             */
    2228             : /************************************************************************/
    2229             : /**
    2230             :  * \brief Clear geometry information.
    2231             :  * This restores the geometry to its initial
    2232             :  * state after construction, and before assignment of actual geometry.
    2233             :  *
    2234             :  * This function relates to the SFCOM IGeometry::Empty() method.
    2235             :  *
    2236             :  * This function is the same as the CPP method OGRGeometry::empty().
    2237             :  *
    2238             :  * @param hGeom handle on the geometry to empty.
    2239             :  */
    2240             : 
    2241           4 : void OGR_G_Empty(OGRGeometryH hGeom)
    2242             : 
    2243             : {
    2244           4 :     VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
    2245             : 
    2246           4 :     OGRGeometry::FromHandle(hGeom)->empty();
    2247             : }
    2248             : 
    2249             : /**
    2250             :  * \fn OGRBoolean OGRGeometry::IsEmpty() const;
    2251             :  *
    2252             :  * \brief Returns TRUE (non-zero) if the object has no points.
    2253             :  *
    2254             :  * Normally this
    2255             :  * returns FALSE except between when an object is instantiated and points
    2256             :  * have been assigned.
    2257             :  *
    2258             :  * This method relates to the SFCOM IGeometry::IsEmpty() method.
    2259             :  *
    2260             :  * @return TRUE if object is empty, otherwise FALSE.
    2261             :  */
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                         OGR_G_IsEmpty()                              */
    2265             : /************************************************************************/
    2266             : 
    2267             : /**
    2268             :  * \brief Test if the geometry is empty.
    2269             :  *
    2270             :  * This method is the same as the CPP method OGRGeometry::IsEmpty().
    2271             :  *
    2272             :  * @param hGeom The Geometry to test.
    2273             :  *
    2274             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2275             :  */
    2276             : 
    2277        2177 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
    2278             : 
    2279             : {
    2280        2177 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
    2281             : 
    2282        2177 :     return OGRGeometry::FromHandle(hGeom)->IsEmpty();
    2283             : }
    2284             : 
    2285             : /************************************************************************/
    2286             : /*                              IsValid()                               */
    2287             : /************************************************************************/
    2288             : 
    2289             : /**
    2290             :  * \brief Test if the geometry is valid.
    2291             :  *
    2292             :  * This method is the same as the C function OGR_G_IsValid().
    2293             :  *
    2294             :  * This method is built on the GEOS library, check it for the definition
    2295             :  * of the geometry operation.
    2296             :  * If OGR is built without the GEOS library, this method will always return
    2297             :  * FALSE.
    2298             :  *
    2299             :  *
    2300             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2301             :  */
    2302             : 
    2303        2005 : OGRBoolean OGRGeometry::IsValid() const
    2304             : 
    2305             : {
    2306        2005 :     if (IsSFCGALCompatible())
    2307             :     {
    2308             : #ifndef HAVE_SFCGAL
    2309             : 
    2310             : #ifdef HAVE_GEOS
    2311           2 :         if (wkbFlatten(getGeometryType()) == wkbTriangle)
    2312             :         {
    2313             :             // go on
    2314             :         }
    2315             :         else
    2316             : #endif
    2317             :         {
    2318           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2319             :                      "SFCGAL support not enabled.");
    2320           1 :             return FALSE;
    2321             :         }
    2322             : #else
    2323             :         sfcgal_init();
    2324             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    2325             :         if (poThis == nullptr)
    2326             :         {
    2327             :             CPLError(CE_Failure, CPLE_IllegalArg,
    2328             :                      "SFCGAL geometry returned is NULL");
    2329             :             return FALSE;
    2330             :         }
    2331             : 
    2332             :         const int res = sfcgal_geometry_is_valid(poThis);
    2333             :         sfcgal_geometry_delete(poThis);
    2334             :         return res == 1;
    2335             : #endif
    2336             :     }
    2337             : 
    2338             :     {
    2339             : #ifndef HAVE_GEOS
    2340             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2341             :         return FALSE;
    2342             : 
    2343             : #else
    2344        1971 :         OGRBoolean bResult = FALSE;
    2345             : 
    2346             :         // Some invalid geometries, such as lines with one point, or
    2347             :         // rings that do not close, cannot be converted to GEOS.
    2348             :         // For validity checking we initialize the GEOS context with
    2349             :         // the warning handler as the error handler to avoid emitting
    2350             :         // CE_Failure when a geometry cannot be converted to GEOS.
    2351             :         GEOSContextHandle_t hGEOSCtxt =
    2352        1971 :             initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
    2353             : 
    2354        2011 :         GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2355             : 
    2356        1958 :         if (hThisGeosGeom != nullptr)
    2357             :         {
    2358        1903 :             bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom);
    2359             : #ifdef DEBUG_VERBOSE
    2360             :             if (!bResult)
    2361             :             {
    2362             :                 char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
    2363             :                 CPLDebug("OGR", "%s", pszReason);
    2364             :                 GEOSFree_r(hGEOSCtxt, pszReason);
    2365             :             }
    2366             : #endif
    2367        1918 :             GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2368             :         }
    2369        2038 :         freeGEOSContext(hGEOSCtxt);
    2370             : 
    2371        1967 :         return bResult;
    2372             : 
    2373             : #endif  // HAVE_GEOS
    2374             :     }
    2375             : }
    2376             : 
    2377             : /************************************************************************/
    2378             : /*                           OGR_G_IsValid()                            */
    2379             : /************************************************************************/
    2380             : 
    2381             : /**
    2382             :  * \brief Test if the geometry is valid.
    2383             :  *
    2384             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2385             :  *
    2386             :  * This function is built on the GEOS library, check it for the definition
    2387             :  * of the geometry operation.
    2388             :  * If OGR is built without the GEOS library, this function will always return
    2389             :  * FALSE.
    2390             :  *
    2391             :  * @param hGeom The Geometry to test.
    2392             :  *
    2393             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2394             :  */
    2395             : 
    2396          19 : int OGR_G_IsValid(OGRGeometryH hGeom)
    2397             : 
    2398             : {
    2399          19 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
    2400             : 
    2401          19 :     return OGRGeometry::FromHandle(hGeom)->IsValid();
    2402             : }
    2403             : 
    2404             : /************************************************************************/
    2405             : /*                              IsSimple()                               */
    2406             : /************************************************************************/
    2407             : 
    2408             : /**
    2409             :  * \brief Test if the geometry is simple.
    2410             :  *
    2411             :  * This method is the same as the C function OGR_G_IsSimple().
    2412             :  *
    2413             :  * This method is built on the GEOS library, check it for the definition
    2414             :  * of the geometry operation.
    2415             :  * If OGR is built without the GEOS library, this method will always return
    2416             :  * FALSE.
    2417             :  *
    2418             :  *
    2419             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2420             :  */
    2421             : 
    2422           5 : OGRBoolean OGRGeometry::IsSimple() const
    2423             : 
    2424             : {
    2425             : #ifndef HAVE_GEOS
    2426             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2427             :     return FALSE;
    2428             : 
    2429             : #else
    2430             : 
    2431           5 :     OGRBoolean bResult = FALSE;
    2432             : 
    2433           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2434           5 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2435             : 
    2436           5 :     if (hThisGeosGeom != nullptr)
    2437             :     {
    2438           5 :         bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom);
    2439           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2440             :     }
    2441           5 :     freeGEOSContext(hGEOSCtxt);
    2442             : 
    2443           5 :     return bResult;
    2444             : 
    2445             : #endif  // HAVE_GEOS
    2446             : }
    2447             : 
    2448             : /**
    2449             :  * \brief Returns TRUE if the geometry is simple.
    2450             :  *
    2451             :  * Returns TRUE if the geometry has no anomalous geometric points, such
    2452             :  * as self intersection or self tangency. The description of each
    2453             :  * instantiable geometric class will include the specific conditions that
    2454             :  * cause an instance of that class to be classified as not simple.
    2455             :  *
    2456             :  * This function is the same as the C++ method OGRGeometry::IsSimple() method.
    2457             :  *
    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             :  *
    2463             :  * @return TRUE if object is simple, otherwise FALSE.
    2464             :  */
    2465             : 
    2466           5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
    2467             : 
    2468             : {
    2469           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
    2470             : 
    2471           5 :     return OGRGeometry::FromHandle(hGeom)->IsSimple();
    2472             : }
    2473             : 
    2474             : /************************************************************************/
    2475             : /*                              IsRing()                               */
    2476             : /************************************************************************/
    2477             : 
    2478             : /**
    2479             :  * \brief Test if the geometry is a ring
    2480             :  *
    2481             :  * This method is the same as the C function OGR_G_IsRing().
    2482             :  *
    2483             :  * This method is built on the GEOS library, check it for the definition
    2484             :  * of the geometry operation.
    2485             :  * If OGR is built without the GEOS library, this method will always return
    2486             :  * FALSE.
    2487             :  *
    2488             :  *
    2489             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2490             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2491             :  */
    2492             : 
    2493           1 : OGRBoolean OGRGeometry::IsRing() const
    2494             : 
    2495             : {
    2496             : #ifndef HAVE_GEOS
    2497             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2498             :     return FALSE;
    2499             : 
    2500             : #else
    2501             : 
    2502           1 :     OGRBoolean bResult = FALSE;
    2503             : 
    2504           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2505           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2506             : 
    2507           1 :     if (hThisGeosGeom != nullptr)
    2508             :     {
    2509           1 :         bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom);
    2510           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2511             :     }
    2512           1 :     freeGEOSContext(hGEOSCtxt);
    2513             : 
    2514           1 :     return bResult;
    2515             : 
    2516             : #endif  // HAVE_GEOS
    2517             : }
    2518             : 
    2519             : /************************************************************************/
    2520             : /*                            OGR_G_IsRing()                            */
    2521             : /************************************************************************/
    2522             : 
    2523             : /**
    2524             :  * \brief Test if the geometry is a ring
    2525             :  *
    2526             :  * This function is the same as the C++ method OGRGeometry::IsRing().
    2527             :  *
    2528             :  * This function is built on the GEOS library, check it for the definition
    2529             :  * of the geometry operation.
    2530             :  * If OGR is built without the GEOS library, this function will always return
    2531             :  * FALSE.
    2532             :  *
    2533             :  * @param hGeom The Geometry to test.
    2534             :  *
    2535             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2536             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2537             :  */
    2538             : 
    2539           1 : int OGR_G_IsRing(OGRGeometryH hGeom)
    2540             : 
    2541             : {
    2542           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
    2543             : 
    2544           1 :     return OGRGeometry::FromHandle(hGeom)->IsRing();
    2545             : }
    2546             : 
    2547             : /************************************************************************/
    2548             : /*                     OGRFromOGCGeomType()                             */
    2549             : /************************************************************************/
    2550             : 
    2551             : /** Map OGC geometry format type to corresponding OGR constants.
    2552             :  * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
    2553             :  * @return OGR constant.
    2554             :  */
    2555        2615 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
    2556             : {
    2557        2615 :     OGRwkbGeometryType eType = wkbUnknown;
    2558        2615 :     bool bConvertTo3D = false;
    2559        2615 :     bool bIsMeasured = false;
    2560        2615 :     if (*pszGeomType != '\0')
    2561             :     {
    2562        2609 :         char ch = pszGeomType[strlen(pszGeomType) - 1];
    2563        2609 :         if (ch == 'm' || ch == 'M')
    2564             :         {
    2565           2 :             bIsMeasured = true;
    2566           2 :             if (strlen(pszGeomType) > 1)
    2567           2 :                 ch = pszGeomType[strlen(pszGeomType) - 2];
    2568             :         }
    2569        2609 :         if (ch == 'z' || ch == 'Z')
    2570             :         {
    2571          27 :             bConvertTo3D = true;
    2572             :         }
    2573             :     }
    2574             : 
    2575        2615 :     if (STARTS_WITH_CI(pszGeomType, "POINT"))
    2576         572 :         eType = wkbPoint;
    2577        2043 :     else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
    2578         182 :         eType = wkbLineString;
    2579        1861 :     else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
    2580         732 :         eType = wkbPolygon;
    2581        1129 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
    2582          23 :         eType = wkbMultiPoint;
    2583        1106 :     else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
    2584          13 :         eType = wkbMultiLineString;
    2585        1093 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
    2586          73 :         eType = wkbMultiPolygon;
    2587        1020 :     else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
    2588           4 :         eType = wkbGeometryCollection;
    2589        1016 :     else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
    2590         333 :         eType = wkbCircularString;
    2591         683 :     else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
    2592           0 :         eType = wkbCompoundCurve;
    2593         683 :     else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
    2594          16 :         eType = wkbCurvePolygon;
    2595         667 :     else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
    2596           2 :         eType = wkbMultiCurve;
    2597         665 :     else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
    2598           0 :         eType = wkbMultiSurface;
    2599         665 :     else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
    2600           0 :         eType = wkbTriangle;
    2601         665 :     else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
    2602           1 :         eType = wkbPolyhedralSurface;
    2603         664 :     else if (STARTS_WITH_CI(pszGeomType, "TIN"))
    2604           5 :         eType = wkbTIN;
    2605         659 :     else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
    2606           3 :         eType = wkbCurve;
    2607         656 :     else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
    2608           3 :         eType = wkbSurface;
    2609             :     else
    2610         653 :         eType = wkbUnknown;
    2611             : 
    2612        2615 :     if (bConvertTo3D)
    2613          27 :         eType = wkbSetZ(eType);
    2614        2615 :     if (bIsMeasured)
    2615           2 :         eType = wkbSetM(eType);
    2616             : 
    2617        2615 :     return eType;
    2618             : }
    2619             : 
    2620             : /************************************************************************/
    2621             : /*                     OGRToOGCGeomType()                               */
    2622             : /************************************************************************/
    2623             : 
    2624             : /** Map OGR geometry format constants to corresponding OGC geometry type.
    2625             :  * @param eGeomType OGR geometry type
    2626             :  * @param bCamelCase Whether the return should be like "MultiPoint"
    2627             :  *        (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
    2628             :  * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
    2629             :  *               Default is false.
    2630             :  * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
    2631             :  *                       suffix. Default is false.
    2632             :  * @return string with OGC geometry type (without dimensionality)
    2633             :  */
    2634        2626 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
    2635             :                              bool bAddZM, bool bSpaceBeforeZM)
    2636             : {
    2637        2626 :     const char *pszRet = "";
    2638        2626 :     switch (wkbFlatten(eGeomType))
    2639             :     {
    2640        1477 :         case wkbUnknown:
    2641        1477 :             pszRet = "Geometry";
    2642        1477 :             break;
    2643         357 :         case wkbPoint:
    2644         357 :             pszRet = "Point";
    2645         357 :             break;
    2646         146 :         case wkbLineString:
    2647         146 :             pszRet = "LineString";
    2648         146 :             break;
    2649         311 :         case wkbPolygon:
    2650         311 :             pszRet = "Polygon";
    2651         311 :             break;
    2652          48 :         case wkbMultiPoint:
    2653          48 :             pszRet = "MultiPoint";
    2654          48 :             break;
    2655          56 :         case wkbMultiLineString:
    2656          56 :             pszRet = "MultiLineString";
    2657          56 :             break;
    2658          74 :         case wkbMultiPolygon:
    2659          74 :             pszRet = "MultiPolygon";
    2660          74 :             break;
    2661          54 :         case wkbGeometryCollection:
    2662          54 :             pszRet = "GeometryCollection";
    2663          54 :             break;
    2664           9 :         case wkbCircularString:
    2665           9 :             pszRet = "CircularString";
    2666           9 :             break;
    2667           3 :         case wkbCompoundCurve:
    2668           3 :             pszRet = "CompoundCurve";
    2669           3 :             break;
    2670          12 :         case wkbCurvePolygon:
    2671          12 :             pszRet = "CurvePolygon";
    2672          12 :             break;
    2673           2 :         case wkbMultiCurve:
    2674           2 :             pszRet = "MultiCurve";
    2675           2 :             break;
    2676           3 :         case wkbMultiSurface:
    2677           3 :             pszRet = "MultiSurface";
    2678           3 :             break;
    2679           3 :         case wkbTriangle:
    2680           3 :             pszRet = "Triangle";
    2681           3 :             break;
    2682           5 :         case wkbPolyhedralSurface:
    2683           5 :             pszRet = "PolyhedralSurface";
    2684           5 :             break;
    2685           1 :         case wkbTIN:
    2686           1 :             pszRet = "Tin";
    2687           1 :             break;
    2688           3 :         case wkbCurve:
    2689           3 :             pszRet = "Curve";
    2690           3 :             break;
    2691           3 :         case wkbSurface:
    2692           3 :             pszRet = "Surface";
    2693           3 :             break;
    2694          59 :         default:
    2695          59 :             break;
    2696             :     }
    2697        2626 :     if (bAddZM)
    2698             :     {
    2699          45 :         const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    2700          45 :         const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    2701          45 :         if (bHasZ || bHasM)
    2702             :         {
    2703          10 :             if (bSpaceBeforeZM)
    2704           1 :                 pszRet = CPLSPrintf("%s ", pszRet);
    2705          10 :             if (bHasZ)
    2706           9 :                 pszRet = CPLSPrintf("%sZ", pszRet);
    2707          10 :             if (bHasM)
    2708           5 :                 pszRet = CPLSPrintf("%sM", pszRet);
    2709             :         }
    2710             :     }
    2711        2626 :     if (!bCamelCase)
    2712        2580 :         pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
    2713        2626 :     return pszRet;
    2714             : }
    2715             : 
    2716             : /************************************************************************/
    2717             : /*                       OGRGeometryTypeToName()                        */
    2718             : /************************************************************************/
    2719             : 
    2720             : /**
    2721             :  * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
    2722             :  * value.  The returned value should not be modified, or freed by the
    2723             :  * application.
    2724             :  *
    2725             :  * This function is C callable.
    2726             :  *
    2727             :  * @param eType the geometry type.
    2728             :  *
    2729             :  * @return internal human readable string, or NULL on failure.
    2730             :  */
    2731             : 
    2732         383 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
    2733             : 
    2734             : {
    2735         383 :     bool b3D = wkbHasZ(eType);
    2736         383 :     bool bMeasured = wkbHasM(eType);
    2737             : 
    2738         383 :     switch (wkbFlatten(eType))
    2739             :     {
    2740          34 :         case wkbUnknown:
    2741          34 :             if (b3D && bMeasured)
    2742           0 :                 return "3D Measured Unknown (any)";
    2743          34 :             else if (b3D)
    2744           1 :                 return "3D Unknown (any)";
    2745          33 :             else if (bMeasured)
    2746           0 :                 return "Measured Unknown (any)";
    2747             :             else
    2748          33 :                 return "Unknown (any)";
    2749             : 
    2750          52 :         case wkbPoint:
    2751          52 :             if (b3D && bMeasured)
    2752           2 :                 return "3D Measured Point";
    2753          50 :             else if (b3D)
    2754          11 :                 return "3D Point";
    2755          39 :             else if (bMeasured)
    2756           2 :                 return "Measured Point";
    2757             :             else
    2758          37 :                 return "Point";
    2759             : 
    2760          37 :         case wkbLineString:
    2761          37 :             if (b3D && bMeasured)
    2762           0 :                 return "3D Measured Line String";
    2763          37 :             else if (b3D)
    2764           9 :                 return "3D Line String";
    2765          28 :             else if (bMeasured)
    2766           0 :                 return "Measured Line String";
    2767             :             else
    2768          28 :                 return "Line String";
    2769             : 
    2770          60 :         case wkbPolygon:
    2771          60 :             if (b3D && bMeasured)
    2772           0 :                 return "3D Measured Polygon";
    2773          60 :             else if (b3D)
    2774           8 :                 return "3D Polygon";
    2775          52 :             else if (bMeasured)
    2776           0 :                 return "Measured Polygon";
    2777             :             else
    2778          52 :                 return "Polygon";
    2779             : 
    2780          19 :         case wkbMultiPoint:
    2781          19 :             if (b3D && bMeasured)
    2782           0 :                 return "3D Measured Multi Point";
    2783          19 :             else if (b3D)
    2784           9 :                 return "3D Multi Point";
    2785          10 :             else if (bMeasured)
    2786           0 :                 return "Measured Multi Point";
    2787             :             else
    2788          10 :                 return "Multi Point";
    2789             : 
    2790          14 :         case wkbMultiLineString:
    2791          14 :             if (b3D && bMeasured)
    2792           0 :                 return "3D Measured Multi Line String";
    2793          14 :             else if (b3D)
    2794           6 :                 return "3D Multi Line String";
    2795           8 :             else if (bMeasured)
    2796           0 :                 return "Measured Multi Line String";
    2797             :             else
    2798           8 :                 return "Multi Line String";
    2799             : 
    2800          19 :         case wkbMultiPolygon:
    2801          19 :             if (b3D && bMeasured)
    2802           0 :                 return "3D Measured Multi Polygon";
    2803          19 :             else if (b3D)
    2804           8 :                 return "3D Multi Polygon";
    2805          11 :             else if (bMeasured)
    2806           0 :                 return "Measured Multi Polygon";
    2807             :             else
    2808          11 :                 return "Multi Polygon";
    2809             : 
    2810          25 :         case wkbGeometryCollection:
    2811          25 :             if (b3D && bMeasured)
    2812           0 :                 return "3D Measured Geometry Collection";
    2813          25 :             else if (b3D)
    2814          10 :                 return "3D Geometry Collection";
    2815          15 :             else if (bMeasured)
    2816           0 :                 return "Measured Geometry Collection";
    2817             :             else
    2818          15 :                 return "Geometry Collection";
    2819             : 
    2820           0 :         case wkbCircularString:
    2821           0 :             if (b3D && bMeasured)
    2822           0 :                 return "3D Measured Circular String";
    2823           0 :             else if (b3D)
    2824           0 :                 return "3D Circular String";
    2825           0 :             else if (bMeasured)
    2826           0 :                 return "Measured Circular String";
    2827             :             else
    2828           0 :                 return "Circular String";
    2829             : 
    2830           1 :         case wkbCompoundCurve:
    2831           1 :             if (b3D && bMeasured)
    2832           0 :                 return "3D Measured Compound Curve";
    2833           1 :             else if (b3D)
    2834           0 :                 return "3D Compound Curve";
    2835           1 :             else if (bMeasured)
    2836           0 :                 return "Measured Compound Curve";
    2837             :             else
    2838           1 :                 return "Compound Curve";
    2839             : 
    2840           0 :         case wkbCurvePolygon:
    2841           0 :             if (b3D && bMeasured)
    2842           0 :                 return "3D Measured Curve Polygon";
    2843           0 :             else if (b3D)
    2844           0 :                 return "3D Curve Polygon";
    2845           0 :             else if (bMeasured)
    2846           0 :                 return "Measured Curve Polygon";
    2847             :             else
    2848           0 :                 return "Curve Polygon";
    2849             : 
    2850           0 :         case wkbMultiCurve:
    2851           0 :             if (b3D && bMeasured)
    2852           0 :                 return "3D Measured Multi Curve";
    2853           0 :             else if (b3D)
    2854           0 :                 return "3D Multi Curve";
    2855           0 :             else if (bMeasured)
    2856           0 :                 return "Measured Multi Curve";
    2857             :             else
    2858           0 :                 return "Multi Curve";
    2859             : 
    2860           0 :         case wkbMultiSurface:
    2861           0 :             if (b3D && bMeasured)
    2862           0 :                 return "3D Measured Multi Surface";
    2863           0 :             else if (b3D)
    2864           0 :                 return "3D Multi Surface";
    2865           0 :             else if (bMeasured)
    2866           0 :                 return "Measured Multi Surface";
    2867             :             else
    2868           0 :                 return "Multi Surface";
    2869             : 
    2870           4 :         case wkbCurve:
    2871           4 :             if (b3D && bMeasured)
    2872           1 :                 return "3D Measured Curve";
    2873           3 :             else if (b3D)
    2874           1 :                 return "3D Curve";
    2875           2 :             else if (bMeasured)
    2876           1 :                 return "Measured Curve";
    2877             :             else
    2878           1 :                 return "Curve";
    2879             : 
    2880           4 :         case wkbSurface:
    2881           4 :             if (b3D && bMeasured)
    2882           1 :                 return "3D Measured Surface";
    2883           3 :             else if (b3D)
    2884           1 :                 return "3D Surface";
    2885           2 :             else if (bMeasured)
    2886           1 :                 return "Measured Surface";
    2887             :             else
    2888           1 :                 return "Surface";
    2889             : 
    2890           0 :         case wkbTriangle:
    2891           0 :             if (b3D && bMeasured)
    2892           0 :                 return "3D Measured Triangle";
    2893           0 :             else if (b3D)
    2894           0 :                 return "3D Triangle";
    2895           0 :             else if (bMeasured)
    2896           0 :                 return "Measured Triangle";
    2897             :             else
    2898           0 :                 return "Triangle";
    2899             : 
    2900           0 :         case wkbPolyhedralSurface:
    2901           0 :             if (b3D && bMeasured)
    2902           0 :                 return "3D Measured PolyhedralSurface";
    2903           0 :             else if (b3D)
    2904           0 :                 return "3D PolyhedralSurface";
    2905           0 :             else if (bMeasured)
    2906           0 :                 return "Measured PolyhedralSurface";
    2907             :             else
    2908           0 :                 return "PolyhedralSurface";
    2909             : 
    2910           2 :         case wkbTIN:
    2911           2 :             if (b3D && bMeasured)
    2912           0 :                 return "3D Measured TIN";
    2913           2 :             else if (b3D)
    2914           0 :                 return "3D TIN";
    2915           2 :             else if (bMeasured)
    2916           0 :                 return "Measured TIN";
    2917             :             else
    2918           2 :                 return "TIN";
    2919             : 
    2920         111 :         case wkbNone:
    2921         111 :             return "None";
    2922             : 
    2923           1 :         default:
    2924             :         {
    2925           1 :             return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
    2926             :         }
    2927             :     }
    2928             : }
    2929             : 
    2930             : /************************************************************************/
    2931             : /*                       OGRMergeGeometryTypes()                        */
    2932             : /************************************************************************/
    2933             : 
    2934             : /**
    2935             :  * \brief Find common geometry type.
    2936             :  *
    2937             :  * Given two geometry types, find the most specific common
    2938             :  * type.  Normally used repeatedly with the geometries in a
    2939             :  * layer to try and establish the most specific geometry type
    2940             :  * that can be reported for the layer.
    2941             :  *
    2942             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    2943             :  * geometry types with nothing in common but the base geometry
    2944             :  * type.  wkbNone should be used to indicate that no geometries
    2945             :  * have been encountered yet, and means the first geometry
    2946             :  * encountered will establish the preliminary type.
    2947             :  *
    2948             :  * @param eMain the first input geometry type.
    2949             :  * @param eExtra the second input geometry type.
    2950             :  *
    2951             :  * @return the merged geometry type.
    2952             :  */
    2953             : 
    2954           0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
    2955             :                                          OGRwkbGeometryType eExtra)
    2956             : 
    2957             : {
    2958           0 :     return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
    2959             : }
    2960             : 
    2961             : /**
    2962             :  * \brief Find common geometry type.
    2963             :  *
    2964             :  * Given two geometry types, find the most specific common
    2965             :  * type.  Normally used repeatedly with the geometries in a
    2966             :  * layer to try and establish the most specific geometry type
    2967             :  * that can be reported for the layer.
    2968             :  *
    2969             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    2970             :  * geometry types with nothing in common but the base geometry
    2971             :  * type.  wkbNone should be used to indicate that no geometries
    2972             :  * have been encountered yet, and means the first geometry
    2973             :  * encountered will establish the preliminary type.
    2974             :  *
    2975             :  * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
    2976             :  * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
    2977             :  * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
    2978             :  * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
    2979             :  *
    2980             :  * @param eMain the first input geometry type.
    2981             :  * @param eExtra the second input geometry type.
    2982             :  * @param bAllowPromotingToCurves determine if promotion to curve type
    2983             :  * must be done.
    2984             :  *
    2985             :  * @return the merged geometry type.
    2986             :  *
    2987             :  * @since GDAL 2.0
    2988             :  */
    2989             : 
    2990         573 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
    2991             :                                            OGRwkbGeometryType eExtra,
    2992             :                                            int bAllowPromotingToCurves)
    2993             : 
    2994             : {
    2995         573 :     OGRwkbGeometryType eFMain = wkbFlatten(eMain);
    2996         573 :     OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
    2997             : 
    2998         573 :     const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
    2999         573 :     const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
    3000             : 
    3001         573 :     if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
    3002          17 :         return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3003             : 
    3004         556 :     if (eFMain == wkbNone)
    3005           2 :         return eExtra;
    3006             : 
    3007         554 :     if (eFExtra == wkbNone)
    3008           0 :         return eMain;
    3009             : 
    3010         554 :     if (eFMain == eFExtra)
    3011             :     {
    3012         537 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3013             :     }
    3014             : 
    3015          17 :     if (bAllowPromotingToCurves)
    3016             :     {
    3017          17 :         if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
    3018           4 :             return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
    3019             : 
    3020          13 :         if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3021           3 :             return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3022             : 
    3023          10 :         if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3024           6 :             return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3025             :     }
    3026             : 
    3027             :     // One is subclass of the other one
    3028           4 :     if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3029             :     {
    3030           0 :         return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3031             :     }
    3032           4 :     else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3033             :     {
    3034           0 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3035             :     }
    3036             : 
    3037             :     // Nothing apparently in common.
    3038           4 :     return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3039             : }
    3040             : 
    3041             : /**
    3042             :  * \fn void OGRGeometry::flattenTo2D();
    3043             :  *
    3044             :  * \brief Convert geometry to strictly 2D.
    3045             :  * In a sense this converts all Z coordinates
    3046             :  * to 0.0.
    3047             :  *
    3048             :  * This method is the same as the C function OGR_G_FlattenTo2D().
    3049             :  */
    3050             : 
    3051             : /************************************************************************/
    3052             : /*                         OGR_G_FlattenTo2D()                          */
    3053             : /************************************************************************/
    3054             : /**
    3055             :  * \brief Convert geometry to strictly 2D.
    3056             :  * In a sense this converts all Z coordinates
    3057             :  * to 0.0.
    3058             :  *
    3059             :  * This function is the same as the CPP method OGRGeometry::flattenTo2D().
    3060             :  *
    3061             :  * @param hGeom handle on the geometry to convert.
    3062             :  */
    3063             : 
    3064          31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
    3065             : 
    3066             : {
    3067          31 :     OGRGeometry::FromHandle(hGeom)->flattenTo2D();
    3068          31 : }
    3069             : 
    3070             : /************************************************************************/
    3071             : /*                            exportToGML()                             */
    3072             : /************************************************************************/
    3073             : 
    3074             : /**
    3075             :  * \fn char *OGRGeometry::exportToGML( const char* const *
    3076             :  * papszOptions = NULL ) const;
    3077             :  *
    3078             :  * \brief Convert a geometry into GML format.
    3079             :  *
    3080             :  * The GML geometry is expressed directly in terms of GML basic data
    3081             :  * types assuming the this is available in the gml namespace.  The returned
    3082             :  * string should be freed with CPLFree() when no longer required.
    3083             :  *
    3084             :  * The supported options are :
    3085             :  * <ul>
    3086             :  * <li> FORMAT=GML2/GML3/GML32 (GML2 or GML32 added in GDAL 2.1).
    3087             :  *      If not set, it will default to GML 2.1.2 output.
    3088             :  * </li>
    3089             :  * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
    3090             :  *      To use gml:Curve element for linestrings.
    3091             :  *      Otherwise gml:LineString will be used .
    3092             :  * </li>
    3093             :  * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
    3094             :  *      SRSNAME_FORMAT in GDAL &gt;=2.2). Defaults to YES.
    3095             :  *      If YES, SRS with EPSG authority will be written with the
    3096             :  *      "urn:ogc:def:crs:EPSG::" prefix.
    3097             :  *      In the case the SRS should be treated as lat/long or
    3098             :  *      northing/easting, then the function will take care of coordinate order
    3099             :  *      swapping if the data axis to CRS axis mapping indicates it.
    3100             :  *      If set to NO, SRS with EPSG authority will be written with the "EPSG:"
    3101             :  *      prefix, even if they are in lat/long order.
    3102             :  * </li>
    3103             :  * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3, added
    3104             :  *      in GDAL 2.2). Defaults to OGC_URN.  If SHORT, then srsName will be in
    3105             :  *      the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
    3106             :  *      in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
    3107             :  *      then srsName will be in the form
    3108             :  *      http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
    3109             :  *      OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
    3110             :  *      or northing/easting, then the function will take care of coordinate
    3111             :  *      order swapping if the data axis to CRS axis mapping indicates it.
    3112             :  * </li>
    3113             :  * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
    3114             :  *      top-level geometry element with the provided value.
    3115             :  *      Required for GML 3.2 compatibility.
    3116             :  * </li>
    3117             :  * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
    3118             :  *      FORMAT=GML3/GML32, GDAL >= 2.0) Default to POSLIST.
    3119             :  *      For 2.5D geometries, define the location where to attach the
    3120             :  *      srsDimension attribute.
    3121             :  *      There are diverging implementations. Some put in on the
    3122             :  *      &lt;gml:posList&gt; element, other on the top geometry element.
    3123             :  * </li>
    3124             :  * <li> NAMESPACE_DECL=YES/NO. If set to YES,
    3125             :  *      xmlns:gml="http://www.opengis.net/gml" will be added to the root node
    3126             :  *      for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
    3127             :  * </li>
    3128             :  * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
    3129             :  *      Resolution for the coordinate precision of the X and Y coordinates.
    3130             :  *      Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
    3131             :  *      to 5 decimal digits. 0 for the default behavior.
    3132             :  * </li>
    3133             :  * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
    3134             :  *      Resolution for the coordinate precision of the Z coordinates.
    3135             :  *      Expressed in the units of the Z axis of the SRS.
    3136             :  *      0 for the default behavior.
    3137             :  * </li>
    3138             :  * </ul>
    3139             :  *
    3140             :  * This method is the same as the C function OGR_G_ExportToGMLEx().
    3141             :  *
    3142             :  * @param papszOptions NULL-terminated list of options.
    3143             :  * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
    3144             :  */
    3145             : 
    3146         251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
    3147             : {
    3148         251 :     return OGR_G_ExportToGMLEx(
    3149             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
    3150         251 :         const_cast<char **>(papszOptions));
    3151             : }
    3152             : 
    3153             : /************************************************************************/
    3154             : /*                            exportToKML()                             */
    3155             : /************************************************************************/
    3156             : 
    3157             : /**
    3158             :  * \fn char *OGRGeometry::exportToKML() const;
    3159             :  *
    3160             :  * \brief Convert a geometry into KML format.
    3161             :  *
    3162             :  * The returned string should be freed with CPLFree() when no longer required.
    3163             :  *
    3164             :  * This method is the same as the C function OGR_G_ExportToKML().
    3165             :  *
    3166             :  * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
    3167             :  */
    3168             : 
    3169           0 : char *OGRGeometry::exportToKML() const
    3170             : {
    3171           0 :     return OGR_G_ExportToKML(
    3172           0 :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
    3173             : }
    3174             : 
    3175             : /************************************************************************/
    3176             : /*                            exportToJson()                             */
    3177             : /************************************************************************/
    3178             : 
    3179             : /**
    3180             :  * \fn char *OGRGeometry::exportToJson() const;
    3181             :  *
    3182             :  * \brief Convert a geometry into GeoJSON format.
    3183             :  *
    3184             :  * The returned string should be freed with CPLFree() when no longer required.
    3185             :  *
    3186             :  * The following options are supported :
    3187             :  * <ul>
    3188             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
    3189             :  * (added in GDAL 3.9)</li>
    3190             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
    3191             :  * (added in GDAL 3.9)</li>
    3192             :  * </ul>
    3193             :  *
    3194             :  * This method is the same as the C function OGR_G_ExportToJson().
    3195             :  *
    3196             :  * @param papszOptions Null terminated list of options, or null (added in 3.9)
    3197             :  * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
    3198             :  */
    3199             : 
    3200          42 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
    3201             : {
    3202          42 :     OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
    3203          42 :     return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
    3204          42 :                                 const_cast<char **>(papszOptions));
    3205             : }
    3206             : 
    3207             : /************************************************************************/
    3208             : /*                 OGRSetGenerate_DB2_V72_BYTE_ORDER()                  */
    3209             : /************************************************************************/
    3210             : 
    3211             : /**
    3212             :  * \brief Special entry point to enable the hack for generating DB2 V7.2 style
    3213             :  * WKB.
    3214             :  *
    3215             :  * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
    3216             :  * order in WKB.  This entry point is used to turn on or off the generation of
    3217             :  * such WKB.
    3218             :  */
    3219           4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
    3220             : 
    3221             : {
    3222             : #if defined(HACK_FOR_IBM_DB2_V72)
    3223           4 :     OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
    3224           4 :     return OGRERR_NONE;
    3225             : #else
    3226             :     if (bGenerate_DB2_V72_BYTE_ORDER)
    3227             :         return OGRERR_FAILURE;
    3228             :     else
    3229             :         return OGRERR_NONE;
    3230             : #endif
    3231             : }
    3232             : 
    3233             : /************************************************************************/
    3234             : /*                 OGRGetGenerate_DB2_V72_BYTE_ORDER()                  */
    3235             : /*                                                                      */
    3236             : /*      This is a special entry point to get the value of static flag   */
    3237             : /*      OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER.                      */
    3238             : /************************************************************************/
    3239           0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
    3240             : {
    3241           0 :     return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
    3242             : }
    3243             : 
    3244             : /************************************************************************/
    3245             : /*                          createGEOSContext()                         */
    3246             : /************************************************************************/
    3247             : 
    3248             : /** Create a new GEOS context.
    3249             :  * @return a new GEOS context (to be freed with freeGEOSContext())
    3250             :  */
    3251       77803 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
    3252             : {
    3253             : #ifndef HAVE_GEOS
    3254             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3255             :     return nullptr;
    3256             : #else
    3257       77803 :     return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
    3258             : #endif
    3259             : }
    3260             : 
    3261             : /************************************************************************/
    3262             : /*                          freeGEOSContext()                           */
    3263             : /************************************************************************/
    3264             : 
    3265             : /** Destroy a GEOS context.
    3266             :  * @param hGEOSCtxt GEOS context
    3267             :  */
    3268       79746 : void OGRGeometry::freeGEOSContext(
    3269             :     UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt)
    3270             : {
    3271             : #ifdef HAVE_GEOS
    3272       79746 :     if (hGEOSCtxt != nullptr)
    3273             :     {
    3274       79699 :         finishGEOS_r(hGEOSCtxt);
    3275             :     }
    3276             : #endif
    3277       79788 : }
    3278             : 
    3279             : #ifdef HAVE_GEOS
    3280             : 
    3281             : /************************************************************************/
    3282             : /*                          convertToGEOSGeom()                         */
    3283             : /************************************************************************/
    3284             : 
    3285      230002 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
    3286             :                                   OGRGeometry *poGeom)
    3287             : {
    3288      230002 :     GEOSGeom hGeom = nullptr;
    3289      230002 :     const size_t nDataSize = poGeom->WkbSize();
    3290             :     unsigned char *pabyData =
    3291      230050 :         static_cast<unsigned char *>(CPLMalloc(nDataSize));
    3292             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    3293             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
    3294      230080 :     OGRwkbVariant eWkbVariant = wkbVariantIso;
    3295             : #else
    3296             :     OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
    3297             : #endif
    3298      230080 :     if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
    3299      229119 :         hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
    3300      230098 :     CPLFree(pabyData);
    3301      230118 :     return hGeom;
    3302             : }
    3303             : #endif
    3304             : 
    3305             : /************************************************************************/
    3306             : /*                            exportToGEOS()                            */
    3307             : /************************************************************************/
    3308             : 
    3309             : /** Returns a GEOSGeom object corresponding to the geometry.
    3310             :  *
    3311             :  * @param hGEOSCtxt GEOS context
    3312             :  * @param bRemoveEmptyParts Whether empty parts of the geometry should be
    3313             :  * removed before exporting to GEOS (GDAL >= 3.10)
    3314             :  * @return a GEOSGeom object corresponding to the geometry (to be freed with
    3315             :  * GEOSGeom_destroy_r()), or NULL in case of error
    3316             :  */
    3317             : GEOSGeom
    3318      230128 : OGRGeometry::exportToGEOS(UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt,
    3319             :                           UNUSED_IF_NO_GEOS bool bRemoveEmptyParts) const
    3320             : 
    3321             : {
    3322             : #ifndef HAVE_GEOS
    3323             : 
    3324             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3325             :     return nullptr;
    3326             : 
    3327             : #else
    3328             : 
    3329      230128 :     if (hGEOSCtxt == nullptr)
    3330           0 :         return nullptr;
    3331             : 
    3332      230128 :     const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
    3333             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3334             :     // POINT EMPTY is exported to WKB as if it were POINT(0 0),
    3335             :     // so that particular case is necessary.
    3336             :     if (eType == wkbPoint && IsEmpty())
    3337             :     {
    3338             :         return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
    3339             :     }
    3340             : #endif
    3341             : 
    3342      230086 :     GEOSGeom hGeom = nullptr;
    3343             : 
    3344      230086 :     OGRGeometry *poLinearGeom = nullptr;
    3345      230086 :     if (hasCurveGeometry())
    3346             :     {
    3347         858 :         poLinearGeom = getLinearGeometry();
    3348         858 :         if (bRemoveEmptyParts)
    3349           0 :             poLinearGeom->removeEmptyParts();
    3350             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3351             :         // GEOS < 3.12 doesn't support M dimension
    3352             :         if (poLinearGeom->IsMeasured())
    3353             :             poLinearGeom->setMeasured(FALSE);
    3354             : #endif
    3355             :     }
    3356             :     else
    3357             :     {
    3358      229217 :         poLinearGeom = const_cast<OGRGeometry *>(this);
    3359             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3360             :         // GEOS < 3.12 doesn't support M dimension
    3361             :         if (IsMeasured())
    3362             :         {
    3363             :             poLinearGeom = clone();
    3364             :             if (bRemoveEmptyParts)
    3365             :                 poLinearGeom->removeEmptyParts();
    3366             :             poLinearGeom->setMeasured(FALSE);
    3367             :         }
    3368             :         else
    3369             : #endif
    3370      229217 :             if (bRemoveEmptyParts && hasEmptyParts())
    3371             :         {
    3372           1 :             poLinearGeom = clone();
    3373           1 :             poLinearGeom->removeEmptyParts();
    3374             :         }
    3375             :     }
    3376      230050 :     if (eType == wkbTriangle)
    3377             :     {
    3378          97 :         OGRPolygon oPolygon(*(poLinearGeom->toPolygon()));
    3379          97 :         hGeom = convertToGEOSGeom(hGEOSCtxt, &oPolygon);
    3380             :     }
    3381      229953 :     else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
    3382             :     {
    3383        1680 :         OGRGeometry *poGC = OGRGeometryFactory::forceTo(
    3384         850 :             poLinearGeom->clone(),
    3385             :             OGR_GT_SetModifier(wkbGeometryCollection, poLinearGeom->Is3D(),
    3386             :                                poLinearGeom->IsMeasured()),
    3387             :             nullptr);
    3388         850 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGC);
    3389         850 :         delete poGC;
    3390             :     }
    3391      229123 :     else if (eType == wkbGeometryCollection)
    3392             :     {
    3393         134 :         bool bCanConvertToMultiPoly = true;
    3394             :         // bool bMustConvertToMultiPoly = true;
    3395             :         const OGRGeometryCollection *poGC =
    3396         134 :             poLinearGeom->toGeometryCollection();
    3397         311 :         for (int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++)
    3398             :         {
    3399             :             const OGRwkbGeometryType eSubGeomType =
    3400         250 :                 wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType());
    3401         250 :             if (eSubGeomType == wkbPolyhedralSurface || eSubGeomType == wkbTIN)
    3402             :             {
    3403             :                 // bMustConvertToMultiPoly = true;
    3404             :             }
    3405         165 :             else if (eSubGeomType != wkbMultiPolygon &&
    3406             :                      eSubGeomType != wkbPolygon)
    3407             :             {
    3408          73 :                 bCanConvertToMultiPoly = false;
    3409          73 :                 break;
    3410             :             }
    3411             :         }
    3412         134 :         if (bCanConvertToMultiPoly /* && bMustConvertToMultiPoly */)
    3413             :         {
    3414         122 :             OGRGeometry *poMultiPolygon = OGRGeometryFactory::forceTo(
    3415          61 :                 poLinearGeom->clone(),
    3416             :                 OGR_GT_SetModifier(wkbMultiPolygon, poLinearGeom->Is3D(),
    3417             :                                    poLinearGeom->IsMeasured()),
    3418             :                 nullptr);
    3419          61 :             OGRGeometry *poGCDest = OGRGeometryFactory::forceTo(
    3420             :                 poMultiPolygon,
    3421             :                 OGR_GT_SetModifier(wkbGeometryCollection, poLinearGeom->Is3D(),
    3422             :                                    poLinearGeom->IsMeasured()),
    3423             :                 nullptr);
    3424          61 :             hGeom = convertToGEOSGeom(hGEOSCtxt, poGCDest);
    3425          61 :             delete poGCDest;
    3426             :         }
    3427             :         else
    3428             :         {
    3429          73 :             hGeom = convertToGEOSGeom(hGEOSCtxt, poLinearGeom);
    3430             :         }
    3431             :     }
    3432             :     else
    3433             :     {
    3434      228989 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poLinearGeom);
    3435             :     }
    3436             : 
    3437      230085 :     if (poLinearGeom != this)
    3438         859 :         delete poLinearGeom;
    3439             : 
    3440      230021 :     return hGeom;
    3441             : 
    3442             : #endif  // HAVE_GEOS
    3443             : }
    3444             : 
    3445             : /************************************************************************/
    3446             : /*                         hasCurveGeometry()                           */
    3447             : /************************************************************************/
    3448             : 
    3449             : /**
    3450             :  * \brief Returns if this geometry is or has curve geometry.
    3451             :  *
    3452             :  * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
    3453             :  * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
    3454             :  *
    3455             :  * If bLookForNonLinear is set to TRUE, it will be actually looked if
    3456             :  * the geometry or its subgeometries are or contain a non-linear
    3457             :  * geometry in them. In which case, if the method returns TRUE, it
    3458             :  * means that getLinearGeometry() would return an approximate version
    3459             :  * of the geometry. Otherwise, getLinearGeometry() would do a
    3460             :  * conversion, but with just converting container type, like
    3461             :  * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
    3462             :  * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
    3463             :  * conversion.
    3464             :  *
    3465             :  * This method is the same as the C function OGR_G_HasCurveGeometry().
    3466             :  *
    3467             :  * @param bLookForNonLinear set it to TRUE to check if the geometry is
    3468             :  * or contains a CIRCULARSTRING.
    3469             :  *
    3470             :  * @return TRUE if this geometry is or has curve geometry.
    3471             :  *
    3472             :  * @since GDAL 2.0
    3473             :  */
    3474             : 
    3475      275484 : OGRBoolean OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
    3476             : {
    3477      275484 :     return FALSE;
    3478             : }
    3479             : 
    3480             : /************************************************************************/
    3481             : /*                         getLinearGeometry()                        */
    3482             : /************************************************************************/
    3483             : 
    3484             : /**
    3485             :  * \brief Return, possibly approximate, non-curve version of this geometry.
    3486             :  *
    3487             :  * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
    3488             :  * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
    3489             :  *
    3490             :  * The ownership of the returned geometry belongs to the caller.
    3491             :  *
    3492             :  * The reverse method is OGRGeometry::getCurveGeometry().
    3493             :  *
    3494             :  * This method is the same as the C function OGR_G_GetLinearGeometry().
    3495             :  *
    3496             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    3497             :  * arc, zero to use the default setting.
    3498             :  * @param papszOptions options as a null-terminated list of strings.
    3499             :  *                     See OGRGeometryFactory::curveToLineString() for
    3500             :  *                     valid options.
    3501             :  *
    3502             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3503             :  *
    3504             :  * @since GDAL 2.0
    3505             :  */
    3506             : 
    3507             : OGRGeometry *
    3508          79 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
    3509             :                                CPL_UNUSED const char *const *papszOptions) const
    3510             : {
    3511          79 :     return clone();
    3512             : }
    3513             : 
    3514             : /************************************************************************/
    3515             : /*                             getCurveGeometry()                       */
    3516             : /************************************************************************/
    3517             : 
    3518             : /**
    3519             :  * \brief Return curve version of this geometry.
    3520             :  *
    3521             :  * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
    3522             :  * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
    3523             :  * curve geometries.
    3524             :  *
    3525             :  * If the geometry has no curve portion, the returned geometry will be a clone
    3526             :  * of it.
    3527             :  *
    3528             :  * The ownership of the returned geometry belongs to the caller.
    3529             :  *
    3530             :  * The reverse method is OGRGeometry::getLinearGeometry().
    3531             :  *
    3532             :  * This function is the same as C function OGR_G_GetCurveGeometry().
    3533             :  *
    3534             :  * @param papszOptions options as a null-terminated list of strings.
    3535             :  *                     Unused for now. Must be set to NULL.
    3536             :  *
    3537             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3538             :  *
    3539             :  * @since GDAL 2.0
    3540             :  */
    3541             : 
    3542             : OGRGeometry *
    3543           5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
    3544             : {
    3545           5 :     return clone();
    3546             : }
    3547             : 
    3548             : /************************************************************************/
    3549             : /*                              Distance()                              */
    3550             : /************************************************************************/
    3551             : 
    3552             : /**
    3553             :  * \brief Compute distance between two geometries.
    3554             :  *
    3555             :  * Returns the shortest distance between the two geometries. The distance is
    3556             :  * expressed into the same unit as the coordinates of the geometries.
    3557             :  *
    3558             :  * This method is the same as the C function OGR_G_Distance().
    3559             :  *
    3560             :  * This method is built on the GEOS library, check it for the definition
    3561             :  * of the geometry operation.
    3562             :  * If OGR is built without the GEOS library, this method will always fail,
    3563             :  * issuing a CPLE_NotSupported error.
    3564             :  *
    3565             :  * @param poOtherGeom the other geometry to compare against.
    3566             :  *
    3567             :  * @return the distance between the geometries or -1 if an error occurs.
    3568             :  */
    3569             : 
    3570          25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
    3571             : 
    3572             : {
    3573          25 :     if (nullptr == poOtherGeom)
    3574             :     {
    3575           0 :         CPLDebug("OGR",
    3576             :                  "OGRGeometry::Distance called with NULL geometry pointer");
    3577           0 :         return -1.0;
    3578             :     }
    3579             : 
    3580          25 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    3581             :     {
    3582             : #ifndef HAVE_SFCGAL
    3583             : 
    3584           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3585           0 :         return -1.0;
    3586             : 
    3587             : #else
    3588             : 
    3589             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3590             :         if (poThis == nullptr)
    3591             :             return -1.0;
    3592             : 
    3593             :         sfcgal_geometry_t *poOther =
    3594             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3595             :         if (poOther == nullptr)
    3596             :         {
    3597             :             sfcgal_geometry_delete(poThis);
    3598             :             return -1.0;
    3599             :         }
    3600             : 
    3601             :         const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
    3602             : 
    3603             :         sfcgal_geometry_delete(poThis);
    3604             :         sfcgal_geometry_delete(poOther);
    3605             : 
    3606             :         return dfDistance > 0.0 ? dfDistance : -1.0;
    3607             : 
    3608             : #endif
    3609             :     }
    3610             : 
    3611             :     else
    3612             :     {
    3613             : #ifndef HAVE_GEOS
    3614             : 
    3615             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3616             :         return -1.0;
    3617             : 
    3618             : #else
    3619             : 
    3620          25 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    3621             :         // GEOSGeom is a pointer
    3622          25 :         GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3623          25 :         GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
    3624             : 
    3625          25 :         int bIsErr = 0;
    3626          25 :         double dfDistance = 0.0;
    3627             : 
    3628          25 :         if (hThis != nullptr && hOther != nullptr)
    3629             :         {
    3630          25 :             bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
    3631             :         }
    3632             : 
    3633          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hThis);
    3634          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hOther);
    3635          25 :         freeGEOSContext(hGEOSCtxt);
    3636             : 
    3637          25 :         if (bIsErr > 0)
    3638             :         {
    3639          25 :             return dfDistance;
    3640             :         }
    3641             : 
    3642             :         /* Calculations error */
    3643           0 :         return -1.0;
    3644             : 
    3645             : #endif /* HAVE_GEOS */
    3646             :     }
    3647             : }
    3648             : 
    3649             : /************************************************************************/
    3650             : /*                           OGR_G_Distance()                           */
    3651             : /************************************************************************/
    3652             : /**
    3653             :  * \brief Compute distance between two geometries.
    3654             :  *
    3655             :  * Returns the shortest distance between the two geometries. The distance is
    3656             :  * expressed into the same unit as the coordinates of the geometries.
    3657             :  *
    3658             :  * This function is the same as the C++ method OGRGeometry::Distance().
    3659             :  *
    3660             :  * This function is built on the GEOS library, check it for the definition
    3661             :  * of the geometry operation.
    3662             :  * If OGR is built without the GEOS library, this function will always fail,
    3663             :  * issuing a CPLE_NotSupported error.
    3664             :  *
    3665             :  * @param hFirst the first geometry to compare against.
    3666             :  * @param hOther the other geometry to compare against.
    3667             :  *
    3668             :  * @return the distance between the geometries or -1 if an error occurs.
    3669             :  */
    3670             : 
    3671           2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
    3672             : 
    3673             : {
    3674           2 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
    3675             : 
    3676           4 :     return OGRGeometry::FromHandle(hFirst)->Distance(
    3677           2 :         OGRGeometry::FromHandle(hOther));
    3678             : }
    3679             : 
    3680             : /************************************************************************/
    3681             : /*                             Distance3D()                             */
    3682             : /************************************************************************/
    3683             : 
    3684             : /**
    3685             :  * \brief Returns the 3D distance between two geometries
    3686             :  *
    3687             :  * The distance is expressed into the same unit as the coordinates of the
    3688             :  * geometries.
    3689             :  *
    3690             :  * This method is built on the SFCGAL library, check it for the definition
    3691             :  * of the geometry operation.
    3692             :  * If OGR is built without the SFCGAL library, this method will always return
    3693             :  * -1.0
    3694             :  *
    3695             :  * This function is the same as the C function OGR_G_Distance3D().
    3696             :  *
    3697             :  * @return distance between the two geometries
    3698             :  * @since GDAL 2.2
    3699             :  */
    3700             : 
    3701           1 : double OGRGeometry::Distance3D(
    3702             :     UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
    3703             : {
    3704           1 :     if (poOtherGeom == nullptr)
    3705             :     {
    3706           0 :         CPLDebug("OGR",
    3707             :                  "OGRTriangle::Distance3D called with NULL geometry pointer");
    3708           0 :         return -1.0;
    3709             :     }
    3710             : 
    3711           1 :     if (!(poOtherGeom->Is3D() && Is3D()))
    3712             :     {
    3713           0 :         CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
    3714             :                         "geometry(geometries)");
    3715           0 :         return -1.0;
    3716             :     }
    3717             : 
    3718             : #ifndef HAVE_SFCGAL
    3719             : 
    3720           1 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3721           1 :     return -1.0;
    3722             : 
    3723             : #else
    3724             : 
    3725             :     sfcgal_init();
    3726             :     sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3727             :     if (poThis == nullptr)
    3728             :         return -1.0;
    3729             : 
    3730             :     sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3731             :     if (poOther == nullptr)
    3732             :     {
    3733             :         sfcgal_geometry_delete(poThis);
    3734             :         return -1.0;
    3735             :     }
    3736             : 
    3737             :     const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
    3738             : 
    3739             :     sfcgal_geometry_delete(poThis);
    3740             :     sfcgal_geometry_delete(poOther);
    3741             : 
    3742             :     return dfDistance > 0 ? dfDistance : -1.0;
    3743             : 
    3744             : #endif
    3745             : }
    3746             : 
    3747             : /************************************************************************/
    3748             : /*                           OGR_G_Distance3D()                         */
    3749             : /************************************************************************/
    3750             : /**
    3751             :  * \brief Returns the 3D distance between two geometries
    3752             :  *
    3753             :  * The distance is expressed into the same unit as the coordinates of the
    3754             :  * geometries.
    3755             :  *
    3756             :  * This method is built on the SFCGAL library, check it for the definition
    3757             :  * of the geometry operation.
    3758             :  * If OGR is built without the SFCGAL library, this method will always return
    3759             :  * -1.0
    3760             :  *
    3761             :  * This function is the same as the C++ method OGRGeometry::Distance3D().
    3762             :  *
    3763             :  * @param hFirst the first geometry to compare against.
    3764             :  * @param hOther the other geometry to compare against.
    3765             :  * @return distance between the two geometries
    3766             :  * @since GDAL 2.2
    3767             :  *
    3768             :  * @return the distance between the geometries or -1 if an error occurs.
    3769             :  */
    3770             : 
    3771           1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
    3772             : 
    3773             : {
    3774           1 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
    3775             : 
    3776           2 :     return OGRGeometry::FromHandle(hFirst)->Distance3D(
    3777           1 :         OGRGeometry::FromHandle(hOther));
    3778             : }
    3779             : 
    3780             : /************************************************************************/
    3781             : /*                       OGRGeometryRebuildCurves()                     */
    3782             : /************************************************************************/
    3783             : 
    3784             : #ifdef HAVE_GEOS
    3785        3739 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
    3786             :                                              const OGRGeometry *poOtherGeom,
    3787             :                                              OGRGeometry *poOGRProduct)
    3788             : {
    3789        7478 :     if (poOGRProduct != nullptr &&
    3790        7402 :         wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
    3791        3663 :         (poGeom->hasCurveGeometry(true) ||
    3792        2645 :          (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
    3793             :     {
    3794           8 :         OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
    3795           8 :         delete poOGRProduct;
    3796           8 :         return poCurveGeom;
    3797             :     }
    3798        3731 :     return poOGRProduct;
    3799             : }
    3800             : 
    3801             : /************************************************************************/
    3802             : /*                       BuildGeometryFromGEOS()                        */
    3803             : /************************************************************************/
    3804             : 
    3805        3646 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
    3806             :                                           GEOSGeom hGeosProduct,
    3807             :                                           const OGRGeometry *poSelf,
    3808             :                                           const OGRGeometry *poOtherGeom)
    3809             : {
    3810        3646 :     OGRGeometry *poOGRProduct = nullptr;
    3811        3646 :     if (hGeosProduct != nullptr)
    3812             :     {
    3813             :         poOGRProduct =
    3814        3645 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
    3815        3645 :         if (poOGRProduct != nullptr &&
    3816        8938 :             poSelf->getSpatialReference() != nullptr &&
    3817        1648 :             (poOtherGeom == nullptr ||
    3818        1648 :              (poOtherGeom->getSpatialReference() != nullptr &&
    3819        1520 :               poOtherGeom->getSpatialReference()->IsSame(
    3820             :                   poSelf->getSpatialReference()))))
    3821             :         {
    3822        1546 :             poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
    3823             :         }
    3824             :         poOGRProduct =
    3825        3645 :             OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
    3826        3645 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
    3827             :     }
    3828        3646 :     return poOGRProduct;
    3829             : }
    3830             : 
    3831             : /************************************************************************/
    3832             : /*                     BuildGeometryFromTwoGeoms()                      */
    3833             : /************************************************************************/
    3834             : 
    3835        2717 : static OGRGeometry *BuildGeometryFromTwoGeoms(
    3836             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    3837             :     GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
    3838             :                                        const GEOSGeometry *,
    3839             :                                        const GEOSGeometry *))
    3840             : {
    3841        2717 :     OGRGeometry *poOGRProduct = nullptr;
    3842             : 
    3843        2717 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    3844        2717 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    3845        2717 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3846        2717 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    3847             :     {
    3848             :         GEOSGeom hGeosProduct =
    3849        2717 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
    3850             : 
    3851             :         poOGRProduct =
    3852        2717 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
    3853             :     }
    3854        2717 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    3855        2717 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    3856        2717 :     poSelf->freeGEOSContext(hGEOSCtxt);
    3857             : 
    3858        2717 :     return poOGRProduct;
    3859             : }
    3860             : 
    3861             : /************************************************************************/
    3862             : /*                       OGRGEOSBooleanPredicate()                      */
    3863             : /************************************************************************/
    3864             : 
    3865       21526 : static OGRBoolean OGRGEOSBooleanPredicate(
    3866             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    3867             :     char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
    3868             :                               const GEOSGeometry *))
    3869             : {
    3870       21526 :     OGRBoolean bResult = FALSE;
    3871             : 
    3872       21526 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    3873       21526 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    3874       21526 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3875       21526 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    3876             :     {
    3877       21015 :         bResult = pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
    3878             :     }
    3879       21526 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    3880       21526 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    3881       21526 :     poSelf->freeGEOSContext(hGEOSCtxt);
    3882             : 
    3883       21526 :     return bResult;
    3884             : }
    3885             : 
    3886             : #endif  // HAVE_GEOS
    3887             : 
    3888             : /************************************************************************/
    3889             : /*                            MakeValid()                               */
    3890             : /************************************************************************/
    3891             : 
    3892             : /**
    3893             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    3894             :  *
    3895             :  * Already-valid geometries are cloned without further intervention
    3896             :  * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
    3897             :  * may be subject to non-significant transformations, such as duplicated point
    3898             :  * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
    3899             :  * geometry collections could be returned a single geometry. GDAL 3.10
    3900             :  * returns the same type of geometry).
    3901             :  *
    3902             :  * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
    3903             :  * post-processing step is often desired.
    3904             :  *
    3905             :  * This method is the same as the C function OGR_G_MakeValid().
    3906             :  *
    3907             :  * This function is built on the GEOS >= 3.8 library, check it for the
    3908             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    3909             :  * library, this function will return a clone of the input geometry if it is
    3910             :  * valid, or NULL if it is invalid
    3911             :  *
    3912             :  * @param papszOptions NULL terminated list of options, or NULL. The following
    3913             :  * options are available:
    3914             :  * <ul>
    3915             :  * <li>METHOD=LINEWORK/STRUCTURE.
    3916             :  *     LINEWORK is the default method, which combines all rings into a set of
    3917             :  *     noded lines and then extracts valid polygons from that linework.
    3918             :  *     The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
    3919             :  *     all rings valid, then merges shells and
    3920             :  *     subtracts holes from shells to generate valid result. Assumes that
    3921             :  *     holes and shells are correctly categorized.</li>
    3922             :  * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
    3923             :  *     NO (default): collapses are converted to empty geometries
    3924             :  *     YES: collapses are converted to a valid geometry of lower dimension.</li>
    3925             :  * </ul>
    3926             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3927             :  *
    3928             :  * @since GDAL 3.0
    3929             :  */
    3930          96 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
    3931             : {
    3932             :     (void)papszOptions;
    3933             : #ifndef HAVE_GEOS
    3934             :     if (IsValid())
    3935             :         return clone();
    3936             : 
    3937             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3938             :     return nullptr;
    3939             : #else
    3940          96 :     if (IsSFCGALCompatible())
    3941             :     {
    3942           0 :         if (IsValid())
    3943           0 :             return clone();
    3944             :     }
    3945          96 :     else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
    3946             :     {
    3947           2 :         GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
    3948           2 :         OGRBoolean bIsValid = FALSE;
    3949           2 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    3950           2 :         if (hGeosGeom)
    3951             :         {
    3952           2 :             bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom);
    3953           2 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    3954             :         }
    3955           2 :         freeGEOSContext(hGEOSCtxt);
    3956           2 :         if (bIsValid)
    3957           1 :             return clone();
    3958             :     }
    3959             : 
    3960          95 :     const bool bStructureMethod = EQUAL(
    3961             :         CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
    3962          95 :     CPL_IGNORE_RET_VAL(bStructureMethod);
    3963             : #if !(GEOS_VERSION_MAJOR > 3 ||                                                \
    3964             :       (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    3965             :     if (bStructureMethod)
    3966             :     {
    3967             :         CPLError(CE_Failure, CPLE_NotSupported,
    3968             :                  "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
    3969             :         return nullptr;
    3970             :     }
    3971             : #endif
    3972             : 
    3973          95 :     OGRGeometry *poOGRProduct = nullptr;
    3974             : 
    3975          95 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    3976          95 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    3977          95 :     if (hGeosGeom != nullptr)
    3978             :     {
    3979             :         GEOSGeom hGEOSRet;
    3980             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    3981             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    3982          94 :         if (bStructureMethod)
    3983             :         {
    3984             :             GEOSMakeValidParams *params =
    3985           7 :                 GEOSMakeValidParams_create_r(hGEOSCtxt);
    3986           7 :             CPLAssert(params);
    3987           7 :             GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
    3988             :                                             GEOS_MAKE_VALID_STRUCTURE);
    3989           7 :             GEOSMakeValidParams_setKeepCollapsed_r(
    3990             :                 hGEOSCtxt, params,
    3991           7 :                 CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
    3992           7 :             hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
    3993           7 :             GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
    3994             :         }
    3995             :         else
    3996             : #endif
    3997             :         {
    3998          87 :             hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
    3999             :         }
    4000          94 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4001             : 
    4002          94 :         if (hGEOSRet != nullptr)
    4003             :         {
    4004             :             poOGRProduct =
    4005          94 :                 OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
    4006          94 :             if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
    4007           6 :                 poOGRProduct->assignSpatialReference(getSpatialReference());
    4008             :             poOGRProduct =
    4009          94 :                 OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
    4010          94 :             GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
    4011             : 
    4012             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4013             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4014             :             // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
    4015             :             // if the input is a multiple geometry
    4016          94 :             if (poOGRProduct && bStructureMethod &&
    4017         192 :                 OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
    4018           4 :                 !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
    4019             :                                      wkbGeometryCollection))
    4020             :             {
    4021           3 :                 poOGRProduct = OGRGeometryFactory::forceTo(poOGRProduct,
    4022           3 :                                                            getGeometryType());
    4023             :             }
    4024             : #endif
    4025             :         }
    4026             :     }
    4027          95 :     freeGEOSContext(hGEOSCtxt);
    4028             : 
    4029          95 :     return poOGRProduct;
    4030             : #endif
    4031             : }
    4032             : 
    4033             : /************************************************************************/
    4034             : /*                         OGR_G_MakeValid()                            */
    4035             : /************************************************************************/
    4036             : 
    4037             : /**
    4038             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4039             :  *
    4040             :  * Already-valid geometries are cloned without further intervention.
    4041             :  *
    4042             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4043             :  *
    4044             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4045             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4046             :  * library, this function will return a clone of the input geometry if it is
    4047             :  * valid, or NULL if it is invalid
    4048             :  *
    4049             :  * @param hGeom The Geometry to make valid.
    4050             :  *
    4051             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4052             :  * or NULL if an error occurs.
    4053             :  *
    4054             :  * @since GDAL 3.0
    4055             :  */
    4056             : 
    4057           0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
    4058             : 
    4059             : {
    4060           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
    4061             : 
    4062           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
    4063             : }
    4064             : 
    4065             : /************************************************************************/
    4066             : /*                         OGR_G_MakeValidEx()                            */
    4067             : /************************************************************************/
    4068             : 
    4069             : /**
    4070             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4071             :  *
    4072             :  * Already-valid geometries are cloned without further intervention.
    4073             :  *
    4074             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4075             :  *
    4076             :  * See documentation of that method for possible options.
    4077             :  *
    4078             :  * @param hGeom The Geometry to make valid.
    4079             :  * @param papszOptions Options.
    4080             :  *
    4081             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4082             :  * or NULL if an error occurs.
    4083             :  *
    4084             :  * @since GDAL 3.4
    4085             :  */
    4086             : 
    4087          11 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
    4088             : 
    4089             : {
    4090          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
    4091             : 
    4092          11 :     return OGRGeometry::ToHandle(
    4093          22 :         OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
    4094             : }
    4095             : 
    4096             : /************************************************************************/
    4097             : /*                            Normalize()                               */
    4098             : /************************************************************************/
    4099             : 
    4100             : /**
    4101             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4102             :  *
    4103             :  * This method is the same as the C function OGR_G_Normalize().
    4104             :  *
    4105             :  * This function is built on the GEOS library; check it for the definition
    4106             :  * of the geometry operation.
    4107             :  * If OGR is built without the GEOS library, this function will always fail,
    4108             :  * issuing a CPLE_NotSupported error.
    4109             :  *
    4110             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4111             :  *
    4112             :  * @since GDAL 3.3
    4113             :  */
    4114          35 : OGRGeometry *OGRGeometry::Normalize() const
    4115             : {
    4116             : #ifndef HAVE_GEOS
    4117             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4118             :     return nullptr;
    4119             : #else
    4120          35 :     OGRGeometry *poOGRProduct = nullptr;
    4121             : 
    4122          35 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4123          35 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4124          35 :     if (hGeosGeom != nullptr)
    4125             :     {
    4126             : 
    4127          35 :         int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
    4128             : 
    4129          35 :         if (hGEOSRet == 0)
    4130             :         {
    4131             :             poOGRProduct =
    4132          35 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
    4133             :         }
    4134             :         else
    4135             :         {
    4136           0 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4137             :         }
    4138             :     }
    4139          35 :     freeGEOSContext(hGEOSCtxt);
    4140             : 
    4141          35 :     return poOGRProduct;
    4142             : #endif
    4143             : }
    4144             : 
    4145             : /************************************************************************/
    4146             : /*                         OGR_G_Normalize()                            */
    4147             : /************************************************************************/
    4148             : 
    4149             : /**
    4150             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4151             :  *
    4152             :  * This function is the same as the C++ method OGRGeometry::Normalize().
    4153             :  *
    4154             :  * This function is built on the GEOS library; check it for the definition
    4155             :  * of the geometry operation.
    4156             :  * If OGR is built without the GEOS library, this function will always fail,
    4157             :  * issuing a CPLE_NotSupported error.
    4158             :  * @param hGeom The Geometry to normalize.
    4159             :  *
    4160             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4161             :  * or NULL if an error occurs.
    4162             :  *
    4163             :  * @since GDAL 3.3
    4164             :  */
    4165             : 
    4166           5 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
    4167             : 
    4168             : {
    4169           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
    4170             : 
    4171           5 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
    4172             : }
    4173             : 
    4174             : /************************************************************************/
    4175             : /*                             ConvexHull()                             */
    4176             : /************************************************************************/
    4177             : 
    4178             : /**
    4179             :  * \brief Compute convex hull.
    4180             :  *
    4181             :  * A new geometry object is created and returned containing the convex
    4182             :  * hull of the geometry on which the method is invoked.
    4183             :  *
    4184             :  * This method is the same as the C function OGR_G_ConvexHull().
    4185             :  *
    4186             :  * This method is built on the GEOS library, check it for the definition
    4187             :  * of the geometry operation.
    4188             :  * If OGR is built without the GEOS library, this method will always fail,
    4189             :  * issuing a CPLE_NotSupported error.
    4190             :  *
    4191             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4192             :  */
    4193             : 
    4194           3 : OGRGeometry *OGRGeometry::ConvexHull() const
    4195             : 
    4196             : {
    4197           3 :     if (IsSFCGALCompatible())
    4198             :     {
    4199             : #ifndef HAVE_SFCGAL
    4200             : 
    4201           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    4202           0 :         return nullptr;
    4203             : 
    4204             : #else
    4205             : 
    4206             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    4207             :         if (poThis == nullptr)
    4208             :             return nullptr;
    4209             : 
    4210             :         sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
    4211             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    4212             :         if (h_prodGeom)
    4213             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    4214             : 
    4215             :         sfcgal_geometry_delete(poThis);
    4216             :         sfcgal_geometry_delete(poRes);
    4217             : 
    4218             :         return h_prodGeom;
    4219             : 
    4220             : #endif
    4221             :     }
    4222             : 
    4223             :     else
    4224             :     {
    4225             : #ifndef HAVE_GEOS
    4226             : 
    4227             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4228             :         return nullptr;
    4229             : 
    4230             : #else
    4231             : 
    4232           3 :         OGRGeometry *poOGRProduct = nullptr;
    4233             : 
    4234           3 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4235           3 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4236           3 :         if (hGeosGeom != nullptr)
    4237             :         {
    4238           3 :             GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
    4239           3 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4240             : 
    4241             :             poOGRProduct =
    4242           3 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4243             :         }
    4244           3 :         freeGEOSContext(hGEOSCtxt);
    4245             : 
    4246           3 :         return poOGRProduct;
    4247             : 
    4248             : #endif /* HAVE_GEOS */
    4249             :     }
    4250             : }
    4251             : 
    4252             : /************************************************************************/
    4253             : /*                          OGR_G_ConvexHull()                          */
    4254             : /************************************************************************/
    4255             : /**
    4256             :  * \brief Compute convex hull.
    4257             :  *
    4258             :  * A new geometry object is created and returned containing the convex
    4259             :  * hull of the geometry on which the method is invoked.
    4260             :  *
    4261             :  * This function is the same as the C++ method OGRGeometry::ConvexHull().
    4262             :  *
    4263             :  * This function is built on the GEOS library, check it for the definition
    4264             :  * of the geometry operation.
    4265             :  * If OGR is built without the GEOS library, this function will always fail,
    4266             :  * issuing a CPLE_NotSupported error.
    4267             :  *
    4268             :  * @param hTarget The Geometry to calculate the convex hull of.
    4269             :  *
    4270             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4271             :  * or NULL if an error occurs.
    4272             :  */
    4273             : 
    4274           1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
    4275             : 
    4276             : {
    4277           1 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
    4278             : 
    4279           1 :     return OGRGeometry::ToHandle(
    4280           2 :         OGRGeometry::FromHandle(hTarget)->ConvexHull());
    4281             : }
    4282             : 
    4283             : /************************************************************************/
    4284             : /*                             ConcaveHull()                            */
    4285             : /************************************************************************/
    4286             : 
    4287             : /**
    4288             :  * \brief Compute "concave hull" of a geometry.
    4289             :  *
    4290             :  * The concave hull is fully contained within the convex hull and also
    4291             :  * contains all the points of the input, but in a smaller area.
    4292             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4293             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4294             :  * that contains all the points in the input Geometry.
    4295             :  *
    4296             :  * A new geometry object is created and returned containing the concave
    4297             :  * hull of the geometry on which the method is invoked.
    4298             :  *
    4299             :  * This method is the same as the C function OGR_G_ConcaveHull().
    4300             :  *
    4301             :  * This method is built on the GEOS >= 3.11 library
    4302             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4303             :  * fail, issuing a CPLE_NotSupported error.
    4304             :  *
    4305             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4306             :  * @param bAllowHoles Whether holes are allowed.
    4307             :  *
    4308             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4309             :  *
    4310             :  * @since GDAL 3.6
    4311             :  */
    4312             : 
    4313           2 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
    4314             : {
    4315             : #ifndef HAVE_GEOS
    4316             :     (void)dfRatio;
    4317             :     (void)bAllowHoles;
    4318             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4319             :     return nullptr;
    4320             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4321             :     (void)dfRatio;
    4322             :     (void)bAllowHoles;
    4323             :     CPLError(CE_Failure, CPLE_NotSupported,
    4324             :              "GEOS 3.11 or later needed for ConcaveHull.");
    4325             :     return nullptr;
    4326             : #else
    4327           2 :     OGRGeometry *poOGRProduct = nullptr;
    4328             : 
    4329           2 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4330           2 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4331           2 :     if (hGeosGeom != nullptr)
    4332             :     {
    4333             :         GEOSGeom hGeosHull =
    4334           2 :             GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
    4335           2 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4336             : 
    4337             :         poOGRProduct =
    4338           2 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4339             :     }
    4340           2 :     freeGEOSContext(hGEOSCtxt);
    4341             : 
    4342           2 :     return poOGRProduct;
    4343             : #endif /* HAVE_GEOS */
    4344             : }
    4345             : 
    4346             : /************************************************************************/
    4347             : /*                          OGR_G_ConcaveHull()                         */
    4348             : /************************************************************************/
    4349             : /**
    4350             :  * \brief Compute "concave hull" of a geometry.
    4351             :  *
    4352             :  * The concave hull is fully contained within the convex hull and also
    4353             :  * contains all the points of the input, but in a smaller area.
    4354             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4355             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4356             :  * that contains all the points in the input Geometry.
    4357             :  *
    4358             :  * A new geometry object is created and returned containing the convex
    4359             :  * hull of the geometry on which the function is invoked.
    4360             :  *
    4361             :  * This function is the same as the C++ method OGRGeometry::ConcaveHull().
    4362             :  *
    4363             :  * This function is built on the GEOS >= 3.11 library
    4364             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4365             :  * fail, issuing a CPLE_NotSupported error.
    4366             :  *
    4367             :  * @param hTarget The Geometry to calculate the concave hull of.
    4368             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4369             :  * @param bAllowHoles Whether holes are allowed.
    4370             :  *
    4371             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4372             :  * or NULL if an error occurs.
    4373             :  *
    4374             :  * @since GDAL 3.6
    4375             :  */
    4376             : 
    4377           2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
    4378             :                                bool bAllowHoles)
    4379             : 
    4380             : {
    4381           2 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
    4382             : 
    4383           2 :     return OGRGeometry::ToHandle(
    4384           4 :         OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
    4385             : }
    4386             : 
    4387             : /************************************************************************/
    4388             : /*                            Boundary()                                */
    4389             : /************************************************************************/
    4390             : 
    4391             : /**
    4392             :  * \brief Compute boundary.
    4393             :  *
    4394             :  * A new geometry object is created and returned containing the boundary
    4395             :  * of the geometry on which the method is invoked.
    4396             :  *
    4397             :  * This method is the same as the C function OGR_G_Boundary().
    4398             :  *
    4399             :  * This method is built on the GEOS library, check it for the definition
    4400             :  * of the geometry operation.
    4401             :  * If OGR is built without the GEOS library, this method will always fail,
    4402             :  * issuing a CPLE_NotSupported error.
    4403             :  *
    4404             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4405             :  *
    4406             :  * @since OGR 1.8.0
    4407             :  */
    4408             : 
    4409           6 : OGRGeometry *OGRGeometry::Boundary() const
    4410             : 
    4411             : {
    4412             : #ifndef HAVE_GEOS
    4413             : 
    4414             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4415             :     return nullptr;
    4416             : 
    4417             : #else
    4418             : 
    4419           6 :     OGRGeometry *poOGRProduct = nullptr;
    4420             : 
    4421           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4422           6 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4423           6 :     if (hGeosGeom != nullptr)
    4424             :     {
    4425           6 :         GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
    4426           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4427             : 
    4428             :         poOGRProduct =
    4429           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4430             :     }
    4431           6 :     freeGEOSContext(hGEOSCtxt);
    4432             : 
    4433           6 :     return poOGRProduct;
    4434             : 
    4435             : #endif  // HAVE_GEOS
    4436             : }
    4437             : 
    4438             : //! @cond Doxygen_Suppress
    4439             : /**
    4440             :  * \brief Compute boundary (deprecated)
    4441             :  *
    4442             :  * @deprecated
    4443             :  *
    4444             :  * @see Boundary()
    4445             :  */
    4446           0 : OGRGeometry *OGRGeometry::getBoundary() const
    4447             : 
    4448             : {
    4449           0 :     return Boundary();
    4450             : }
    4451             : 
    4452             : //! @endcond
    4453             : 
    4454             : /************************************************************************/
    4455             : /*                         OGR_G_Boundary()                             */
    4456             : /************************************************************************/
    4457             : /**
    4458             :  * \brief Compute boundary.
    4459             :  *
    4460             :  * A new geometry object is created and returned containing the boundary
    4461             :  * of the geometry on which the method is invoked.
    4462             :  *
    4463             :  * This function is the same as the C++ method OGR_G_Boundary().
    4464             :  *
    4465             :  * This function is built on the GEOS library, check it for the definition
    4466             :  * of the geometry operation.
    4467             :  * If OGR is built without the GEOS library, this function will always fail,
    4468             :  * issuing a CPLE_NotSupported error.
    4469             :  *
    4470             :  * @param hTarget The Geometry to calculate the boundary of.
    4471             :  *
    4472             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4473             :  * or NULL if an error occurs.
    4474             :  *
    4475             :  * @since OGR 1.8.0
    4476             :  */
    4477           6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
    4478             : 
    4479             : {
    4480           6 :     VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
    4481             : 
    4482           6 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4483             : }
    4484             : 
    4485             : /**
    4486             :  * \brief Compute boundary (deprecated)
    4487             :  *
    4488             :  * @deprecated
    4489             :  *
    4490             :  * @see OGR_G_Boundary()
    4491             :  */
    4492           0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
    4493             : 
    4494             : {
    4495           0 :     VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
    4496             : 
    4497           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4498             : }
    4499             : 
    4500             : /************************************************************************/
    4501             : /*                               Buffer()                               */
    4502             : /************************************************************************/
    4503             : 
    4504             : /**
    4505             :  * \brief Compute buffer of geometry.
    4506             :  *
    4507             :  * Builds a new geometry containing the buffer region around the geometry
    4508             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4509             :  * the buffer distance of the original geometry.
    4510             :  *
    4511             :  * Some buffer sections are properly described as curves, but are converted to
    4512             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4513             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4514             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4515             :  * large numbers of vertices in the resulting buffer geometry while small
    4516             :  * numbers reduce the accuracy of the result.
    4517             :  *
    4518             :  * This method is the same as the C function OGR_G_Buffer().
    4519             :  *
    4520             :  * This method is built on the GEOS library, check it for the definition
    4521             :  * of the geometry operation.
    4522             :  * If OGR is built without the GEOS library, this method will always fail,
    4523             :  * issuing a CPLE_NotSupported error.
    4524             :  *
    4525             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4526             :  *               the same unit as the coordinates of the geometry.
    4527             :  *
    4528             :  * @param nQuadSegs the number of segments used to approximate a 90
    4529             :  * degree (quadrant) of curvature.
    4530             :  *
    4531             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4532             :  */
    4533             : 
    4534          38 : OGRGeometry *OGRGeometry::Buffer(UNUSED_IF_NO_GEOS double dfDist,
    4535             :                                  UNUSED_IF_NO_GEOS int nQuadSegs) const
    4536             : 
    4537             : {
    4538             : #ifndef HAVE_GEOS
    4539             : 
    4540             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4541             :     return nullptr;
    4542             : 
    4543             : #else
    4544             : 
    4545          38 :     OGRGeometry *poOGRProduct = nullptr;
    4546             : 
    4547          38 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4548          38 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4549          38 :     if (hGeosGeom != nullptr)
    4550             :     {
    4551             :         GEOSGeom hGeosProduct =
    4552          38 :             GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
    4553          38 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4554             : 
    4555             :         poOGRProduct =
    4556          38 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4557             :     }
    4558          38 :     freeGEOSContext(hGEOSCtxt);
    4559             : 
    4560          38 :     return poOGRProduct;
    4561             : 
    4562             : #endif  // HAVE_GEOS
    4563             : }
    4564             : 
    4565             : /************************************************************************/
    4566             : /*                            OGR_G_Buffer()                            */
    4567             : /************************************************************************/
    4568             : 
    4569             : /**
    4570             :  * \brief Compute buffer of geometry.
    4571             :  *
    4572             :  * Builds a new geometry containing the buffer region around the geometry
    4573             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4574             :  * the buffer distance of the original geometry.
    4575             :  *
    4576             :  * Some buffer sections are properly described as curves, but are converted to
    4577             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4578             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4579             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4580             :  * large numbers of vertices in the resulting buffer geometry while small
    4581             :  * numbers reduce the accuracy of the result.
    4582             :  *
    4583             :  * This function is the same as the C++ method OGRGeometry::Buffer().
    4584             :  *
    4585             :  * This function is built on the GEOS library, check it for the definition
    4586             :  * of the geometry operation.
    4587             :  * If OGR is built without the GEOS library, this function will always fail,
    4588             :  * issuing a CPLE_NotSupported error.
    4589             :  *
    4590             :  * @param hTarget the geometry.
    4591             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4592             :  *               the same unit as the coordinates of the geometry.
    4593             :  *
    4594             :  * @param nQuadSegs the number of segments used to approximate a 90 degree
    4595             :  * (quadrant) of curvature.
    4596             :  *
    4597             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4598             :  * or NULL if an error occurs.
    4599             :  */
    4600             : 
    4601          38 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
    4602             : 
    4603             : {
    4604          38 :     VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
    4605             : 
    4606          38 :     return OGRGeometry::ToHandle(
    4607          76 :         OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
    4608             : }
    4609             : 
    4610             : /**
    4611             :  * \brief Compute buffer of geometry.
    4612             :  *
    4613             :  * Builds a new geometry containing the buffer region around the geometry
    4614             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4615             :  * the buffer distance of the original geometry.
    4616             :  *
    4617             :  * This function is built on the GEOS library, check it for the definition
    4618             :  * of the geometry operation.
    4619             :  * If OGR is built without the GEOS library, this function will always fail,
    4620             :  * issuing a CPLE_NotSupported error.
    4621             :  *
    4622             :  * The following options are supported. See the GEOS library for more detailed
    4623             :  * descriptions.
    4624             :  *
    4625             :  * <ul>
    4626             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    4627             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    4628             :  * <li>MITRE_LIMIT=double</li>
    4629             :  * <li>QUADRANT_SEGMENTS=int</li>
    4630             :  * <li>SINGLE_SIDED=YES/NO</li>
    4631             :  * </ul>
    4632             :  *
    4633             :  * This function is the same as the C function OGR_G_BufferEx().
    4634             :  *
    4635             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4636             :  *               the same unit as the coordinates of the geometry.
    4637             :  * @param papszOptions NULL terminated list of options (may be NULL)
    4638             :  *
    4639             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4640             :  *
    4641             :  * @since GDAL 3.10
    4642             :  */
    4643             : 
    4644             : OGRGeometry *
    4645          19 : OGRGeometry::BufferEx(UNUSED_IF_NO_GEOS double dfDist,
    4646             :                       UNUSED_IF_NO_GEOS CSLConstList papszOptions) const
    4647             : {
    4648             : #ifndef HAVE_GEOS
    4649             : 
    4650             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4651             :     return nullptr;
    4652             : 
    4653             : #else
    4654          19 :     OGRGeometry *poOGRProduct = nullptr;
    4655          19 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4656             : 
    4657          19 :     auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
    4658          19 :     bool bParamsAreValid = true;
    4659             : 
    4660          70 :     for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
    4661             :     {
    4662          51 :         if (EQUAL(pszParam, "ENDCAP_STYLE"))
    4663             :         {
    4664             :             int nStyle;
    4665           9 :             if (EQUAL(pszValue, "ROUND"))
    4666             :             {
    4667           6 :                 nStyle = GEOSBUF_CAP_ROUND;
    4668             :             }
    4669           3 :             else if (EQUAL(pszValue, "FLAT"))
    4670             :             {
    4671           1 :                 nStyle = GEOSBUF_CAP_FLAT;
    4672             :             }
    4673           2 :             else if (EQUAL(pszValue, "SQUARE"))
    4674             :             {
    4675           1 :                 nStyle = GEOSBUF_CAP_SQUARE;
    4676             :             }
    4677             :             else
    4678             :             {
    4679           1 :                 bParamsAreValid = false;
    4680           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    4681             :                          "Invalid value for ENDCAP_STYLE: %s", pszValue);
    4682           2 :                 break;
    4683             :             }
    4684             : 
    4685           8 :             if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
    4686             :             {
    4687           0 :                 bParamsAreValid = false;
    4688             :             }
    4689             :         }
    4690          42 :         else if (EQUAL(pszParam, "JOIN_STYLE"))
    4691             :         {
    4692             :             int nStyle;
    4693           9 :             if (EQUAL(pszValue, "ROUND"))
    4694             :             {
    4695           5 :                 nStyle = GEOSBUF_JOIN_ROUND;
    4696             :             }
    4697           4 :             else if (EQUAL(pszValue, "MITRE"))
    4698             :             {
    4699           3 :                 nStyle = GEOSBUF_JOIN_MITRE;
    4700             :             }
    4701           1 :             else if (EQUAL(pszValue, "BEVEL"))
    4702             :             {
    4703           0 :                 nStyle = GEOSBUF_JOIN_BEVEL;
    4704             :             }
    4705             :             else
    4706             :             {
    4707           1 :                 bParamsAreValid = false;
    4708           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    4709             :                          "Invalid value for JOIN_STYLE: %s", pszValue);
    4710           1 :                 break;
    4711             :             }
    4712             : 
    4713           8 :             if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
    4714             :             {
    4715           0 :                 bParamsAreValid = false;
    4716           0 :                 break;
    4717             :             }
    4718             :         }
    4719          33 :         else if (EQUAL(pszParam, "MITRE_LIMIT"))
    4720             :         {
    4721             :             try
    4722             :             {
    4723             :                 std::size_t end;
    4724          14 :                 double dfLimit = std::stod(pszValue, &end);
    4725             : 
    4726           8 :                 if (end != strlen(pszValue))
    4727             :                 {
    4728           0 :                     throw std::invalid_argument("");
    4729             :                 }
    4730             : 
    4731           8 :                 if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
    4732             :                                                       dfLimit))
    4733             :                 {
    4734           0 :                     bParamsAreValid = false;
    4735           0 :                     break;
    4736             :                 }
    4737             :             }
    4738           4 :             catch (const std::invalid_argument &)
    4739             :             {
    4740           2 :                 bParamsAreValid = false;
    4741           2 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    4742             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    4743             :             }
    4744           0 :             catch (const std::out_of_range &)
    4745             :             {
    4746           0 :                 bParamsAreValid = false;
    4747           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    4748             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    4749             :             }
    4750             :         }
    4751          23 :         else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
    4752             :         {
    4753             :             try
    4754             :             {
    4755             :                 std::size_t end;
    4756          22 :                 int nQuadSegs = std::stoi(pszValue, &end, 10);
    4757             : 
    4758          10 :                 if (end != strlen(pszValue))
    4759             :                 {
    4760           0 :                     throw std::invalid_argument("");
    4761             :                 }
    4762             : 
    4763          10 :                 if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
    4764             :                                                             nQuadSegs))
    4765             :                 {
    4766           0 :                     bParamsAreValid = false;
    4767           0 :                     break;
    4768             :                 }
    4769             :             }
    4770           6 :             catch (const std::invalid_argument &)
    4771             :             {
    4772           3 :                 bParamsAreValid = false;
    4773           3 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    4774             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    4775             :             }
    4776           2 :             catch (const std::out_of_range &)
    4777             :             {
    4778           1 :                 bParamsAreValid = false;
    4779           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    4780             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    4781             :             }
    4782             :         }
    4783           9 :         else if (EQUAL(pszParam, "SINGLE_SIDED"))
    4784             :         {
    4785           8 :             bool bSingleSided = CPLTestBool(pszValue);
    4786             : 
    4787           8 :             if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
    4788             :                                                    bSingleSided))
    4789             :             {
    4790           0 :                 bParamsAreValid = false;
    4791           0 :                 break;
    4792             :             }
    4793             :         }
    4794             :         else
    4795             :         {
    4796           1 :             bParamsAreValid = false;
    4797           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    4798             :                      "Unsupported buffer option: %s", pszValue);
    4799             :         }
    4800             :     }
    4801             : 
    4802          19 :     if (bParamsAreValid)
    4803             :     {
    4804          10 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4805          10 :         if (hGeosGeom != nullptr)
    4806             :         {
    4807             :             GEOSGeom hGeosProduct =
    4808          10 :                 GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
    4809          10 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4810             : 
    4811          10 :             if (hGeosProduct != nullptr)
    4812             :             {
    4813          10 :                 poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
    4814             :                                                      this, nullptr);
    4815             :             }
    4816             :         }
    4817             :     }
    4818             : 
    4819          19 :     GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
    4820          19 :     freeGEOSContext(hGEOSCtxt);
    4821          19 :     return poOGRProduct;
    4822             : #endif
    4823             : }
    4824             : 
    4825             : /**
    4826             :  * \brief Compute buffer of geometry.
    4827             :  *
    4828             :  * Builds a new geometry containing the buffer region around the geometry
    4829             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4830             :  * the buffer distance of the original geometry.
    4831             :  *
    4832             :  * This function is built on the GEOS library, check it for the definition
    4833             :  * of the geometry operation.
    4834             :  * If OGR is built without the GEOS library, this function will always fail,
    4835             :  * issuing a CPLE_NotSupported error.
    4836             :  *
    4837             :  * The following options are supported. See the GEOS library for more detailed
    4838             :  * descriptions.
    4839             :  *
    4840             :  * <ul>
    4841             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    4842             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    4843             :  * <li>MITRE_LIMIT=double</li>
    4844             :  * <li>QUADRANT_SEGMENTS=int</li>
    4845             :  * <li>SINGLE_SIDED=YES/NO</li>
    4846             :  * </ul>
    4847             :  *
    4848             :  * This function is the same as the C++ method OGRGeometry::BufferEx().
    4849             :  *
    4850             :  * @param hTarget the geometry.
    4851             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4852             :  *               the same unit as the coordinates of the geometry.
    4853             :  * @param papszOptions NULL terminated list of options (may be NULL)
    4854             :  *
    4855             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4856             :  * or NULL if an error occurs.
    4857             :  *
    4858             :  * @since GDAL 3.10
    4859             :  */
    4860             : 
    4861          12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
    4862             :                             CSLConstList papszOptions)
    4863             : 
    4864             : {
    4865          12 :     VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
    4866             : 
    4867          12 :     return OGRGeometry::ToHandle(
    4868          24 :         OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
    4869             : }
    4870             : 
    4871             : /************************************************************************/
    4872             : /*                            Intersection()                            */
    4873             : /************************************************************************/
    4874             : 
    4875             : /**
    4876             :  * \brief Compute intersection.
    4877             :  *
    4878             :  * Generates a new geometry which is the region of intersection of the
    4879             :  * two geometries operated on.  The Intersects() method can be used to test if
    4880             :  * two geometries intersect.
    4881             :  *
    4882             :  * Geometry validity is not checked. In case you are unsure of the validity
    4883             :  * of the input geometries, call IsValid() before, otherwise the result might
    4884             :  * be wrong.
    4885             :  *
    4886             :  * This method is the same as the C function OGR_G_Intersection().
    4887             :  *
    4888             :  * This method is built on the GEOS library, check it for the definition
    4889             :  * of the geometry operation.
    4890             :  * If OGR is built without the GEOS library, this method will always fail,
    4891             :  * issuing a CPLE_NotSupported error.
    4892             :  *
    4893             :  * @param poOtherGeom the other geometry intersected with "this" geometry.
    4894             :  *
    4895             :  * @return a new geometry to be freed by the caller, or NULL if there is no
    4896             :  * intersection or if an error occurs.
    4897             :  *
    4898             :  */
    4899             : 
    4900             : OGRGeometry *
    4901        1896 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    4902             : 
    4903             : {
    4904        1896 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    4905             :     {
    4906             : #ifndef HAVE_SFCGAL
    4907             : 
    4908           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    4909           0 :         return nullptr;
    4910             : 
    4911             : #else
    4912             : 
    4913             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    4914             :         if (poThis == nullptr)
    4915             :             return nullptr;
    4916             : 
    4917             :         sfcgal_geometry_t *poOther =
    4918             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    4919             :         if (poOther == nullptr)
    4920             :         {
    4921             :             sfcgal_geometry_delete(poThis);
    4922             :             return nullptr;
    4923             :         }
    4924             : 
    4925             :         sfcgal_geometry_t *poRes =
    4926             :             sfcgal_geometry_intersection_3d(poThis, poOther);
    4927             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    4928             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    4929             :             poOtherGeom->getSpatialReference() != nullptr &&
    4930             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    4931             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    4932             : 
    4933             :         sfcgal_geometry_delete(poThis);
    4934             :         sfcgal_geometry_delete(poOther);
    4935             :         sfcgal_geometry_delete(poRes);
    4936             : 
    4937             :         return h_prodGeom;
    4938             : 
    4939             : #endif
    4940             :     }
    4941             : 
    4942             :     else
    4943             :     {
    4944             : #ifndef HAVE_GEOS
    4945             : 
    4946             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4947             :         return nullptr;
    4948             : 
    4949             : #else
    4950        1896 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
    4951             : #endif /* HAVE_GEOS */
    4952             :     }
    4953             : }
    4954             : 
    4955             : /************************************************************************/
    4956             : /*                         OGR_G_Intersection()                         */
    4957             : /************************************************************************/
    4958             : 
    4959             : /**
    4960             :  * \brief Compute intersection.
    4961             :  *
    4962             :  * Generates a new geometry which is the region of intersection of the
    4963             :  * two geometries operated on.  The OGR_G_Intersects() function can be used to
    4964             :  * test if two geometries intersect.
    4965             :  *
    4966             :  * Geometry validity is not checked. In case you are unsure of the validity
    4967             :  * of the input geometries, call IsValid() before, otherwise the result might
    4968             :  * be wrong.
    4969             :  *
    4970             :  * This function is the same as the C++ method OGRGeometry::Intersection().
    4971             :  *
    4972             :  * This function is built on the GEOS library, check it for the definition
    4973             :  * of the geometry operation.
    4974             :  * If OGR is built without the GEOS library, this function will always fail,
    4975             :  * issuing a CPLE_NotSupported error.
    4976             :  *
    4977             :  * @param hThis the geometry.
    4978             :  * @param hOther the other geometry.
    4979             :  *
    4980             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4981             :  * or NULL if there is not intersection of if an error occurs.
    4982             :  */
    4983             : 
    4984          12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
    4985             : 
    4986             : {
    4987          12 :     VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
    4988             : 
    4989          24 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
    4990          24 :         OGRGeometry::FromHandle(hOther)));
    4991             : }
    4992             : 
    4993             : /************************************************************************/
    4994             : /*                               Union()                                */
    4995             : /************************************************************************/
    4996             : 
    4997             : /**
    4998             :  * \brief Compute union.
    4999             :  *
    5000             :  * Generates a new geometry which is the region of union of the
    5001             :  * two geometries operated on.
    5002             :  *
    5003             :  * Geometry validity is not checked. In case you are unsure of the validity
    5004             :  * of the input geometries, call IsValid() before, otherwise the result might
    5005             :  * be wrong.
    5006             :  *
    5007             :  * This method is the same as the C function OGR_G_Union().
    5008             :  *
    5009             :  * This method is built on the GEOS library, check it for the definition
    5010             :  * of the geometry operation.
    5011             :  * If OGR is built without the GEOS library, this method will always fail,
    5012             :  * issuing a CPLE_NotSupported error.
    5013             :  *
    5014             :  * @param poOtherGeom the other geometry unioned with "this" geometry.
    5015             :  *
    5016             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5017             :  */
    5018             : 
    5019             : OGRGeometry *
    5020          68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5021             : 
    5022             : {
    5023          68 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5024             :     {
    5025             : #ifndef HAVE_SFCGAL
    5026             : 
    5027           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5028           0 :         return nullptr;
    5029             : 
    5030             : #else
    5031             : 
    5032             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5033             :         if (poThis == nullptr)
    5034             :             return nullptr;
    5035             : 
    5036             :         sfcgal_geometry_t *poOther =
    5037             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5038             :         if (poOther == nullptr)
    5039             :         {
    5040             :             sfcgal_geometry_delete(poThis);
    5041             :             return nullptr;
    5042             :         }
    5043             : 
    5044             :         sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
    5045             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5046             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5047             :             poOtherGeom->getSpatialReference() != nullptr &&
    5048             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5049             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5050             : 
    5051             :         sfcgal_geometry_delete(poThis);
    5052             :         sfcgal_geometry_delete(poOther);
    5053             :         sfcgal_geometry_delete(poRes);
    5054             : 
    5055             :         return h_prodGeom;
    5056             : 
    5057             : #endif
    5058             :     }
    5059             : 
    5060             :     else
    5061             :     {
    5062             : #ifndef HAVE_GEOS
    5063             : 
    5064             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5065             :         return nullptr;
    5066             : 
    5067             : #else
    5068          68 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
    5069             : #endif /* HAVE_GEOS */
    5070             :     }
    5071             : }
    5072             : 
    5073             : /************************************************************************/
    5074             : /*                            OGR_G_Union()                             */
    5075             : /************************************************************************/
    5076             : 
    5077             : /**
    5078             :  * \brief Compute union.
    5079             :  *
    5080             :  * Generates a new geometry which is the region of union of the
    5081             :  * two geometries operated on.
    5082             :  *
    5083             :  * Geometry validity is not checked. In case you are unsure of the validity
    5084             :  * of the input geometries, call IsValid() before, otherwise the result might
    5085             :  * be wrong.
    5086             :  *
    5087             :  * This function is the same as the C++ method OGRGeometry::Union().
    5088             :  *
    5089             :  * This function is built on the GEOS library, check it for the definition
    5090             :  * of the geometry operation.
    5091             :  * If OGR is built without the GEOS library, this function will always fail,
    5092             :  * issuing a CPLE_NotSupported error.
    5093             :  *
    5094             :  * @param hThis the geometry.
    5095             :  * @param hOther the other geometry.
    5096             :  *
    5097             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5098             :  * or NULL if an error occurs.
    5099             :  */
    5100             : 
    5101          10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
    5102             : 
    5103             : {
    5104          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
    5105             : 
    5106          10 :     return OGRGeometry::ToHandle(
    5107          20 :         OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
    5108             : }
    5109             : 
    5110             : /************************************************************************/
    5111             : /*                               UnionCascaded()                        */
    5112             : /************************************************************************/
    5113             : 
    5114             : /**
    5115             :  * \brief Compute union using cascading.
    5116             :  *
    5117             :  * Geometry validity is not checked. In case you are unsure of the validity
    5118             :  * of the input geometries, call IsValid() before, otherwise the result might
    5119             :  * be wrong.
    5120             :  *
    5121             :  * The input geometry must be a MultiPolygon.
    5122             :  *
    5123             :  * This method is the same as the C function OGR_G_UnionCascaded().
    5124             :  *
    5125             :  * This method is built on the GEOS library, check it for the definition
    5126             :  * of the geometry operation.
    5127             :  * If OGR is built without the GEOS library, this method will always fail,
    5128             :  * issuing a CPLE_NotSupported error.
    5129             :  *
    5130             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5131             :  *
    5132             :  * @since OGR 1.8.0
    5133             :  *
    5134             :  * @deprecated Use UnaryUnion() instead
    5135             :  */
    5136             : 
    5137           2 : OGRGeometry *OGRGeometry::UnionCascaded() const
    5138             : 
    5139             : {
    5140             : #ifndef HAVE_GEOS
    5141             : 
    5142             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5143             :     return nullptr;
    5144             : #else
    5145             : 
    5146             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5147             :     if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
    5148             :     {
    5149             :         // GEOS < 3.11 crashes on an empty multipolygon input
    5150             :         auto poRet = new OGRGeometryCollection();
    5151             :         poRet->assignSpatialReference(getSpatialReference());
    5152             :         return poRet;
    5153             :     }
    5154             : #endif
    5155           2 :     OGRGeometry *poOGRProduct = nullptr;
    5156             : 
    5157           2 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5158           2 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5159           2 :     if (hThisGeosGeom != nullptr)
    5160             :     {
    5161           2 :         GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
    5162           2 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5163             : 
    5164             :         poOGRProduct =
    5165           2 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5166             :     }
    5167           2 :     freeGEOSContext(hGEOSCtxt);
    5168             : 
    5169           2 :     return poOGRProduct;
    5170             : 
    5171             : #endif  // HAVE_GEOS
    5172             : }
    5173             : 
    5174             : /************************************************************************/
    5175             : /*                            OGR_G_UnionCascaded()                     */
    5176             : /************************************************************************/
    5177             : 
    5178             : /**
    5179             :  * \brief Compute union using cascading.
    5180             :  *
    5181             :  * Geometry validity is not checked. In case you are unsure of the validity
    5182             :  * of the input geometries, call IsValid() before, otherwise the result might
    5183             :  * be wrong.
    5184             :  *
    5185             :  * The input geometry must be a MultiPolygon.
    5186             :  *
    5187             :  * This function is the same as the C++ method OGRGeometry::UnionCascaded().
    5188             :  *
    5189             :  * This function is built on the GEOS library, check it for the definition
    5190             :  * of the geometry operation.
    5191             :  * If OGR is built without the GEOS library, this function will always fail,
    5192             :  * issuing a CPLE_NotSupported error.
    5193             :  *
    5194             :  * @param hThis the geometry.
    5195             :  *
    5196             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5197             :  * or NULL if an error occurs.
    5198             :  *
    5199             :  * @deprecated Use OGR_G_UnaryUnion() instead
    5200             :  */
    5201             : 
    5202           2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
    5203             : 
    5204             : {
    5205           2 :     VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
    5206             : 
    5207           2 :     return OGRGeometry::ToHandle(
    5208           4 :         OGRGeometry::FromHandle(hThis)->UnionCascaded());
    5209             : }
    5210             : 
    5211             : /************************************************************************/
    5212             : /*                               UnaryUnion()                           */
    5213             : /************************************************************************/
    5214             : 
    5215             : /**
    5216             :  * \brief Returns the union of all components of a single geometry.
    5217             :  *
    5218             :  * Usually used to convert a collection into the smallest set of polygons that
    5219             :  * cover the same area.
    5220             :  *
    5221             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5222             :  *
    5223             :  * This method is the same as the C function OGR_G_UnaryUnion().
    5224             :  *
    5225             :  * This method is built on the GEOS library, check it for the definition
    5226             :  * of the geometry operation.
    5227             :  * If OGR is built without the GEOS library, this method will always fail,
    5228             :  * issuing a CPLE_NotSupported error.
    5229             :  *
    5230             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5231             :  *
    5232             :  * @since GDAL 3.7
    5233             :  */
    5234             : 
    5235         625 : OGRGeometry *OGRGeometry::UnaryUnion() const
    5236             : 
    5237             : {
    5238             : #ifndef HAVE_GEOS
    5239             : 
    5240             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5241             :     return nullptr;
    5242             : #else
    5243             : 
    5244             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5245             :     if (IsEmpty())
    5246             :     {
    5247             :         // GEOS < 3.11 crashes on an empty geometry
    5248             :         auto poRet = new OGRGeometryCollection();
    5249             :         poRet->assignSpatialReference(getSpatialReference());
    5250             :         return poRet;
    5251             :     }
    5252             : #endif
    5253         625 :     OGRGeometry *poOGRProduct = nullptr;
    5254             : 
    5255         625 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5256         625 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5257         625 :     if (hThisGeosGeom != nullptr)
    5258             :     {
    5259         625 :         GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
    5260         625 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5261             : 
    5262             :         poOGRProduct =
    5263         625 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5264             :     }
    5265         625 :     freeGEOSContext(hGEOSCtxt);
    5266             : 
    5267         625 :     return poOGRProduct;
    5268             : 
    5269             : #endif  // HAVE_GEOS
    5270             : }
    5271             : 
    5272             : /************************************************************************/
    5273             : /*                            OGR_G_UnaryUnion()                        */
    5274             : /************************************************************************/
    5275             : 
    5276             : /**
    5277             :  * \brief Returns the union of all components of a single geometry.
    5278             :  *
    5279             :  * Usually used to convert a collection into the smallest set of polygons that
    5280             :  * cover the same area.
    5281             :  *
    5282             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5283             :  *
    5284             :  * Geometry validity is not checked. In case you are unsure of the validity
    5285             :  * of the input geometries, call IsValid() before, otherwise the result might
    5286             :  * be wrong.
    5287             :  *
    5288             :  * This function is the same as the C++ method OGRGeometry::UnaryUnion().
    5289             :  *
    5290             :  * This function is built on the GEOS library, check it for the definition
    5291             :  * of the geometry operation.
    5292             :  * If OGR is built without the GEOS library, this function will always fail,
    5293             :  * issuing a CPLE_NotSupported error.
    5294             :  *
    5295             :  * @param hThis the geometry.
    5296             :  *
    5297             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5298             :  * or NULL if an error occurs.
    5299             :  *
    5300             :  * @since GDAL 3.7
    5301             :  */
    5302             : 
    5303           3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
    5304             : 
    5305             : {
    5306           3 :     VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
    5307             : 
    5308           3 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
    5309             : }
    5310             : 
    5311             : /************************************************************************/
    5312             : /*                             Difference()                             */
    5313             : /************************************************************************/
    5314             : 
    5315             : /**
    5316             :  * \brief Compute difference.
    5317             :  *
    5318             :  * Generates a new geometry which is the region of this geometry with the
    5319             :  * region of the second geometry removed.
    5320             :  *
    5321             :  * Geometry validity is not checked. In case you are unsure of the validity
    5322             :  * of the input geometries, call IsValid() before, otherwise the result might
    5323             :  * be wrong.
    5324             :  *
    5325             :  * This method is the same as the C function OGR_G_Difference().
    5326             :  *
    5327             :  * This method is built on the GEOS library, check it for the definition
    5328             :  * of the geometry operation.
    5329             :  * If OGR is built without the GEOS library, this method will always fail,
    5330             :  * issuing a CPLE_NotSupported error.
    5331             :  *
    5332             :  * @param poOtherGeom the other geometry removed from "this" geometry.
    5333             :  *
    5334             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5335             :  * is empty or if an error occurs.
    5336             :  */
    5337             : 
    5338             : OGRGeometry *
    5339         746 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5340             : 
    5341             : {
    5342         746 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5343             :     {
    5344             : #ifndef HAVE_SFCGAL
    5345             : 
    5346           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5347           0 :         return nullptr;
    5348             : 
    5349             : #else
    5350             : 
    5351             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5352             :         if (poThis == nullptr)
    5353             :             return nullptr;
    5354             : 
    5355             :         sfcgal_geometry_t *poOther =
    5356             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5357             :         if (poOther == nullptr)
    5358             :         {
    5359             :             sfcgal_geometry_delete(poThis);
    5360             :             return nullptr;
    5361             :         }
    5362             : 
    5363             :         sfcgal_geometry_t *poRes =
    5364             :             sfcgal_geometry_difference_3d(poThis, poOther);
    5365             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5366             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5367             :             poOtherGeom->getSpatialReference() != nullptr &&
    5368             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5369             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5370             : 
    5371             :         sfcgal_geometry_delete(poThis);
    5372             :         sfcgal_geometry_delete(poOther);
    5373             :         sfcgal_geometry_delete(poRes);
    5374             : 
    5375             :         return h_prodGeom;
    5376             : 
    5377             : #endif
    5378             :     }
    5379             : 
    5380             :     else
    5381             :     {
    5382             : #ifndef HAVE_GEOS
    5383             : 
    5384             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5385             :         return nullptr;
    5386             : 
    5387             : #else
    5388         746 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
    5389             : #endif /* HAVE_GEOS */
    5390             :     }
    5391             : }
    5392             : 
    5393             : /************************************************************************/
    5394             : /*                          OGR_G_Difference()                          */
    5395             : /************************************************************************/
    5396             : 
    5397             : /**
    5398             :  * \brief Compute difference.
    5399             :  *
    5400             :  * Generates a new geometry which is the region of this geometry with the
    5401             :  * region of the other geometry removed.
    5402             :  *
    5403             :  * Geometry validity is not checked. In case you are unsure of the validity
    5404             :  * of the input geometries, call IsValid() before, otherwise the result might
    5405             :  * be wrong.
    5406             :  *
    5407             :  * This function is the same as the C++ method OGRGeometry::Difference().
    5408             :  *
    5409             :  * This function is built on the GEOS library, check it for the definition
    5410             :  * of the geometry operation.
    5411             :  * If OGR is built without the GEOS library, this function will always fail,
    5412             :  * issuing a CPLE_NotSupported error.
    5413             :  *
    5414             :  * @param hThis the geometry.
    5415             :  * @param hOther the other geometry.
    5416             :  *
    5417             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5418             :  * or NULL if the difference is empty or if an error occurs.
    5419             :  */
    5420             : 
    5421           6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
    5422             : 
    5423             : {
    5424           6 :     VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
    5425             : 
    5426          12 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
    5427          12 :         OGRGeometry::FromHandle(hOther)));
    5428             : }
    5429             : 
    5430             : /************************************************************************/
    5431             : /*                        SymDifference()                               */
    5432             : /************************************************************************/
    5433             : 
    5434             : /**
    5435             :  * \brief Compute symmetric difference.
    5436             :  *
    5437             :  * Generates a new geometry which is the symmetric difference of this
    5438             :  * geometry and the second geometry passed into the method.
    5439             :  *
    5440             :  * Geometry validity is not checked. In case you are unsure of the validity
    5441             :  * of the input geometries, call IsValid() before, otherwise the result might
    5442             :  * be wrong.
    5443             :  *
    5444             :  * This method is the same as the C function OGR_G_SymDifference().
    5445             :  *
    5446             :  * This method is built on the GEOS library, check it for the definition
    5447             :  * of the geometry operation.
    5448             :  * If OGR is built without the GEOS library, this method will always fail,
    5449             :  * issuing a CPLE_NotSupported error.
    5450             :  *
    5451             :  * @param poOtherGeom the other geometry.
    5452             :  *
    5453             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5454             :  * is empty or if an error occurs.
    5455             :  *
    5456             :  * @since OGR 1.8.0
    5457             :  */
    5458             : 
    5459           7 : OGRGeometry *OGRGeometry::SymDifference(
    5460             :     UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
    5461             : 
    5462             : {
    5463           7 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5464             :     {
    5465             : #ifndef HAVE_SFCGAL
    5466           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5467           0 :         return nullptr;
    5468             : #else
    5469             :         OGRGeometry *poFirstDifference = Difference(poOtherGeom);
    5470             :         if (poFirstDifference == nullptr)
    5471             :             return nullptr;
    5472             : 
    5473             :         OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
    5474             :         if (poOtherDifference == nullptr)
    5475             :         {
    5476             :             delete poFirstDifference;
    5477             :             return nullptr;
    5478             :         }
    5479             : 
    5480             :         OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
    5481             :         delete poFirstDifference;
    5482             :         delete poOtherDifference;
    5483             :         return poSymDiff;
    5484             : #endif
    5485             :     }
    5486             : 
    5487             : #ifndef HAVE_GEOS
    5488             : 
    5489             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5490             :     return nullptr;
    5491             : 
    5492             : #else
    5493           7 :     return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
    5494             : #endif  // HAVE_GEOS
    5495             : }
    5496             : 
    5497             : //! @cond Doxygen_Suppress
    5498             : /**
    5499             :  * \brief Compute symmetric difference (deprecated)
    5500             :  *
    5501             :  * @deprecated
    5502             :  *
    5503             :  * @see OGRGeometry::SymDifference()
    5504             :  */
    5505             : OGRGeometry *
    5506           0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
    5507             : 
    5508             : {
    5509           0 :     return SymDifference(poOtherGeom);
    5510             : }
    5511             : 
    5512             : //! @endcond
    5513             : 
    5514             : /************************************************************************/
    5515             : /*                      OGR_G_SymDifference()                           */
    5516             : /************************************************************************/
    5517             : 
    5518             : /**
    5519             :  * \brief Compute symmetric difference.
    5520             :  *
    5521             :  * Generates a new geometry which is the symmetric difference of this
    5522             :  * geometry and the other geometry.
    5523             :  *
    5524             :  * Geometry validity is not checked. In case you are unsure of the validity
    5525             :  * of the input geometries, call IsValid() before, otherwise the result might
    5526             :  * be wrong.
    5527             :  *
    5528             :  * This function is the same as the C++ method
    5529             :  * OGRGeometry::SymmetricDifference().
    5530             :  *
    5531             :  * This function is built on the GEOS library, check it for the definition
    5532             :  * of the geometry operation.
    5533             :  * If OGR is built without the GEOS library, this function will always fail,
    5534             :  * issuing a CPLE_NotSupported error.
    5535             :  *
    5536             :  * @param hThis the geometry.
    5537             :  * @param hOther the other geometry.
    5538             :  *
    5539             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5540             :  * or NULL if the difference is empty or if an error occurs.
    5541             :  *
    5542             :  * @since OGR 1.8.0
    5543             :  */
    5544             : 
    5545           7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5546             : 
    5547             : {
    5548           7 :     VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
    5549             : 
    5550          14 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5551          14 :         OGRGeometry::FromHandle(hOther)));
    5552             : }
    5553             : 
    5554             : /**
    5555             :  * \brief Compute symmetric difference (deprecated)
    5556             :  *
    5557             :  * @deprecated
    5558             :  *
    5559             :  * @see OGR_G_SymmetricDifference()
    5560             :  */
    5561           0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5562             : 
    5563             : {
    5564           0 :     VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
    5565             : 
    5566           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5567           0 :         OGRGeometry::FromHandle(hOther)));
    5568             : }
    5569             : 
    5570             : /************************************************************************/
    5571             : /*                              Disjoint()                              */
    5572             : /************************************************************************/
    5573             : 
    5574             : /**
    5575             :  * \brief Test for disjointness.
    5576             :  *
    5577             :  * Tests if this geometry and the other passed into the method are disjoint.
    5578             :  *
    5579             :  * Geometry validity is not checked. In case you are unsure of the validity
    5580             :  * of the input geometries, call IsValid() before, otherwise the result might
    5581             :  * be wrong.
    5582             :  *
    5583             :  * This method is the same as the C function OGR_G_Disjoint().
    5584             :  *
    5585             :  * This method is built on the GEOS library, check it for the definition
    5586             :  * of the geometry operation.
    5587             :  * If OGR is built without the GEOS library, this method will always fail,
    5588             :  * issuing a CPLE_NotSupported error.
    5589             :  *
    5590             :  * @param poOtherGeom the geometry to compare to this geometry.
    5591             :  *
    5592             :  * @return TRUE if they are disjoint, otherwise FALSE.
    5593             :  */
    5594             : 
    5595             : OGRBoolean
    5596           8 : OGRGeometry::Disjoint(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
    5597             : 
    5598             : {
    5599             : #ifndef HAVE_GEOS
    5600             : 
    5601             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5602             :     return FALSE;
    5603             : 
    5604             : #else
    5605           8 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
    5606             : #endif  // HAVE_GEOS
    5607             : }
    5608             : 
    5609             : /************************************************************************/
    5610             : /*                           OGR_G_Disjoint()                           */
    5611             : /************************************************************************/
    5612             : 
    5613             : /**
    5614             :  * \brief Test for disjointness.
    5615             :  *
    5616             :  * Tests if this geometry and the other geometry are disjoint.
    5617             :  *
    5618             :  * Geometry validity is not checked. In case you are unsure of the validity
    5619             :  * of the input geometries, call IsValid() before, otherwise the result might
    5620             :  * be wrong.
    5621             :  *
    5622             :  * This function is the same as the C++ method OGRGeometry::Disjoint().
    5623             :  *
    5624             :  * This function is built on the GEOS library, check it for the definition
    5625             :  * of the geometry operation.
    5626             :  * If OGR is built without the GEOS library, this function will always fail,
    5627             :  * issuing a CPLE_NotSupported error.
    5628             :  *
    5629             :  * @param hThis the geometry to compare.
    5630             :  * @param hOther the other geometry to compare.
    5631             :  *
    5632             :  * @return TRUE if they are disjoint, otherwise FALSE.
    5633             :  */
    5634           8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
    5635             : 
    5636             : {
    5637           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
    5638             : 
    5639          16 :     return OGRGeometry::FromHandle(hThis)->Disjoint(
    5640           8 :         OGRGeometry::FromHandle(hOther));
    5641             : }
    5642             : 
    5643             : /************************************************************************/
    5644             : /*                              Touches()                               */
    5645             : /************************************************************************/
    5646             : 
    5647             : /**
    5648             :  * \brief Test for touching.
    5649             :  *
    5650             :  * Tests if this geometry and the other passed into the method are touching.
    5651             :  *
    5652             :  * Geometry validity is not checked. In case you are unsure of the validity
    5653             :  * of the input geometries, call IsValid() before, otherwise the result might
    5654             :  * be wrong.
    5655             :  *
    5656             :  * This method is the same as the C function OGR_G_Touches().
    5657             :  *
    5658             :  * This method is built on the GEOS library, check it for the definition
    5659             :  * of the geometry operation.
    5660             :  * If OGR is built without the GEOS library, this method will always fail,
    5661             :  * issuing a CPLE_NotSupported error.
    5662             :  *
    5663             :  * @param poOtherGeom the geometry to compare to this geometry.
    5664             :  *
    5665             :  * @return TRUE if they are touching, otherwise FALSE.
    5666             :  */
    5667             : 
    5668             : OGRBoolean
    5669          11 : OGRGeometry::Touches(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
    5670             : 
    5671             : {
    5672             : #ifndef HAVE_GEOS
    5673             : 
    5674             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5675             :     return FALSE;
    5676             : 
    5677             : #else
    5678          11 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
    5679             : #endif  // HAVE_GEOS
    5680             : }
    5681             : 
    5682             : /************************************************************************/
    5683             : /*                           OGR_G_Touches()                            */
    5684             : /************************************************************************/
    5685             : /**
    5686             :  * \brief Test for touching.
    5687             :  *
    5688             :  * Tests if this geometry and the other geometry are touching.
    5689             :  *
    5690             :  * Geometry validity is not checked. In case you are unsure of the validity
    5691             :  * of the input geometries, call IsValid() before, otherwise the result might
    5692             :  * be wrong.
    5693             :  *
    5694             :  * This function is the same as the C++ method OGRGeometry::Touches().
    5695             :  *
    5696             :  * This function is built on the GEOS library, check it for the definition
    5697             :  * of the geometry operation.
    5698             :  * If OGR is built without the GEOS library, this function will always fail,
    5699             :  * issuing a CPLE_NotSupported error.
    5700             :  *
    5701             :  * @param hThis the geometry to compare.
    5702             :  * @param hOther the other geometry to compare.
    5703             :  *
    5704             :  * @return TRUE if they are touching, otherwise FALSE.
    5705             :  */
    5706             : 
    5707           8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
    5708             : 
    5709             : {
    5710           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
    5711             : 
    5712          16 :     return OGRGeometry::FromHandle(hThis)->Touches(
    5713           8 :         OGRGeometry::FromHandle(hOther));
    5714             : }
    5715             : 
    5716             : /************************************************************************/
    5717             : /*                              Crosses()                               */
    5718             : /************************************************************************/
    5719             : 
    5720             : /**
    5721             :  * \brief Test for crossing.
    5722             :  *
    5723             :  * Tests if this geometry and the other passed into the method are crossing.
    5724             :  *
    5725             :  * Geometry validity is not checked. In case you are unsure of the validity
    5726             :  * of the input geometries, call IsValid() before, otherwise the result might
    5727             :  * be wrong.
    5728             :  *
    5729             :  * This method is the same as the C function OGR_G_Crosses().
    5730             :  *
    5731             :  * This method is built on the GEOS library, check it for the definition
    5732             :  * of the geometry operation.
    5733             :  * If OGR is built without the GEOS library, this method will always fail,
    5734             :  * issuing a CPLE_NotSupported error.
    5735             :  *
    5736             :  * @param poOtherGeom the geometry to compare to this geometry.
    5737             :  *
    5738             :  * @return TRUE if they are crossing, otherwise FALSE.
    5739             :  */
    5740             : 
    5741             : OGRBoolean
    5742           8 : OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5743             : 
    5744             : {
    5745           8 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5746             :     {
    5747             : #ifndef HAVE_SFCGAL
    5748             : 
    5749           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5750           0 :         return FALSE;
    5751             : 
    5752             : #else
    5753             : 
    5754             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5755             :         if (poThis == nullptr)
    5756             :             return FALSE;
    5757             : 
    5758             :         sfcgal_geometry_t *poOther =
    5759             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5760             :         if (poOther == nullptr)
    5761             :         {
    5762             :             sfcgal_geometry_delete(poThis);
    5763             :             return FALSE;
    5764             :         }
    5765             : 
    5766             :         int res = sfcgal_geometry_intersects_3d(poThis, poOther);
    5767             : 
    5768             :         sfcgal_geometry_delete(poThis);
    5769             :         sfcgal_geometry_delete(poOther);
    5770             : 
    5771             :         return (res == 1) ? TRUE : FALSE;
    5772             : 
    5773             : #endif
    5774             :     }
    5775             : 
    5776             :     else
    5777             :     {
    5778             : 
    5779             : #ifndef HAVE_GEOS
    5780             : 
    5781             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5782             :         return FALSE;
    5783             : 
    5784             : #else
    5785           8 :         return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
    5786             : #endif /* HAVE_GEOS */
    5787             :     }
    5788             : }
    5789             : 
    5790             : /************************************************************************/
    5791             : /*                           OGR_G_Crosses()                            */
    5792             : /************************************************************************/
    5793             : /**
    5794             :  * \brief Test for crossing.
    5795             :  *
    5796             :  * Tests if this geometry and the other geometry are crossing.
    5797             :  *
    5798             :  * Geometry validity is not checked. In case you are unsure of the validity
    5799             :  * of the input geometries, call IsValid() before, otherwise the result might
    5800             :  * be wrong.
    5801             :  *
    5802             :  * This function is the same as the C++ method OGRGeometry::Crosses().
    5803             :  *
    5804             :  * This function is built on the GEOS library, check it for the definition
    5805             :  * of the geometry operation.
    5806             :  * If OGR is built without the GEOS library, this function will always fail,
    5807             :  * issuing a CPLE_NotSupported error.
    5808             :  *
    5809             :  * @param hThis the geometry to compare.
    5810             :  * @param hOther the other geometry to compare.
    5811             :  *
    5812             :  * @return TRUE if they are crossing, otherwise FALSE.
    5813             :  */
    5814             : 
    5815           8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
    5816             : 
    5817             : {
    5818           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
    5819             : 
    5820          16 :     return OGRGeometry::FromHandle(hThis)->Crosses(
    5821           8 :         OGRGeometry::FromHandle(hOther));
    5822             : }
    5823             : 
    5824             : /************************************************************************/
    5825             : /*                               Within()                               */
    5826             : /************************************************************************/
    5827             : 
    5828             : /**
    5829             :  * \brief Test for containment.
    5830             :  *
    5831             :  * Tests if actual geometry object is within the passed geometry.
    5832             :  *
    5833             :  * Geometry validity is not checked. In case you are unsure of the validity
    5834             :  * of the input geometries, call IsValid() before, otherwise the result might
    5835             :  * be wrong.
    5836             :  *
    5837             :  * This method is the same as the C function OGR_G_Within().
    5838             :  *
    5839             :  * This method is built on the GEOS library, check it for the definition
    5840             :  * of the geometry operation.
    5841             :  * If OGR is built without the GEOS library, this method will always fail,
    5842             :  * issuing a CPLE_NotSupported error.
    5843             :  *
    5844             :  * @param poOtherGeom the geometry to compare to this geometry.
    5845             :  *
    5846             :  * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
    5847             :  */
    5848             : 
    5849             : OGRBoolean
    5850       21445 : OGRGeometry::Within(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
    5851             : 
    5852             : {
    5853             : #ifndef HAVE_GEOS
    5854             : 
    5855             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5856             :     return FALSE;
    5857             : 
    5858             : #else
    5859       21445 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
    5860             : #endif  // HAVE_GEOS
    5861             : }
    5862             : 
    5863             : /************************************************************************/
    5864             : /*                            OGR_G_Within()                            */
    5865             : /************************************************************************/
    5866             : 
    5867             : /**
    5868             :  * \brief Test for containment.
    5869             :  *
    5870             :  * Tests if this geometry is within the other geometry.
    5871             :  *
    5872             :  * Geometry validity is not checked. In case you are unsure of the validity
    5873             :  * of the input geometries, call IsValid() before, otherwise the result might
    5874             :  * be wrong.
    5875             :  *
    5876             :  * This function is the same as the C++ method OGRGeometry::Within().
    5877             :  *
    5878             :  * This function is built on the GEOS library, check it for the definition
    5879             :  * of the geometry operation.
    5880             :  * If OGR is built without the GEOS library, this function will always fail,
    5881             :  * issuing a CPLE_NotSupported error.
    5882             :  *
    5883             :  * @param hThis the geometry to compare.
    5884             :  * @param hOther the other geometry to compare.
    5885             :  *
    5886             :  * @return TRUE if hThis is within hOther, otherwise FALSE.
    5887             :  */
    5888        6402 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
    5889             : 
    5890             : {
    5891        6402 :     VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
    5892             : 
    5893       12804 :     return OGRGeometry::FromHandle(hThis)->Within(
    5894        6402 :         OGRGeometry::FromHandle(hOther));
    5895             : }
    5896             : 
    5897             : /************************************************************************/
    5898             : /*                              Contains()                              */
    5899             : /************************************************************************/
    5900             : 
    5901             : /**
    5902             :  * \brief Test for containment.
    5903             :  *
    5904             :  * Tests if actual geometry object contains the passed geometry.
    5905             :  *
    5906             :  * Geometry validity is not checked. In case you are unsure of the validity
    5907             :  * of the input geometries, call IsValid() before, otherwise the result might
    5908             :  * be wrong.
    5909             :  *
    5910             :  * This method is the same as the C function OGR_G_Contains().
    5911             :  *
    5912             :  * This method is built on the GEOS library, check it for the definition
    5913             :  * of the geometry operation.
    5914             :  * If OGR is built without the GEOS library, this method will always fail,
    5915             :  * issuing a CPLE_NotSupported error.
    5916             :  *
    5917             :  * @param poOtherGeom the geometry to compare to this geometry.
    5918             :  *
    5919             :  * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
    5920             :  */
    5921             : 
    5922             : OGRBoolean
    5923          47 : OGRGeometry::Contains(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
    5924             : 
    5925             : {
    5926             : #ifndef HAVE_GEOS
    5927             : 
    5928             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5929             :     return FALSE;
    5930             : 
    5931             : #else
    5932          47 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
    5933             : #endif  // HAVE_GEOS
    5934             : }
    5935             : 
    5936             : /************************************************************************/
    5937             : /*                            OGR_G_Contains()                            */
    5938             : /************************************************************************/
    5939             : 
    5940             : /**
    5941             :  * \brief Test for containment.
    5942             :  *
    5943             :  * Tests if this geometry contains the other geometry.
    5944             :  *
    5945             :  * Geometry validity is not checked. In case you are unsure of the validity
    5946             :  * of the input geometries, call IsValid() before, otherwise the result might
    5947             :  * be wrong.
    5948             :  *
    5949             :  * This function is the same as the C++ method OGRGeometry::Contains().
    5950             :  *
    5951             :  * This function is built on the GEOS library, check it for the definition
    5952             :  * of the geometry operation.
    5953             :  * If OGR is built without the GEOS library, this function will always fail,
    5954             :  * issuing a CPLE_NotSupported error.
    5955             :  *
    5956             :  * @param hThis the geometry to compare.
    5957             :  * @param hOther the other geometry to compare.
    5958             :  *
    5959             :  * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
    5960             :  */
    5961          11 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
    5962             : 
    5963             : {
    5964          11 :     VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
    5965             : 
    5966          22 :     return OGRGeometry::FromHandle(hThis)->Contains(
    5967          11 :         OGRGeometry::FromHandle(hOther));
    5968             : }
    5969             : 
    5970             : /************************************************************************/
    5971             : /*                              Overlaps()                              */
    5972             : /************************************************************************/
    5973             : 
    5974             : /**
    5975             :  * \brief Test for overlap.
    5976             :  *
    5977             :  * Tests if this geometry and the other passed into the method overlap, that is
    5978             :  * their intersection has a non-zero area.
    5979             :  *
    5980             :  * Geometry validity is not checked. In case you are unsure of the validity
    5981             :  * of the input geometries, call IsValid() before, otherwise the result might
    5982             :  * be wrong.
    5983             :  *
    5984             :  * This method is the same as the C function OGR_G_Overlaps().
    5985             :  *
    5986             :  * This method is built on the GEOS library, check it for the definition
    5987             :  * of the geometry operation.
    5988             :  * If OGR is built without the GEOS library, this method will always fail,
    5989             :  * issuing a CPLE_NotSupported error.
    5990             :  *
    5991             :  * @param poOtherGeom the geometry to compare to this geometry.
    5992             :  *
    5993             :  * @return TRUE if they are overlapping, otherwise FALSE.
    5994             :  */
    5995             : 
    5996             : OGRBoolean
    5997           7 : OGRGeometry::Overlaps(UNUSED_IF_NO_GEOS const OGRGeometry *poOtherGeom) const
    5998             : 
    5999             : {
    6000             : #ifndef HAVE_GEOS
    6001             : 
    6002             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6003             :     return FALSE;
    6004             : 
    6005             : #else
    6006           7 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
    6007             : #endif  // HAVE_GEOS
    6008             : }
    6009             : 
    6010             : /************************************************************************/
    6011             : /*                           OGR_G_Overlaps()                           */
    6012             : /************************************************************************/
    6013             : /**
    6014             :  * \brief Test for overlap.
    6015             :  *
    6016             :  * Tests if this geometry and the other geometry overlap, that is their
    6017             :  * intersection has a non-zero area.
    6018             :  *
    6019             :  * Geometry validity is not checked. In case you are unsure of the validity
    6020             :  * of the input geometries, call IsValid() before, otherwise the result might
    6021             :  * be wrong.
    6022             :  *
    6023             :  * This function is the same as the C++ method OGRGeometry::Overlaps().
    6024             :  *
    6025             :  * This function is built on the GEOS library, check it for the definition
    6026             :  * of the geometry operation.
    6027             :  * If OGR is built without the GEOS library, this function will always fail,
    6028             :  * issuing a CPLE_NotSupported error.
    6029             :  *
    6030             :  * @param hThis the geometry to compare.
    6031             :  * @param hOther the other geometry to compare.
    6032             :  *
    6033             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6034             :  */
    6035             : 
    6036           7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
    6037             : 
    6038             : {
    6039           7 :     VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
    6040             : 
    6041          14 :     return OGRGeometry::FromHandle(hThis)->Overlaps(
    6042           7 :         OGRGeometry::FromHandle(hOther));
    6043             : }
    6044             : 
    6045             : /************************************************************************/
    6046             : /*                             closeRings()                             */
    6047             : /************************************************************************/
    6048             : 
    6049             : /**
    6050             :  * \brief Force rings to be closed.
    6051             :  *
    6052             :  * If this geometry, or any contained geometries has polygon rings that
    6053             :  * are not closed, they will be closed by adding the starting point at
    6054             :  * the end.
    6055             :  */
    6056             : 
    6057        1248 : void OGRGeometry::closeRings()
    6058             : {
    6059        1248 : }
    6060             : 
    6061             : /************************************************************************/
    6062             : /*                          OGR_G_CloseRings()                          */
    6063             : /************************************************************************/
    6064             : 
    6065             : /**
    6066             :  * \brief Force rings to be closed.
    6067             :  *
    6068             :  * If this geometry, or any contained geometries has polygon rings that
    6069             :  * are not closed, they will be closed by adding the starting point at
    6070             :  * the end.
    6071             :  *
    6072             :  * @param hGeom handle to the geometry.
    6073             :  */
    6074             : 
    6075           6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
    6076             : 
    6077             : {
    6078           6 :     VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
    6079             : 
    6080           6 :     OGRGeometry::FromHandle(hGeom)->closeRings();
    6081             : }
    6082             : 
    6083             : /************************************************************************/
    6084             : /*                              Centroid()                              */
    6085             : /************************************************************************/
    6086             : 
    6087             : /**
    6088             :  * \brief Compute the geometry centroid.
    6089             :  *
    6090             :  * The centroid location is applied to the passed in OGRPoint object.
    6091             :  * The centroid is not necessarily within the geometry.
    6092             :  *
    6093             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6094             :  * however the current implementation based on GEOS can operate on other
    6095             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6096             :  * multipolygons.
    6097             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6098             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6099             :  * (multipolygons).
    6100             :  *
    6101             :  * This function is the same as the C function OGR_G_Centroid().
    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             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6109             :  *
    6110             :  * @since OGR 1.8.0 as a OGRGeometry method (previously was restricted
    6111             :  * to OGRPolygon)
    6112             :  */
    6113             : 
    6114           5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
    6115             : 
    6116             : {
    6117           5 :     if (poPoint == nullptr)
    6118           0 :         return OGRERR_FAILURE;
    6119             : 
    6120             : #ifndef HAVE_GEOS
    6121             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6122             :     return OGRERR_FAILURE;
    6123             : 
    6124             : #else
    6125             : 
    6126           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6127             :     GEOSGeom hThisGeosGeom =
    6128           5 :         exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
    6129             : 
    6130           5 :     if (hThisGeosGeom != nullptr)
    6131             :     {
    6132           5 :         GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
    6133           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6134             : 
    6135           5 :         if (hOtherGeosGeom == nullptr)
    6136             :         {
    6137           0 :             freeGEOSContext(hGEOSCtxt);
    6138           0 :             return OGRERR_FAILURE;
    6139             :         }
    6140             : 
    6141             :         OGRGeometry *poCentroidGeom =
    6142           5 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6143             : 
    6144           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6145             : 
    6146           5 :         if (poCentroidGeom == nullptr)
    6147             :         {
    6148           0 :             freeGEOSContext(hGEOSCtxt);
    6149           0 :             return OGRERR_FAILURE;
    6150             :         }
    6151           5 :         if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6152             :         {
    6153           0 :             delete poCentroidGeom;
    6154           0 :             freeGEOSContext(hGEOSCtxt);
    6155           0 :             return OGRERR_FAILURE;
    6156             :         }
    6157             : 
    6158           5 :         if (getSpatialReference() != nullptr)
    6159           0 :             poCentroidGeom->assignSpatialReference(getSpatialReference());
    6160             : 
    6161           5 :         OGRPoint *poCentroid = poCentroidGeom->toPoint();
    6162             : 
    6163           5 :         if (!poCentroid->IsEmpty())
    6164             :         {
    6165           4 :             poPoint->setX(poCentroid->getX());
    6166           4 :             poPoint->setY(poCentroid->getY());
    6167             :         }
    6168             :         else
    6169             :         {
    6170           1 :             poPoint->empty();
    6171             :         }
    6172             : 
    6173           5 :         delete poCentroidGeom;
    6174             : 
    6175           5 :         freeGEOSContext(hGEOSCtxt);
    6176           5 :         return OGRERR_NONE;
    6177             :     }
    6178             :     else
    6179             :     {
    6180           0 :         freeGEOSContext(hGEOSCtxt);
    6181           0 :         return OGRERR_FAILURE;
    6182             :     }
    6183             : 
    6184             : #endif  // HAVE_GEOS
    6185             : }
    6186             : 
    6187             : /************************************************************************/
    6188             : /*                           OGR_G_Centroid()                           */
    6189             : /************************************************************************/
    6190             : 
    6191             : /**
    6192             :  * \brief Compute the geometry centroid.
    6193             :  *
    6194             :  * The centroid location is applied to the passed in OGRPoint object.
    6195             :  * The centroid is not necessarily within the geometry.
    6196             :  *
    6197             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6198             :  * however the current implementation based on GEOS can operate on other
    6199             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6200             :  * multipolygons.
    6201             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6202             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6203             :  * (multipolygons).
    6204             :  *
    6205             :  * This function is the same as the C++ method OGRGeometry::Centroid().
    6206             :  *
    6207             :  * This function is built on the GEOS library, check it for the definition
    6208             :  * of the geometry operation.
    6209             :  * If OGR is built without the GEOS library, this function will always fail,
    6210             :  * issuing a CPLE_NotSupported error.
    6211             :  *
    6212             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6213             :  */
    6214             : 
    6215           5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
    6216             : 
    6217             : {
    6218           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
    6219             : 
    6220           5 :     OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
    6221           5 :     if (poCentroidGeom == nullptr)
    6222           0 :         return OGRERR_FAILURE;
    6223           5 :     if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6224             :     {
    6225           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6226             :                  "Passed wrong geometry type as centroid argument.");
    6227           0 :         return OGRERR_FAILURE;
    6228             :     }
    6229             : 
    6230           5 :     return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
    6231             : }
    6232             : 
    6233             : /************************************************************************/
    6234             : /*                        OGR_G_PointOnSurface()                        */
    6235             : /************************************************************************/
    6236             : 
    6237             : /**
    6238             :  * \brief Returns a point guaranteed to lie on the surface.
    6239             :  *
    6240             :  * This method relates to the SFCOM ISurface::get_PointOnSurface() method
    6241             :  * however the current implementation based on GEOS can operate on other
    6242             :  * geometry types than the types that are supported by SQL/MM-Part 3 :
    6243             :  * surfaces (polygons) and multisurfaces (multipolygons).
    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 hGeom the geometry to operate on.
    6251             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6252             :  * or NULL if an error occurs.
    6253             :  *
    6254             :  * @since OGR 1.10
    6255             :  */
    6256             : 
    6257           4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
    6258             : 
    6259             : {
    6260           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
    6261             : 
    6262             : #ifndef HAVE_GEOS
    6263             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6264             :     return nullptr;
    6265             : #else
    6266             : 
    6267           4 :     OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
    6268             : 
    6269           4 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    6270           4 :     GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
    6271             : 
    6272           4 :     if (hThisGeosGeom != nullptr)
    6273             :     {
    6274             :         GEOSGeom hOtherGeosGeom =
    6275           4 :             GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
    6276           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6277             : 
    6278           4 :         if (hOtherGeosGeom == nullptr)
    6279             :         {
    6280           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6281           0 :             return nullptr;
    6282             :         }
    6283             : 
    6284             :         OGRGeometry *poInsidePointGeom =
    6285           4 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6286             : 
    6287           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6288             : 
    6289           4 :         if (poInsidePointGeom == nullptr)
    6290             :         {
    6291           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6292           0 :             return nullptr;
    6293             :         }
    6294           4 :         if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
    6295             :         {
    6296           0 :             delete poInsidePointGeom;
    6297           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6298           0 :             return nullptr;
    6299             :         }
    6300             : 
    6301           4 :         if (poThis->getSpatialReference() != nullptr)
    6302           0 :             poInsidePointGeom->assignSpatialReference(
    6303           0 :                 poThis->getSpatialReference());
    6304             : 
    6305           4 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6306           4 :         return OGRGeometry::ToHandle(poInsidePointGeom);
    6307             :     }
    6308             : 
    6309           0 :     OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6310           0 :     return nullptr;
    6311             : #endif
    6312             : }
    6313             : 
    6314             : /************************************************************************/
    6315             : /*                          PointOnSurfaceInternal()                    */
    6316             : /************************************************************************/
    6317             : 
    6318             : //! @cond Doxygen_Suppress
    6319           0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
    6320             : {
    6321           0 :     if (poPoint == nullptr || poPoint->IsEmpty())
    6322           0 :         return OGRERR_FAILURE;
    6323             : 
    6324           0 :     OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
    6325             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
    6326           0 :     if (hInsidePoint == nullptr)
    6327           0 :         return OGRERR_FAILURE;
    6328             : 
    6329           0 :     OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
    6330           0 :     if (poInsidePoint->IsEmpty())
    6331             :     {
    6332           0 :         poPoint->empty();
    6333             :     }
    6334             :     else
    6335             :     {
    6336           0 :         poPoint->setX(poInsidePoint->getX());
    6337           0 :         poPoint->setY(poInsidePoint->getY());
    6338             :     }
    6339             : 
    6340           0 :     OGR_G_DestroyGeometry(hInsidePoint);
    6341             : 
    6342           0 :     return OGRERR_NONE;
    6343             : }
    6344             : 
    6345             : //! @endcond
    6346             : 
    6347             : /************************************************************************/
    6348             : /*                              Simplify()                              */
    6349             : /************************************************************************/
    6350             : 
    6351             : /**
    6352             :  * \brief Simplify the geometry.
    6353             :  *
    6354             :  * This function is the same as the C function OGR_G_Simplify().
    6355             :  *
    6356             :  * This function is built on the GEOS library, check it for the definition
    6357             :  * of the geometry operation.
    6358             :  * If OGR is built without the GEOS library, this function will always fail,
    6359             :  * issuing a CPLE_NotSupported error.
    6360             :  *
    6361             :  * @param dTolerance the distance tolerance for the simplification.
    6362             :  *
    6363             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6364             :  *
    6365             :  * @since OGR 1.8.0
    6366             :  */
    6367             : 
    6368          55 : OGRGeometry *OGRGeometry::Simplify(UNUSED_IF_NO_GEOS double dTolerance) const
    6369             : 
    6370             : {
    6371             : #ifndef HAVE_GEOS
    6372             : 
    6373             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6374             :     return nullptr;
    6375             : 
    6376             : #else
    6377          55 :     OGRGeometry *poOGRProduct = nullptr;
    6378             : 
    6379          55 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6380          55 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6381          55 :     if (hThisGeosGeom != nullptr)
    6382             :     {
    6383             :         GEOSGeom hGeosProduct =
    6384          55 :             GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
    6385          55 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6386             :         poOGRProduct =
    6387          55 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6388             :     }
    6389          55 :     freeGEOSContext(hGEOSCtxt);
    6390          55 :     return poOGRProduct;
    6391             : 
    6392             : #endif  // HAVE_GEOS
    6393             : }
    6394             : 
    6395             : /************************************************************************/
    6396             : /*                         OGR_G_Simplify()                             */
    6397             : /************************************************************************/
    6398             : 
    6399             : /**
    6400             :  * \brief Compute a simplified geometry.
    6401             :  *
    6402             :  * This function is the same as the C++ method OGRGeometry::Simplify().
    6403             :  *
    6404             :  * This function is built on the GEOS library, check it for the definition
    6405             :  * of the geometry operation.
    6406             :  * If OGR is built without the GEOS library, this function will always fail,
    6407             :  * issuing a CPLE_NotSupported error.
    6408             :  *
    6409             :  * @param hThis the geometry.
    6410             :  * @param dTolerance the distance tolerance for the simplification.
    6411             :  *
    6412             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6413             :  * or NULL if an error occurs.
    6414             :  *
    6415             :  * @since OGR 1.8.0
    6416             :  */
    6417             : 
    6418           1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
    6419             : 
    6420             : {
    6421           1 :     VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
    6422           1 :     return OGRGeometry::ToHandle(
    6423           2 :         OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
    6424             : }
    6425             : 
    6426             : /************************************************************************/
    6427             : /*                         SimplifyPreserveTopology()                   */
    6428             : /************************************************************************/
    6429             : 
    6430             : /**
    6431             :  * \brief Simplify the geometry while preserving topology.
    6432             :  *
    6433             :  * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
    6434             :  *
    6435             :  * This function is built on the GEOS library, check it for the definition
    6436             :  * of the geometry operation.
    6437             :  * If OGR is built without the GEOS library, this function will always fail,
    6438             :  * issuing a CPLE_NotSupported error.
    6439             :  *
    6440             :  * @param dTolerance the distance tolerance for the simplification.
    6441             :  *
    6442             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6443             :  *
    6444             :  * @since OGR 1.9.0
    6445             :  */
    6446             : 
    6447             : OGRGeometry *
    6448          17 : OGRGeometry::SimplifyPreserveTopology(UNUSED_IF_NO_GEOS double dTolerance) const
    6449             : 
    6450             : {
    6451             : #ifndef HAVE_GEOS
    6452             : 
    6453             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6454             :     return nullptr;
    6455             : 
    6456             : #else
    6457          17 :     OGRGeometry *poOGRProduct = nullptr;
    6458             : 
    6459          17 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6460          17 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6461          17 :     if (hThisGeosGeom != nullptr)
    6462             :     {
    6463          17 :         GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
    6464             :             hGEOSCtxt, hThisGeosGeom, dTolerance);
    6465          17 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6466             :         poOGRProduct =
    6467          17 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6468             :     }
    6469          17 :     freeGEOSContext(hGEOSCtxt);
    6470          17 :     return poOGRProduct;
    6471             : 
    6472             : #endif  // HAVE_GEOS
    6473             : }
    6474             : 
    6475             : /************************************************************************/
    6476             : /*                     OGR_G_SimplifyPreserveTopology()                 */
    6477             : /************************************************************************/
    6478             : 
    6479             : /**
    6480             :  * \brief Simplify the geometry while preserving topology.
    6481             :  *
    6482             :  * This function is the same as the C++ method
    6483             :  * OGRGeometry::SimplifyPreserveTopology().
    6484             :  *
    6485             :  * This function is built on the GEOS library, check it for the definition
    6486             :  * of the geometry operation.
    6487             :  * If OGR is built without the GEOS library, this function will always fail,
    6488             :  * issuing a CPLE_NotSupported error.
    6489             :  *
    6490             :  * @param hThis the geometry.
    6491             :  * @param dTolerance the distance tolerance for the simplification.
    6492             :  *
    6493             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6494             :  * or NULL if an error occurs.
    6495             :  *
    6496             :  * @since OGR 1.9.0
    6497             :  */
    6498             : 
    6499           1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
    6500             :                                             double dTolerance)
    6501             : 
    6502             : {
    6503           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
    6504           1 :     return OGRGeometry::ToHandle(
    6505           1 :         OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
    6506             : }
    6507             : 
    6508             : /************************************************************************/
    6509             : /*                           roundCoordinates()                         */
    6510             : /************************************************************************/
    6511             : 
    6512             : /** Round coordinates of the geometry to the specified precision.
    6513             :  *
    6514             :  * Note that this is not the same as OGRGeometry::SetPrecision(). The later
    6515             :  * will return valid geometries, whereas roundCoordinates() does not make
    6516             :  * such guarantee and may return geometries with invalidities, if they are
    6517             :  * not compatible of the specified precision. roundCoordinates() supports
    6518             :  * curve geometries, whereas SetPrecision() does not currently.
    6519             :  *
    6520             :  * One use case for roundCoordinates() is to undo the effect of
    6521             :  * quantizeCoordinates().
    6522             :  *
    6523             :  * @param sPrecision Contains the precision requirements.
    6524             :  * @since GDAL 3.9
    6525             :  */
    6526          39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
    6527             : {
    6528             :     struct Rounder : public OGRDefaultGeometryVisitor
    6529             :     {
    6530             :         const OGRGeomCoordinatePrecision &m_precision;
    6531             :         const double m_invXYResolution;
    6532             :         const double m_invZResolution;
    6533             :         const double m_invMResolution;
    6534             : 
    6535          39 :         explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
    6536          39 :             : m_precision(sPrecisionIn),
    6537          39 :               m_invXYResolution(m_precision.dfXYResolution !=
    6538             :                                         OGRGeomCoordinatePrecision::UNKNOWN
    6539          39 :                                     ? 1.0 / m_precision.dfXYResolution
    6540             :                                     : 0.0),
    6541          39 :               m_invZResolution(m_precision.dfZResolution !=
    6542             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6543          39 :                                    ? 1.0 / m_precision.dfZResolution
    6544             :                                    : 0.0),
    6545          39 :               m_invMResolution(m_precision.dfMResolution !=
    6546             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6547          39 :                                    ? 1.0 / m_precision.dfMResolution
    6548         117 :                                    : 0.0)
    6549             :         {
    6550          39 :         }
    6551             : 
    6552             :         using OGRDefaultGeometryVisitor::visit;
    6553             : 
    6554         379 :         void visit(OGRPoint *poPoint) override
    6555             :         {
    6556         379 :             if (m_precision.dfXYResolution !=
    6557             :                 OGRGeomCoordinatePrecision::UNKNOWN)
    6558             :             {
    6559         379 :                 poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
    6560         379 :                               m_precision.dfXYResolution);
    6561         379 :                 poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
    6562         379 :                               m_precision.dfXYResolution);
    6563             :             }
    6564         758 :             if (m_precision.dfZResolution !=
    6565         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6566           4 :                 poPoint->Is3D())
    6567             :             {
    6568           4 :                 poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
    6569           4 :                               m_precision.dfZResolution);
    6570             :             }
    6571         758 :             if (m_precision.dfMResolution !=
    6572         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6573           4 :                 poPoint->IsMeasured())
    6574             :             {
    6575           4 :                 poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
    6576           4 :                               m_precision.dfMResolution);
    6577             :             }
    6578         379 :         }
    6579             :     };
    6580             : 
    6581          78 :     Rounder rounder(sPrecision);
    6582          39 :     accept(&rounder);
    6583          39 : }
    6584             : 
    6585             : /************************************************************************/
    6586             : /*                           SetPrecision()                             */
    6587             : /************************************************************************/
    6588             : 
    6589             : /** Set the geometry's precision, rounding all its coordinates to the precision
    6590             :  * grid, and making sure the geometry is still valid.
    6591             :  *
    6592             :  * This is a stronger version of roundCoordinates().
    6593             :  *
    6594             :  * Note that at time of writing GEOS does no supported curve geometries. So
    6595             :  * currently if this function is called on such a geometry, OGR will first call
    6596             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    6597             :  * that it is unlikely to yield to the expected result.
    6598             :  *
    6599             :  * This function is the same as the C function OGR_G_SetPrecision().
    6600             :  *
    6601             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    6602             :  * GEOS library. Check it for the definition of the geometry operation.
    6603             :  * If OGR is built without the GEOS library, this function will always fail,
    6604             :  * issuing a CPLE_NotSupported error.
    6605             :  *
    6606             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    6607             :  *                 precision.
    6608             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    6609             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    6610             :  *
    6611             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6612             :  *
    6613             :  * @since GDAL 3.9
    6614             :  */
    6615             : 
    6616           6 : OGRGeometry *OGRGeometry::SetPrecision(UNUSED_IF_NO_GEOS double dfGridSize,
    6617             :                                        UNUSED_IF_NO_GEOS int nFlags) const
    6618             : {
    6619             : #ifndef HAVE_GEOS
    6620             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6621             :     return nullptr;
    6622             : 
    6623             : #else
    6624           6 :     OGRGeometry *poOGRProduct = nullptr;
    6625             : 
    6626           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6627           6 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6628           6 :     if (hThisGeosGeom != nullptr)
    6629             :     {
    6630           6 :         GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
    6631             :             hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
    6632           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6633             :         poOGRProduct =
    6634           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6635             :     }
    6636           6 :     freeGEOSContext(hGEOSCtxt);
    6637           6 :     return poOGRProduct;
    6638             : 
    6639             : #endif  // HAVE_GEOS
    6640             : }
    6641             : 
    6642             : /************************************************************************/
    6643             : /*                         OGR_G_SetPrecision()                         */
    6644             : /************************************************************************/
    6645             : 
    6646             : /** Set the geometry's precision, rounding all its coordinates to the precision
    6647             :  * grid, and making sure the geometry is still valid.
    6648             :  *
    6649             :  * This is a stronger version of roundCoordinates().
    6650             :  *
    6651             :  * Note that at time of writing GEOS does no supported curve geometries. So
    6652             :  * currently if this function is called on such a geometry, OGR will first call
    6653             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    6654             :  * that it is unlikely to yield to the expected result.
    6655             :  *
    6656             :  * This function is the same as the C++ method OGRGeometry::SetPrecision().
    6657             :  *
    6658             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    6659             :  * GEOS library. Check it for the definition of the geometry operation.
    6660             :  * If OGR is built without the GEOS library, this function will always fail,
    6661             :  * issuing a CPLE_NotSupported error.
    6662             :  *
    6663             :  * @param hThis the geometry.
    6664             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    6665             :  *                 precision.
    6666             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    6667             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    6668             :  *
    6669             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6670             :  * or NULL if an error occurs.
    6671             :  *
    6672             :  * @since GDAL 3.9
    6673             :  */
    6674           1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
    6675             :                                 int nFlags)
    6676             : {
    6677           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
    6678           1 :     return OGRGeometry::ToHandle(
    6679           1 :         OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
    6680             : }
    6681             : 
    6682             : /************************************************************************/
    6683             : /*                         DelaunayTriangulation()                      */
    6684             : /************************************************************************/
    6685             : 
    6686             : /**
    6687             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    6688             :  *
    6689             :  * This function is the same as the C function OGR_G_DelaunayTriangulation().
    6690             :  *
    6691             :  * This function is built on the GEOS library, v3.4 or above.
    6692             :  * If OGR is built without the GEOS library, this function will always fail,
    6693             :  * issuing a CPLE_NotSupported error.
    6694             :  *
    6695             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    6696             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    6697             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    6698             :  *
    6699             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6700             :  *
    6701             :  * @since OGR 2.1
    6702             :  */
    6703             : 
    6704             : #ifndef HAVE_GEOS
    6705             : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
    6706             :                                                 int /*bOnlyEdges*/) const
    6707             : {
    6708             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6709             :     return nullptr;
    6710             : }
    6711             : #else
    6712           1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
    6713             :                                                 int bOnlyEdges) const
    6714             : {
    6715           1 :     OGRGeometry *poOGRProduct = nullptr;
    6716             : 
    6717           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6718           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6719           1 :     if (hThisGeosGeom != nullptr)
    6720             :     {
    6721           1 :         GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
    6722             :             hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
    6723           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6724             :         poOGRProduct =
    6725           1 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6726             :     }
    6727           1 :     freeGEOSContext(hGEOSCtxt);
    6728           1 :     return poOGRProduct;
    6729             : }
    6730             : #endif
    6731             : 
    6732             : /************************************************************************/
    6733             : /*                     OGR_G_DelaunayTriangulation()                    */
    6734             : /************************************************************************/
    6735             : 
    6736             : /**
    6737             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    6738             :  *
    6739             :  * This function is the same as the C++ method
    6740             :  * OGRGeometry::DelaunayTriangulation().
    6741             :  *
    6742             :  * This function is built on the GEOS library, v3.4 or above.
    6743             :  * If OGR is built without the GEOS library, this function will always fail,
    6744             :  * issuing a CPLE_NotSupported error.
    6745             :  *
    6746             :  * @param hThis the geometry.
    6747             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    6748             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    6749             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    6750             :  *
    6751             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6752             :  * or NULL if an error occurs.
    6753             :  *
    6754             :  * @since OGR 2.1
    6755             :  */
    6756             : 
    6757           1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
    6758             :                                          int bOnlyEdges)
    6759             : 
    6760             : {
    6761           1 :     VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
    6762             : 
    6763           1 :     return OGRGeometry::ToHandle(
    6764           1 :         OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
    6765           2 :                                                               bOnlyEdges));
    6766             : }
    6767             : 
    6768             : /************************************************************************/
    6769             : /*                             Polygonize()                             */
    6770             : /************************************************************************/
    6771             : /* Contributor: Alessandro Furieri, a.furieri@lqt.it                    */
    6772             : /* Developed for Faunalia (http://www.faunalia.it) with funding from    */
    6773             : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED        */
    6774             : /*                   AMBIENTALE                                         */
    6775             : /************************************************************************/
    6776             : 
    6777             : /**
    6778             :  * \brief Polygonizes a set of sparse edges.
    6779             :  *
    6780             :  * A new geometry object is created and returned containing a collection
    6781             :  * of reassembled Polygons: NULL will be returned if the input collection
    6782             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    6783             :  * into Polygons is impossible due to topological inconsistencies.
    6784             :  *
    6785             :  * This method is the same as the C function OGR_G_Polygonize().
    6786             :  *
    6787             :  * This method is built on the GEOS library, check it for the definition
    6788             :  * of the geometry operation.
    6789             :  * If OGR is built without the GEOS library, this method will always fail,
    6790             :  * issuing a CPLE_NotSupported error.
    6791             :  *
    6792             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6793             :  *
    6794             :  * @since OGR 1.9.0
    6795             :  */
    6796             : 
    6797         117 : OGRGeometry *OGRGeometry::Polygonize() const
    6798             : 
    6799             : {
    6800             : #ifndef HAVE_GEOS
    6801             : 
    6802             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6803             :     return nullptr;
    6804             : 
    6805             : #else
    6806             : 
    6807         117 :     const OGRGeometryCollection *poColl = nullptr;
    6808         233 :     if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
    6809         116 :         wkbFlatten(getGeometryType()) == wkbMultiLineString)
    6810         116 :         poColl = toGeometryCollection();
    6811             :     else
    6812           1 :         return nullptr;
    6813             : 
    6814         116 :     const int nCount = poColl->getNumGeometries();
    6815             : 
    6816         116 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    6817         116 :     bool bError = false;
    6818             : 
    6819         116 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6820             : 
    6821         116 :     GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
    6822         747 :     for (int ig = 0; ig < nCount; ig++)
    6823             :     {
    6824         631 :         GEOSGeom hGeosGeom = nullptr;
    6825         631 :         const OGRGeometry *poChild = poColl->getGeometryRef(ig);
    6826        1262 :         if (poChild == nullptr ||
    6827         631 :             wkbFlatten(poChild->getGeometryType()) != wkbLineString)
    6828           1 :             bError = true;
    6829             :         else
    6830             :         {
    6831         630 :             hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
    6832         630 :             if (hGeosGeom == nullptr)
    6833           0 :                 bError = true;
    6834             :         }
    6835         631 :         pahGeosGeomList[ig] = hGeosGeom;
    6836             :     }
    6837             : 
    6838         116 :     if (!bError)
    6839             :     {
    6840             :         GEOSGeom hGeosPolygs =
    6841         115 :             GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
    6842             : 
    6843             :         poPolygsOGRGeom =
    6844         115 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    6845             :     }
    6846             : 
    6847         747 :     for (int ig = 0; ig < nCount; ig++)
    6848             :     {
    6849         631 :         GEOSGeom hGeosGeom = pahGeosGeomList[ig];
    6850         631 :         if (hGeosGeom != nullptr)
    6851         630 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    6852             :     }
    6853         116 :     delete[] pahGeosGeomList;
    6854         116 :     freeGEOSContext(hGEOSCtxt);
    6855             : 
    6856         116 :     return poPolygsOGRGeom;
    6857             : 
    6858             : #endif  // HAVE_GEOS
    6859             : }
    6860             : 
    6861             : /************************************************************************/
    6862             : /*                          OGR_G_Polygonize()                          */
    6863             : /************************************************************************/
    6864             : /**
    6865             :  * \brief Polygonizes a set of sparse edges.
    6866             :  *
    6867             :  * A new geometry object is created and returned containing a collection
    6868             :  * of reassembled Polygons: NULL will be returned if the input collection
    6869             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    6870             :  * into Polygons is impossible due to topological inconsistencies.
    6871             :  *
    6872             :  * This function is the same as the C++ method OGRGeometry::Polygonize().
    6873             :  *
    6874             :  * This function is built on the GEOS library, check it for the definition
    6875             :  * of the geometry operation.
    6876             :  * If OGR is built without the GEOS library, this function will always fail,
    6877             :  * issuing a CPLE_NotSupported error.
    6878             :  *
    6879             :  * @param hTarget The Geometry to be polygonized.
    6880             :  *
    6881             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6882             :  * or NULL if an error occurs.
    6883             :  *
    6884             :  * @since OGR 1.9.0
    6885             :  */
    6886             : 
    6887           3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
    6888             : 
    6889             : {
    6890           3 :     VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
    6891             : 
    6892           3 :     return OGRGeometry::ToHandle(
    6893           6 :         OGRGeometry::FromHandle(hTarget)->Polygonize());
    6894             : }
    6895             : 
    6896             : /************************************************************************/
    6897             : /*                             BuildArea()                              */
    6898             : /************************************************************************/
    6899             : 
    6900             : /**
    6901             :  * \brief Polygonize a linework assuming inner polygons are holes.
    6902             :  *
    6903             :  * This method is the same as the C function OGR_G_BuildArea().
    6904             :  *
    6905             :  * Polygonization is performed similarly to OGRGeometry::Polygonize().
    6906             :  * Additionally, holes are dropped and the result is unified producing
    6907             :  * a single Polygon or a MultiPolygon.
    6908             :  *
    6909             :  * A new geometry object is created and returned: NULL on failure,
    6910             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    6911             :  * Polygon or MultiPolygon on success.
    6912             :  *
    6913             :  * This method is built on the GEOSBuildArea_r() function of the GEOS
    6914             :  * library, check it for the definition of the geometry operation.
    6915             :  * If OGR is built without the GEOS library, this method will always fail,
    6916             :  * issuing a CPLE_NotSupported error.
    6917             :  *
    6918             :  * @return a newly allocated geometry now owned by the caller,
    6919             :  *         or NULL on failure.
    6920             :  *
    6921             :  * @since OGR 3.11
    6922             :  */
    6923             : 
    6924          14 : OGRGeometry *OGRGeometry::BuildArea() const
    6925             : 
    6926             : {
    6927             : #ifndef HAVE_GEOS
    6928             : 
    6929             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6930             :     return nullptr;
    6931             : 
    6932             : #else
    6933             : 
    6934          14 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    6935             : 
    6936          14 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6937          14 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6938          14 :     if (hThisGeosGeom != nullptr)
    6939             :     {
    6940          14 :         GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
    6941             :         poPolygsOGRGeom =
    6942          14 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    6943          14 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6944             :     }
    6945          14 :     freeGEOSContext(hGEOSCtxt);
    6946             : 
    6947          14 :     return poPolygsOGRGeom;
    6948             : 
    6949             : #endif  // HAVE_GEOS
    6950             : }
    6951             : 
    6952             : /************************************************************************/
    6953             : /*                          OGR_G_BuildArea()                           */
    6954             : /************************************************************************/
    6955             : 
    6956             : /**
    6957             :  * \brief Polygonize a linework assuming inner polygons are holes.
    6958             :  *
    6959             :  * This function is the same as the C++ method OGRGeometry::BuildArea().
    6960             :  *
    6961             :  * Polygonization is performed similarly to OGR_G_Polygonize().
    6962             :  * Additionally, holes are dropped and the result is unified producing
    6963             :  * a single Polygon or a MultiPolygon.
    6964             :  *
    6965             :  * A new geometry object is created and returned: NULL on failure,
    6966             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    6967             :  * Polygon or MultiPolygon on success.
    6968             :  *
    6969             :  * This function is built on the GEOSBuildArea_r() function of the GEOS
    6970             :  * library, check it for the definition of the geometry operation.
    6971             :  * If OGR is built without the GEOS library, this function will always fail,
    6972             :  * issuing a CPLE_NotSupported error.
    6973             :  *
    6974             :  * @param hGeom handle on the geometry to polygonize.
    6975             :  *
    6976             :  * @return a handle on newly allocated geometry now owned by the caller,
    6977             :  *         or NULL on failure.
    6978             :  *
    6979             :  * @since OGR 3.11
    6980             :  */
    6981             : 
    6982           0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
    6983             : 
    6984             : {
    6985           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
    6986             : 
    6987           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
    6988             : }
    6989             : 
    6990             : /************************************************************************/
    6991             : /*                               swapXY()                               */
    6992             : /************************************************************************/
    6993             : 
    6994             : /**
    6995             :  * \brief Swap x and y coordinates.
    6996             :  *
    6997             :  * @since OGR 1.8.0
    6998             :  */
    6999             : 
    7000           0 : void OGRGeometry::swapXY()
    7001             : 
    7002             : {
    7003           0 : }
    7004             : 
    7005             : /************************************************************************/
    7006             : /*                               swapXY()                               */
    7007             : /************************************************************************/
    7008             : 
    7009             : /**
    7010             :  * \brief Swap x and y coordinates.
    7011             :  *
    7012             :  * @param hGeom geometry.
    7013             :  * @since OGR 2.3.0
    7014             :  */
    7015             : 
    7016           2 : void OGR_G_SwapXY(OGRGeometryH hGeom)
    7017             : {
    7018           2 :     VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
    7019             : 
    7020           2 :     OGRGeometry::FromHandle(hGeom)->swapXY();
    7021             : }
    7022             : 
    7023             : /************************************************************************/
    7024             : /*                        Prepared geometry API                         */
    7025             : /************************************************************************/
    7026             : 
    7027             : #if defined(HAVE_GEOS)
    7028             : struct _OGRPreparedGeometry
    7029             : {
    7030             :     GEOSContextHandle_t hGEOSCtxt;
    7031             :     GEOSGeom hGEOSGeom;
    7032             :     const GEOSPreparedGeometry *poPreparedGEOSGeom;
    7033             : };
    7034             : #endif
    7035             : 
    7036             : /************************************************************************/
    7037             : /*                       OGRHasPreparedGeometrySupport()                */
    7038             : /************************************************************************/
    7039             : 
    7040             : /** Returns if GEOS has prepared geometry support.
    7041             :  * @return TRUE or FALSE
    7042             :  */
    7043           1 : int OGRHasPreparedGeometrySupport()
    7044             : {
    7045             : #if defined(HAVE_GEOS)
    7046           1 :     return TRUE;
    7047             : #else
    7048             :     return FALSE;
    7049             : #endif
    7050             : }
    7051             : 
    7052             : /************************************************************************/
    7053             : /*                         OGRCreatePreparedGeometry()                  */
    7054             : /************************************************************************/
    7055             : 
    7056             : /** Creates a prepared geometry.
    7057             :  *
    7058             :  * To free with OGRDestroyPreparedGeometry()
    7059             :  *
    7060             :  * @param hGeom input geometry to prepare.
    7061             :  * @return handle to a prepared geometry.
    7062             :  * @since GDAL 3.3
    7063             :  */
    7064             : OGRPreparedGeometryH
    7065       52352 : OGRCreatePreparedGeometry(UNUSED_IF_NO_GEOS OGRGeometryH hGeom)
    7066             : {
    7067             : #if defined(HAVE_GEOS)
    7068       52352 :     OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
    7069       52352 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    7070       52352 :     GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
    7071       52352 :     if (hGEOSGeom == nullptr)
    7072             :     {
    7073           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7074           0 :         return nullptr;
    7075             :     }
    7076             :     const GEOSPreparedGeometry *poPreparedGEOSGeom =
    7077       52352 :         GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
    7078       52352 :     if (poPreparedGEOSGeom == nullptr)
    7079             :     {
    7080           0 :         GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
    7081           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7082           0 :         return nullptr;
    7083             :     }
    7084             : 
    7085       52352 :     OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
    7086       52352 :     poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
    7087       52352 :     poPreparedGeom->hGEOSGeom = hGEOSGeom;
    7088       52352 :     poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
    7089             : 
    7090       52352 :     return poPreparedGeom;
    7091             : #else
    7092             :     return nullptr;
    7093             : #endif
    7094             : }
    7095             : 
    7096             : /************************************************************************/
    7097             : /*                        OGRDestroyPreparedGeometry()                  */
    7098             : /************************************************************************/
    7099             : 
    7100             : /** Destroys a prepared geometry.
    7101             :  * @param hPreparedGeom prepared geometry.
    7102             :  * @since GDAL 3.3
    7103             :  */
    7104       52398 : void OGRDestroyPreparedGeometry(
    7105             :     UNUSED_IF_NO_GEOS OGRPreparedGeometryH hPreparedGeom)
    7106             : {
    7107             : #if defined(HAVE_GEOS)
    7108       52398 :     if (hPreparedGeom != nullptr)
    7109             :     {
    7110       52352 :         GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
    7111             :                                    hPreparedGeom->poPreparedGEOSGeom);
    7112       52352 :         GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
    7113       52352 :         OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
    7114       52352 :         delete hPreparedGeom;
    7115             :     }
    7116             : #endif
    7117       52398 : }
    7118             : 
    7119             : /************************************************************************/
    7120             : /*                      OGRPreparedGeometryIntersects()                 */
    7121             : /************************************************************************/
    7122             : 
    7123             : /** Returns whether a prepared geometry intersects with a geometry.
    7124             :  * @param hPreparedGeom prepared geometry.
    7125             :  * @param hOtherGeom other geometry.
    7126             :  * @return TRUE or FALSE.
    7127             :  * @since GDAL 3.3
    7128             :  */
    7129        5235 : int OGRPreparedGeometryIntersects(
    7130             :     UNUSED_IF_NO_GEOS const OGRPreparedGeometryH hPreparedGeom,
    7131             :     UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
    7132             : {
    7133             : #if defined(HAVE_GEOS)
    7134        5235 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7135        5235 :     if (hPreparedGeom == nullptr ||
    7136             :         poOtherGeom == nullptr
    7137             :         // The check for IsEmpty() is for buggy GEOS versions.
    7138             :         // See https://github.com/libgeos/geos/pull/423
    7139       10470 :         || poOtherGeom->IsEmpty())
    7140             :     {
    7141           1 :         return FALSE;
    7142             :     }
    7143             : 
    7144             :     GEOSGeom hGEOSOtherGeom =
    7145        5234 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7146        5234 :     if (hGEOSOtherGeom == nullptr)
    7147           0 :         return FALSE;
    7148             : 
    7149        5234 :     const bool bRet = CPL_TO_BOOL(GEOSPreparedIntersects_r(
    7150             :         hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
    7151             :         hGEOSOtherGeom));
    7152        5234 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7153             : 
    7154        5234 :     return bRet;
    7155             : #else
    7156             :     return FALSE;
    7157             : #endif
    7158             : }
    7159             : 
    7160             : /** Returns whether a prepared geometry contains a geometry.
    7161             :  * @param hPreparedGeom prepared geometry.
    7162             :  * @param hOtherGeom other geometry.
    7163             :  * @return TRUE or FALSE.
    7164             :  */
    7165      120116 : int OGRPreparedGeometryContains(UNUSED_IF_NO_GEOS const OGRPreparedGeometryH
    7166             :                                     hPreparedGeom,
    7167             :                                 UNUSED_IF_NO_GEOS const OGRGeometryH hOtherGeom)
    7168             : {
    7169             : #if defined(HAVE_GEOS)
    7170      120116 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7171      120116 :     if (hPreparedGeom == nullptr ||
    7172             :         poOtherGeom == nullptr
    7173             :         // The check for IsEmpty() is for buggy GEOS versions.
    7174             :         // See https://github.com/libgeos/geos/pull/423
    7175      240232 :         || poOtherGeom->IsEmpty())
    7176             :     {
    7177           1 :         return FALSE;
    7178             :     }
    7179             : 
    7180             :     GEOSGeom hGEOSOtherGeom =
    7181      120115 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7182      120115 :     if (hGEOSOtherGeom == nullptr)
    7183           0 :         return FALSE;
    7184             : 
    7185      120115 :     const bool bRet = CPL_TO_BOOL(GEOSPreparedContains_r(
    7186             :         hPreparedGeom->hGEOSCtxt, hPreparedGeom->poPreparedGEOSGeom,
    7187             :         hGEOSOtherGeom));
    7188      120115 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7189             : 
    7190      120115 :     return bRet;
    7191             : #else
    7192             :     return FALSE;
    7193             : #endif
    7194             : }
    7195             : 
    7196             : /************************************************************************/
    7197             : /*                       OGRGeometryFromEWKB()                          */
    7198             : /************************************************************************/
    7199             : 
    7200        1429 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
    7201             :                                  int bIsPostGIS1_EWKB)
    7202             : 
    7203             : {
    7204        1429 :     OGRGeometry *poGeometry = nullptr;
    7205             : 
    7206        1429 :     size_t nWKBSize = 0;
    7207        1429 :     const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
    7208        1429 :     if (pabyWKB == nullptr)
    7209           0 :         return nullptr;
    7210             : 
    7211             :     /* -------------------------------------------------------------------- */
    7212             :     /*      Try to ingest the geometry.                                     */
    7213             :     /* -------------------------------------------------------------------- */
    7214        1429 :     (void)OGRGeometryFactory::createFromWkb(
    7215             :         pabyWKB, nullptr, &poGeometry, nWKBSize,
    7216             :         (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
    7217             : 
    7218        1429 :     return poGeometry;
    7219             : }
    7220             : 
    7221             : /************************************************************************/
    7222             : /*                     OGRGeometryFromHexEWKB()                         */
    7223             : /************************************************************************/
    7224             : 
    7225        1427 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
    7226             :                                     int bIsPostGIS1_EWKB)
    7227             : 
    7228             : {
    7229        1427 :     if (pszBytea == nullptr)
    7230           0 :         return nullptr;
    7231             : 
    7232        1427 :     int nWKBLength = 0;
    7233        1427 :     GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
    7234             : 
    7235             :     OGRGeometry *poGeometry =
    7236        1427 :         OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
    7237             : 
    7238        1427 :     CPLFree(pabyWKB);
    7239             : 
    7240        1427 :     return poGeometry;
    7241             : }
    7242             : 
    7243             : /************************************************************************/
    7244             : /*                       OGRGeometryToHexEWKB()                         */
    7245             : /************************************************************************/
    7246             : 
    7247        1059 : char *OGRGeometryToHexEWKB(OGRGeometry *poGeometry, int nSRSId,
    7248             :                            int nPostGISMajor, int nPostGISMinor)
    7249             : {
    7250        1059 :     const size_t nWkbSize = poGeometry->WkbSize();
    7251        1059 :     GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
    7252        1059 :     if (pabyWKB == nullptr)
    7253           0 :         return CPLStrdup("");
    7254             : 
    7255         118 :     if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
    7256        1795 :         wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
    7257         618 :         poGeometry->IsEmpty())
    7258             :     {
    7259           2 :         if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
    7260             :             OGRERR_NONE)
    7261             :         {
    7262           0 :             CPLFree(pabyWKB);
    7263           0 :             return CPLStrdup("");
    7264             :         }
    7265             :     }
    7266        1057 :     else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
    7267             :                                      (nPostGISMajor < 2)
    7268             :                                          ? wkbVariantPostGIS1
    7269        1057 :                                          : wkbVariantOldOgc) != OGRERR_NONE)
    7270             :     {
    7271           0 :         CPLFree(pabyWKB);
    7272           0 :         return CPLStrdup("");
    7273             :     }
    7274             : 
    7275             :     // When converting to hex, each byte takes 2 hex characters.  In addition
    7276             :     // we add in 8 characters to represent the SRID integer in hex, and
    7277             :     // one for a null terminator.
    7278             :     // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
    7279             :     // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
    7280             :     // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
    7281        1059 :     if (nWkbSize >
    7282        1059 :         static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
    7283             :     {
    7284           0 :         CPLFree(pabyWKB);
    7285           0 :         return CPLStrdup("");
    7286             :     }
    7287        1059 :     const size_t nTextSize = nWkbSize * 2 + 8 + 1;
    7288        1059 :     char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
    7289        1059 :     if (pszTextBuf == nullptr)
    7290             :     {
    7291           0 :         CPLFree(pabyWKB);
    7292           0 :         return CPLStrdup("");
    7293             :     }
    7294        1059 :     char *pszTextBufCurrent = pszTextBuf;
    7295             : 
    7296             :     // Convert the 1st byte, which is the endianness flag, to hex.
    7297        1059 :     char *pszHex = CPLBinaryToHex(1, pabyWKB);
    7298        1059 :     strcpy(pszTextBufCurrent, pszHex);
    7299        1059 :     CPLFree(pszHex);
    7300        1059 :     pszTextBufCurrent += 2;
    7301             : 
    7302             :     // Next, get the geom type which is bytes 2 through 5.
    7303             :     GUInt32 geomType;
    7304        1059 :     memcpy(&geomType, pabyWKB + 1, 4);
    7305             : 
    7306             :     // Now add the SRID flag if an SRID is provided.
    7307        1059 :     if (nSRSId > 0)
    7308             :     {
    7309             :         // Change the flag to wkbNDR (little) endianness.
    7310         529 :         constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
    7311         529 :         GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
    7312             :         // Apply the flag.
    7313         529 :         geomType = geomType | nGSrsFlag;
    7314             :     }
    7315             : 
    7316             :     // Now write the geom type which is 4 bytes.
    7317        1059 :     pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
    7318        1059 :     strcpy(pszTextBufCurrent, pszHex);
    7319        1059 :     CPLFree(pszHex);
    7320        1059 :     pszTextBufCurrent += 8;
    7321             : 
    7322             :     // Now include SRID if provided.
    7323        1059 :     if (nSRSId > 0)
    7324             :     {
    7325             :         // Force the srsid to wkbNDR (little) endianness.
    7326         529 :         const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
    7327         529 :         pszHex = CPLBinaryToHex(sizeof(nGSRSId),
    7328             :                                 reinterpret_cast<const GByte *>(&nGSRSId));
    7329         529 :         strcpy(pszTextBufCurrent, pszHex);
    7330         529 :         CPLFree(pszHex);
    7331         529 :         pszTextBufCurrent += 8;
    7332             :     }
    7333             : 
    7334             :     // Copy the rest of the data over - subtract
    7335             :     // 5 since we already copied 5 bytes above.
    7336        1059 :     pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
    7337        1059 :     CPLFree(pabyWKB);
    7338        1059 :     if (!pszHex || pszHex[0] == 0)
    7339             :     {
    7340           0 :         CPLFree(pszTextBuf);
    7341           0 :         return pszHex;
    7342             :     }
    7343        1059 :     strcpy(pszTextBufCurrent, pszHex);
    7344        1059 :     CPLFree(pszHex);
    7345             : 
    7346        1059 :     return pszTextBuf;
    7347             : }
    7348             : 
    7349             : /************************************************************************/
    7350             : /*                       importPreambleFromWkb()                       */
    7351             : /************************************************************************/
    7352             : 
    7353             : //! @cond Doxygen_Suppress
    7354      156990 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
    7355             :                                           size_t nSize,
    7356             :                                           OGRwkbByteOrder &eByteOrder,
    7357             :                                           OGRwkbVariant eWkbVariant)
    7358             : {
    7359      156990 :     if (nSize < 9 && nSize != static_cast<size_t>(-1))
    7360           0 :         return OGRERR_NOT_ENOUGH_DATA;
    7361             : 
    7362             :     /* -------------------------------------------------------------------- */
    7363             :     /*      Get the byte order byte.                                        */
    7364             :     /* -------------------------------------------------------------------- */
    7365      156990 :     int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
    7366      156990 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    7367           0 :         return OGRERR_CORRUPT_DATA;
    7368      156990 :     eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    7369             : 
    7370             :     /* -------------------------------------------------------------------- */
    7371             :     /*      Get the geometry feature type.                                  */
    7372             :     /* -------------------------------------------------------------------- */
    7373             :     OGRwkbGeometryType eGeometryType;
    7374             :     const OGRErr err =
    7375      156990 :         OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
    7376      156990 :     if (wkbHasZ(eGeometryType))
    7377       62304 :         flags |= OGR_G_3D;
    7378      156990 :     if (wkbHasM(eGeometryType))
    7379       59690 :         flags |= OGR_G_MEASURED;
    7380             : 
    7381      156990 :     if (err != OGRERR_NONE || eGeometryType != getGeometryType())
    7382           0 :         return OGRERR_CORRUPT_DATA;
    7383             : 
    7384      156990 :     return OGRERR_NONE;
    7385             : }
    7386             : 
    7387             : /************************************************************************/
    7388             : /*                    importPreambleOfCollectionFromWkb()              */
    7389             : /*                                                                      */
    7390             : /*      Utility method for OGRSimpleCurve, OGRCompoundCurve,            */
    7391             : /*      OGRCurvePolygon and OGRGeometryCollection.                      */
    7392             : /************************************************************************/
    7393             : 
    7394       74360 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
    7395             :     const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
    7396             :     OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
    7397             :     OGRwkbVariant eWkbVariant)
    7398             : {
    7399       74360 :     nGeomCount = 0;
    7400             : 
    7401             :     OGRErr eErr =
    7402       74360 :         importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
    7403       74360 :     if (eErr != OGRERR_NONE)
    7404           0 :         return eErr;
    7405             : 
    7406             :     /* -------------------------------------------------------------------- */
    7407             :     /*      Clear existing Geoms.                                           */
    7408             :     /* -------------------------------------------------------------------- */
    7409       74360 :     int _flags = flags;  // flags set in importPreambleFromWkb
    7410       74360 :     empty();             // may reset flags etc.
    7411             : 
    7412             :     // restore
    7413       74360 :     if (_flags & OGR_G_3D)
    7414       59262 :         set3D(TRUE);
    7415       74360 :     if (_flags & OGR_G_MEASURED)
    7416       56768 :         setMeasured(TRUE);
    7417             : 
    7418             :     /* -------------------------------------------------------------------- */
    7419             :     /*      Get the sub-geometry count.                                     */
    7420             :     /* -------------------------------------------------------------------- */
    7421       74360 :     memcpy(&nGeomCount, pabyData + 5, 4);
    7422             : 
    7423       74360 :     if (OGR_SWAP(eByteOrder))
    7424         383 :         nGeomCount = CPL_SWAP32(nGeomCount);
    7425             : 
    7426      148586 :     if (nGeomCount < 0 ||
    7427       74226 :         static_cast<size_t>(nGeomCount) >
    7428       74226 :             std::numeric_limits<size_t>::max() / nMinSubGeomSize)
    7429             :     {
    7430         134 :         nGeomCount = 0;
    7431         134 :         return OGRERR_CORRUPT_DATA;
    7432             :     }
    7433       74226 :     const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
    7434             : 
    7435             :     // Each ring has a minimum of nMinSubGeomSize bytes.
    7436       74226 :     if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
    7437             :     {
    7438         910 :         CPLError(CE_Failure, CPLE_AppDefined,
    7439             :                  "Length of input WKB is too small");
    7440         910 :         nGeomCount = 0;
    7441         910 :         return OGRERR_NOT_ENOUGH_DATA;
    7442             :     }
    7443             : 
    7444       73316 :     nDataOffset = 9;
    7445       73316 :     if (nSize != static_cast<size_t>(-1))
    7446             :     {
    7447       73296 :         CPLAssert(nSize >= nDataOffset);
    7448       73296 :         nSize -= nDataOffset;
    7449             :     }
    7450             : 
    7451       73316 :     return OGRERR_NONE;
    7452             : }
    7453             : 
    7454             : /************************************************************************/
    7455             : /*                      importCurveCollectionFromWkt()                  */
    7456             : /*                                                                      */
    7457             : /*      Utility method for OGRCompoundCurve, OGRCurvePolygon and        */
    7458             : /*      OGRMultiCurve.                                                  */
    7459             : /************************************************************************/
    7460             : 
    7461        1398 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
    7462             :     const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
    7463             :     int bAllowCurve, int bAllowCompoundCurve,
    7464             :     OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
    7465             : 
    7466             : {
    7467        1398 :     int bHasZ = FALSE;
    7468        1398 :     int bHasM = FALSE;
    7469        1398 :     bool bIsEmpty = false;
    7470        1398 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
    7471        1398 :     flags = 0;
    7472        1398 :     if (eErr != OGRERR_NONE)
    7473          14 :         return eErr;
    7474        1384 :     if (bHasZ)
    7475         189 :         flags |= OGR_G_3D;
    7476        1384 :     if (bHasM)
    7477         120 :         flags |= OGR_G_MEASURED;
    7478        1384 :     if (bIsEmpty)
    7479         109 :         return OGRERR_NONE;
    7480             : 
    7481             :     char szToken[OGR_WKT_TOKEN_MAX];
    7482        1275 :     const char *pszInput = *ppszInput;
    7483        1275 :     eErr = OGRERR_NONE;
    7484             : 
    7485             :     // Skip first '('.
    7486        1275 :     pszInput = OGRWktReadToken(pszInput, szToken);
    7487             : 
    7488             :     /* ==================================================================== */
    7489             :     /*      Read each curve in turn.  Note that we try to reuse the same    */
    7490             :     /*      point list buffer from curve to curve to cut down on            */
    7491             :     /*      allocate/deallocate overhead.                                   */
    7492             :     /* ==================================================================== */
    7493        1275 :     OGRRawPoint *paoPoints = nullptr;
    7494        1275 :     int nMaxPoints = 0;
    7495        1275 :     double *padfZ = nullptr;
    7496             : 
    7497         611 :     do
    7498             :     {
    7499             : 
    7500             :         /* --------------------------------------------------------------------
    7501             :          */
    7502             :         /*      Get the first token, which should be the geometry type. */
    7503             :         /* --------------------------------------------------------------------
    7504             :          */
    7505        1886 :         const char *pszInputBefore = pszInput;
    7506        1886 :         pszInput = OGRWktReadToken(pszInput, szToken);
    7507             : 
    7508             :         /* --------------------------------------------------------------------
    7509             :          */
    7510             :         /*      Do the import. */
    7511             :         /* --------------------------------------------------------------------
    7512             :          */
    7513        1886 :         OGRCurve *poCurve = nullptr;
    7514        1886 :         if (EQUAL(szToken, "("))
    7515             :         {
    7516        1364 :             OGRLineString *poLine = new OGRLineString();
    7517        1364 :             poCurve = poLine;
    7518        1364 :             pszInput = pszInputBefore;
    7519        1364 :             eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
    7520             :                                                  paoPoints, nMaxPoints, padfZ);
    7521             :         }
    7522         522 :         else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
    7523             :         {
    7524          16 :             poCurve = new OGRLineString();
    7525             :         }
    7526             :         // Accept LINESTRING(), but this is an extension to the BNF, also
    7527             :         // accepted by PostGIS.
    7528         506 :         else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
    7529         491 :                  (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
    7530         491 :                   !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
    7531        1162 :                   OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
    7532         150 :                  (bAllowCompoundCurve &&
    7533         150 :                   STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
    7534             :         {
    7535         468 :             OGRGeometry *poGeom = nullptr;
    7536         468 :             pszInput = pszInputBefore;
    7537             :             eErr =
    7538         468 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
    7539         468 :             if (poGeom == nullptr)
    7540             :             {
    7541           1 :                 eErr = OGRERR_CORRUPT_DATA;
    7542             :             }
    7543             :             else
    7544             :             {
    7545         467 :                 poCurve = poGeom->toCurve();
    7546             :             }
    7547             :         }
    7548             :         else
    7549             :         {
    7550          38 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
    7551             :                      szToken);
    7552          38 :             eErr = OGRERR_CORRUPT_DATA;
    7553             :         }
    7554             : 
    7555             :         // If this has M it is an error if poGeom does not have M.
    7556        1886 :         if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
    7557           0 :             eErr = OGRERR_CORRUPT_DATA;
    7558             : 
    7559        1886 :         if (eErr == OGRERR_NONE)
    7560        1841 :             eErr = pfnAddCurveDirectly(this, poCurve);
    7561        1886 :         if (eErr != OGRERR_NONE)
    7562             :         {
    7563          55 :             delete poCurve;
    7564          55 :             break;
    7565             :         }
    7566             : 
    7567             :         /* --------------------------------------------------------------------
    7568             :          */
    7569             :         /*      Read the delimiter following the surface. */
    7570             :         /* --------------------------------------------------------------------
    7571             :          */
    7572        1831 :         pszInput = OGRWktReadToken(pszInput, szToken);
    7573        1831 :     } while (szToken[0] == ',' && eErr == OGRERR_NONE);
    7574             : 
    7575        1275 :     CPLFree(paoPoints);
    7576        1275 :     CPLFree(padfZ);
    7577             : 
    7578             :     /* -------------------------------------------------------------------- */
    7579             :     /*      freak if we don't get a closing bracket.                        */
    7580             :     /* -------------------------------------------------------------------- */
    7581             : 
    7582        1275 :     if (eErr != OGRERR_NONE)
    7583          55 :         return eErr;
    7584             : 
    7585        1220 :     if (szToken[0] != ')')
    7586           9 :         return OGRERR_CORRUPT_DATA;
    7587             : 
    7588        1211 :     *ppszInput = pszInput;
    7589        1211 :     return OGRERR_NONE;
    7590             : }
    7591             : 
    7592             : //! @endcond
    7593             : 
    7594             : /************************************************************************/
    7595             : /*                          OGR_GT_Flatten()                            */
    7596             : /************************************************************************/
    7597             : /**
    7598             :  * \brief Returns the 2D geometry type corresponding to the passed geometry
    7599             :  * type.
    7600             :  *
    7601             :  * This function is intended to work with geometry types as old-style 99-402
    7602             :  * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
    7603             :  * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
    7604             :  *
    7605             :  * @param eType Input geometry type
    7606             :  *
    7607             :  * @return 2D geometry type corresponding to the passed geometry type.
    7608             :  *
    7609             :  * @since GDAL 2.0
    7610             :  */
    7611             : 
    7612     7483780 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
    7613             : {
    7614     7483780 :     eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
    7615     7483780 :     if (eType >= 1000 && eType < 2000)  // ISO Z.
    7616     2708820 :         return static_cast<OGRwkbGeometryType>(eType - 1000);
    7617     4774970 :     if (eType >= 2000 && eType < 3000)  // ISO M.
    7618        5754 :         return static_cast<OGRwkbGeometryType>(eType - 2000);
    7619     4769210 :     if (eType >= 3000 && eType < 4000)  // ISO ZM.
    7620      135645 :         return static_cast<OGRwkbGeometryType>(eType - 3000);
    7621     4633570 :     return eType;
    7622             : }
    7623             : 
    7624             : /************************************************************************/
    7625             : /*                          OGR_GT_HasZ()                               */
    7626             : /************************************************************************/
    7627             : /**
    7628             :  * \brief Return if the geometry type is a 3D geometry type.
    7629             :  *
    7630             :  * @param eType Input geometry type
    7631             :  *
    7632             :  * @return TRUE if the geometry type is a 3D geometry type.
    7633             :  *
    7634             :  * @since GDAL 2.0
    7635             :  */
    7636             : 
    7637     2016040 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
    7638             : {
    7639     2016040 :     if (eType & wkb25DBitInternalUse)
    7640      156348 :         return TRUE;
    7641     1859690 :     if (eType >= 1000 && eType < 2000)  // Accept 1000 for wkbUnknownZ.
    7642         251 :         return TRUE;
    7643     1859440 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    7644      121235 :         return TRUE;
    7645     1738210 :     return FALSE;
    7646             : }
    7647             : 
    7648             : /************************************************************************/
    7649             : /*                          OGR_GT_HasM()                               */
    7650             : /************************************************************************/
    7651             : /**
    7652             :  * \brief Return if the geometry type is a measured type.
    7653             :  *
    7654             :  * @param eType Input geometry type
    7655             :  *
    7656             :  * @return TRUE if the geometry type is a measured type.
    7657             :  *
    7658             :  * @since GDAL 2.1
    7659             :  */
    7660             : 
    7661     2072970 : int OGR_GT_HasM(OGRwkbGeometryType eType)
    7662             : {
    7663     2072970 :     if (eType >= 2000 && eType < 3000)  // Accept 2000 for wkbUnknownM.
    7664        2549 :         return TRUE;
    7665     2070420 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    7666      120897 :         return TRUE;
    7667     1949530 :     return FALSE;
    7668             : }
    7669             : 
    7670             : /************************************************************************/
    7671             : /*                           OGR_GT_SetZ()                              */
    7672             : /************************************************************************/
    7673             : /**
    7674             :  * \brief Returns the 3D geometry type corresponding to the passed geometry
    7675             :  * type.
    7676             :  *
    7677             :  * @param eType Input geometry type
    7678             :  *
    7679             :  * @return 3D geometry type corresponding to the passed geometry type.
    7680             :  *
    7681             :  * @since GDAL 2.0
    7682             :  */
    7683             : 
    7684        5298 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
    7685             : {
    7686        5298 :     if (OGR_GT_HasZ(eType) || eType == wkbNone)
    7687         494 :         return eType;
    7688        4804 :     if (eType <= wkbGeometryCollection)
    7689        4745 :         return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
    7690             :     else
    7691          59 :         return static_cast<OGRwkbGeometryType>(eType + 1000);
    7692             : }
    7693             : 
    7694             : /************************************************************************/
    7695             : /*                           OGR_GT_SetM()                              */
    7696             : /************************************************************************/
    7697             : /**
    7698             :  * \brief Returns the measured geometry type corresponding to the passed
    7699             :  * geometry type.
    7700             :  *
    7701             :  * @param eType Input geometry type
    7702             :  *
    7703             :  * @return measured geometry type corresponding to the passed geometry type.
    7704             :  *
    7705             :  * @since GDAL 2.1
    7706             :  */
    7707             : 
    7708        1929 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
    7709             : {
    7710        1929 :     if (OGR_GT_HasM(eType) || eType == wkbNone)
    7711         258 :         return eType;
    7712        1671 :     if (eType & wkb25DBitInternalUse)
    7713             :     {
    7714         700 :         eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
    7715         700 :         eType = static_cast<OGRwkbGeometryType>(eType + 1000);
    7716             :     }
    7717        1671 :     return static_cast<OGRwkbGeometryType>(eType + 2000);
    7718             : }
    7719             : 
    7720             : /************************************************************************/
    7721             : /*                        OGR_GT_SetModifier()                          */
    7722             : /************************************************************************/
    7723             : /**
    7724             :  * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
    7725             :  *
    7726             :  * @param eType Input geometry type
    7727             :  * @param bHasZ TRUE if the output geometry type must be 3D.
    7728             :  * @param bHasM TRUE if the output geometry type must be measured.
    7729             :  *
    7730             :  * @return Output geometry type.
    7731             :  *
    7732             :  * @since GDAL 2.0
    7733             :  */
    7734             : 
    7735        5034 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
    7736             :                                       int bHasM)
    7737             : {
    7738        5034 :     if (bHasZ && bHasM)
    7739         342 :         return OGR_GT_SetM(OGR_GT_SetZ(eType));
    7740        4692 :     else if (bHasM)
    7741         331 :         return OGR_GT_SetM(wkbFlatten(eType));
    7742        4361 :     else if (bHasZ)
    7743        1758 :         return OGR_GT_SetZ(wkbFlatten(eType));
    7744             :     else
    7745        2603 :         return wkbFlatten(eType);
    7746             : }
    7747             : 
    7748             : /************************************************************************/
    7749             : /*                        OGR_GT_IsSubClassOf)                          */
    7750             : /************************************************************************/
    7751             : /**
    7752             :  * \brief Returns if a type is a subclass of another one
    7753             :  *
    7754             :  * @param eType Type.
    7755             :  * @param eSuperType Super type
    7756             :  *
    7757             :  * @return TRUE if eType is a subclass of eSuperType.
    7758             :  *
    7759             :  * @since GDAL 2.0
    7760             :  */
    7761             : 
    7762      112266 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
    7763             : {
    7764      112266 :     eSuperType = wkbFlatten(eSuperType);
    7765      112266 :     eType = wkbFlatten(eType);
    7766             : 
    7767      112266 :     if (eSuperType == eType || eSuperType == wkbUnknown)
    7768        9190 :         return TRUE;
    7769             : 
    7770      103076 :     if (eSuperType == wkbGeometryCollection)
    7771       28595 :         return eType == wkbMultiPoint || eType == wkbMultiLineString ||
    7772       58642 :                eType == wkbMultiPolygon || eType == wkbMultiCurve ||
    7773       30047 :                eType == wkbMultiSurface;
    7774             : 
    7775       73029 :     if (eSuperType == wkbCurvePolygon)
    7776       19717 :         return eType == wkbPolygon || eType == wkbTriangle;
    7777             : 
    7778       53312 :     if (eSuperType == wkbMultiCurve)
    7779         244 :         return eType == wkbMultiLineString;
    7780             : 
    7781       53068 :     if (eSuperType == wkbMultiSurface)
    7782         283 :         return eType == wkbMultiPolygon;
    7783             : 
    7784       52785 :     if (eSuperType == wkbCurve)
    7785       22008 :         return eType == wkbLineString || eType == wkbCircularString ||
    7786       22008 :                eType == wkbCompoundCurve;
    7787             : 
    7788       30777 :     if (eSuperType == wkbSurface)
    7789        3457 :         return eType == wkbCurvePolygon || eType == wkbPolygon ||
    7790        7052 :                eType == wkbTriangle || eType == wkbPolyhedralSurface ||
    7791        3595 :                eType == wkbTIN;
    7792             : 
    7793       27182 :     if (eSuperType == wkbPolygon)
    7794         216 :         return eType == wkbTriangle;
    7795             : 
    7796       26966 :     if (eSuperType == wkbPolyhedralSurface)
    7797       13867 :         return eType == wkbTIN;
    7798             : 
    7799       13099 :     return FALSE;
    7800             : }
    7801             : 
    7802             : /************************************************************************/
    7803             : /*                       OGR_GT_GetCollection()                         */
    7804             : /************************************************************************/
    7805             : /**
    7806             :  * \brief Returns the collection type that can contain the passed geometry type
    7807             :  *
    7808             :  * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
    7809             :  * wkbLineString->wkbMultiLineString,
    7810             :  * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
    7811             :  * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
    7812             :  * wkbCurvePolygon->wkbMultiSurface.
    7813             :  * In other cases, wkbUnknown is returned
    7814             :  *
    7815             :  * Passed Z, M, ZM flag is preserved.
    7816             :  *
    7817             :  *
    7818             :  * @param eType Input geometry type
    7819             :  *
    7820             :  * @return the collection type that can contain the passed geometry type or
    7821             :  * wkbUnknown
    7822             :  *
    7823             :  * @since GDAL 2.0
    7824             :  */
    7825             : 
    7826        2561 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
    7827             : {
    7828        2561 :     const bool bHasZ = wkbHasZ(eType);
    7829        2561 :     const bool bHasM = wkbHasM(eType);
    7830        2561 :     if (eType == wkbNone)
    7831           1 :         return wkbNone;
    7832        2560 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    7833        2560 :     if (eFGType == wkbPoint)
    7834          47 :         eType = wkbMultiPoint;
    7835             : 
    7836        2513 :     else if (eFGType == wkbLineString)
    7837         174 :         eType = wkbMultiLineString;
    7838             : 
    7839        2339 :     else if (eFGType == wkbPolygon)
    7840         931 :         eType = wkbMultiPolygon;
    7841             : 
    7842        1408 :     else if (eFGType == wkbTriangle)
    7843           7 :         eType = wkbTIN;
    7844             : 
    7845        1401 :     else if (OGR_GT_IsCurve(eFGType))
    7846         166 :         eType = wkbMultiCurve;
    7847             : 
    7848        1235 :     else if (OGR_GT_IsSurface(eFGType))
    7849         953 :         eType = wkbMultiSurface;
    7850             : 
    7851             :     else
    7852         282 :         return wkbUnknown;
    7853             : 
    7854        2278 :     if (bHasZ)
    7855           3 :         eType = wkbSetZ(eType);
    7856        2278 :     if (bHasM)
    7857           2 :         eType = wkbSetM(eType);
    7858             : 
    7859        2278 :     return eType;
    7860             : }
    7861             : 
    7862             : /************************************************************************/
    7863             : /*                         OGR_GT_GetSingle()                           */
    7864             : /************************************************************************/
    7865             : /**
    7866             :  * \brief Returns the non-collection type that be contained in the passed
    7867             :  * geometry type.
    7868             :  *
    7869             :  * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
    7870             :  * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
    7871             :  * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
    7872             :  * wkbGeometryCollection -> wkbUnknown
    7873             :  * In other cases, the original geometry is returned.
    7874             :  *
    7875             :  * Passed Z, M, ZM flag is preserved.
    7876             :  *
    7877             :  *
    7878             :  * @param eType Input geometry type
    7879             :  *
    7880             :  * @return the the non-collection type that be contained in the passed geometry
    7881             :  * type or wkbUnknown
    7882             :  *
    7883             :  * @since GDAL 3.11
    7884             :  */
    7885             : 
    7886          43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
    7887             : {
    7888          43 :     const bool bHasZ = wkbHasZ(eType);
    7889          43 :     const bool bHasM = wkbHasM(eType);
    7890          43 :     if (eType == wkbNone)
    7891           1 :         return wkbNone;
    7892          42 :     const OGRwkbGeometryType eFGType = wkbFlatten(eType);
    7893          42 :     if (eFGType == wkbMultiPoint)
    7894           8 :         eType = wkbPoint;
    7895             : 
    7896          34 :     else if (eFGType == wkbMultiLineString)
    7897           4 :         eType = wkbLineString;
    7898             : 
    7899          30 :     else if (eFGType == wkbMultiPolygon)
    7900           2 :         eType = wkbPolygon;
    7901             : 
    7902          28 :     else if (eFGType == wkbMultiCurve)
    7903           2 :         eType = wkbCompoundCurve;
    7904             : 
    7905          26 :     else if (eFGType == wkbMultiSurface)
    7906           2 :         eType = wkbCurvePolygon;
    7907             : 
    7908          24 :     else if (eFGType == wkbGeometryCollection)
    7909           1 :         return wkbUnknown;
    7910             : 
    7911          41 :     if (bHasZ)
    7912           3 :         eType = wkbSetZ(eType);
    7913          41 :     if (bHasM)
    7914           2 :         eType = wkbSetM(eType);
    7915             : 
    7916          41 :     return eType;
    7917             : }
    7918             : 
    7919             : /************************************************************************/
    7920             : /*                        OGR_GT_GetCurve()                             */
    7921             : /************************************************************************/
    7922             : /**
    7923             :  * \brief Returns the curve geometry type that can contain the passed geometry
    7924             :  * type
    7925             :  *
    7926             :  * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
    7927             :  * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
    7928             :  * and wkbMultiLineString->wkbMultiCurve.
    7929             :  * In other cases, the passed geometry is returned.
    7930             :  *
    7931             :  * Passed Z, M, ZM flag is preserved.
    7932             :  *
    7933             :  * @param eType Input geometry type
    7934             :  *
    7935             :  * @return the curve type that can contain the passed geometry type
    7936             :  *
    7937             :  * @since GDAL 2.0
    7938             :  */
    7939             : 
    7940          35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
    7941             : {
    7942          35 :     const bool bHasZ = wkbHasZ(eType);
    7943          35 :     const bool bHasM = wkbHasM(eType);
    7944          35 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    7945             : 
    7946          35 :     if (eFGType == wkbLineString)
    7947           3 :         eType = wkbCompoundCurve;
    7948             : 
    7949          32 :     else if (eFGType == wkbPolygon)
    7950           1 :         eType = wkbCurvePolygon;
    7951             : 
    7952          31 :     else if (eFGType == wkbTriangle)
    7953           0 :         eType = wkbCurvePolygon;
    7954             : 
    7955          31 :     else if (eFGType == wkbMultiLineString)
    7956           6 :         eType = wkbMultiCurve;
    7957             : 
    7958          25 :     else if (eFGType == wkbMultiPolygon)
    7959           4 :         eType = wkbMultiSurface;
    7960             : 
    7961          35 :     if (bHasZ)
    7962           4 :         eType = wkbSetZ(eType);
    7963          35 :     if (bHasM)
    7964           4 :         eType = wkbSetM(eType);
    7965             : 
    7966          35 :     return eType;
    7967             : }
    7968             : 
    7969             : /************************************************************************/
    7970             : /*                        OGR_GT_GetLinear()                          */
    7971             : /************************************************************************/
    7972             : /**
    7973             :  * \brief Returns the non-curve geometry type that can contain the passed
    7974             :  * geometry type
    7975             :  *
    7976             :  * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
    7977             :  * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
    7978             :  * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
    7979             :  * In other cases, the passed geometry is returned.
    7980             :  *
    7981             :  * Passed Z, M, ZM flag is preserved.
    7982             :  *
    7983             :  * @param eType Input geometry type
    7984             :  *
    7985             :  * @return the non-curve type that can contain the passed geometry type
    7986             :  *
    7987             :  * @since GDAL 2.0
    7988             :  */
    7989             : 
    7990         740 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
    7991             : {
    7992         740 :     const bool bHasZ = wkbHasZ(eType);
    7993         740 :     const bool bHasM = wkbHasM(eType);
    7994         740 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    7995             : 
    7996         740 :     if (OGR_GT_IsCurve(eFGType))
    7997          37 :         eType = wkbLineString;
    7998             : 
    7999         703 :     else if (OGR_GT_IsSurface(eFGType))
    8000          38 :         eType = wkbPolygon;
    8001             : 
    8002         665 :     else if (eFGType == wkbMultiCurve)
    8003         177 :         eType = wkbMultiLineString;
    8004             : 
    8005         488 :     else if (eFGType == wkbMultiSurface)
    8006         156 :         eType = wkbMultiPolygon;
    8007             : 
    8008         740 :     if (bHasZ)
    8009         139 :         eType = wkbSetZ(eType);
    8010         740 :     if (bHasM)
    8011          86 :         eType = wkbSetM(eType);
    8012             : 
    8013         740 :     return eType;
    8014             : }
    8015             : 
    8016             : /************************************************************************/
    8017             : /*                           OGR_GT_IsCurve()                           */
    8018             : /************************************************************************/
    8019             : 
    8020             : /**
    8021             :  * \brief Return if a geometry type is an instance of Curve
    8022             :  *
    8023             :  * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
    8024             :  * and their Z/M/ZM variant.
    8025             :  *
    8026             :  * @param eGeomType the geometry type
    8027             :  * @return TRUE if the geometry type is an instance of Curve
    8028             :  *
    8029             :  * @since GDAL 2.0
    8030             :  */
    8031             : 
    8032       21999 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
    8033             : {
    8034       21999 :     return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
    8035             : }
    8036             : 
    8037             : /************************************************************************/
    8038             : /*                         OGR_GT_IsSurface()                           */
    8039             : /************************************************************************/
    8040             : 
    8041             : /**
    8042             :  * \brief Return if a geometry type is an instance of Surface
    8043             :  *
    8044             :  * Such geometry type are wkbCurvePolygon and wkbPolygon
    8045             :  * and their Z/M/ZM variant.
    8046             :  *
    8047             :  * @param eGeomType the geometry type
    8048             :  * @return TRUE if the geometry type is an instance of Surface
    8049             :  *
    8050             :  * @since GDAL 2.0
    8051             :  */
    8052             : 
    8053        3589 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
    8054             : {
    8055        3589 :     return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
    8056             : }
    8057             : 
    8058             : /************************************************************************/
    8059             : /*                          OGR_GT_IsNonLinear()                        */
    8060             : /************************************************************************/
    8061             : 
    8062             : /**
    8063             :  * \brief Return if a geometry type is a non-linear geometry type.
    8064             :  *
    8065             :  * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
    8066             :  * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
    8067             :  * Z/M variants.
    8068             :  *
    8069             :  * @param eGeomType the geometry type
    8070             :  * @return TRUE if the geometry type is a non-linear geometry type.
    8071             :  *
    8072             :  * @since GDAL 2.0
    8073             :  */
    8074             : 
    8075      108852 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
    8076             : {
    8077      108852 :     OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
    8078      108844 :     return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
    8079      108790 :            eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
    8080      217696 :            eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
    8081      108852 :            eFGeomType == wkbMultiSurface;
    8082             : }
    8083             : 
    8084             : /************************************************************************/
    8085             : /*                          CastToError()                               */
    8086             : /************************************************************************/
    8087             : 
    8088             : //! @cond Doxygen_Suppress
    8089           0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
    8090             : {
    8091           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
    8092           0 :              poGeom->getGeometryName());
    8093           0 :     delete poGeom;
    8094           0 :     return nullptr;
    8095             : }
    8096             : 
    8097             : //! @endcond
    8098             : 
    8099             : /************************************************************************/
    8100             : /*                          OGRexportToSFCGAL()                         */
    8101             : /************************************************************************/
    8102             : 
    8103             : //! @cond Doxygen_Suppress
    8104             : sfcgal_geometry_t *
    8105           0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
    8106             : {
    8107             : #ifdef HAVE_SFCGAL
    8108             : 
    8109             :     sfcgal_init();
    8110             : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8111             : 
    8112             :     const auto exportToSFCGALViaWKB =
    8113             :         [](const OGRGeometry *geom) -> sfcgal_geometry_t *
    8114             :     {
    8115             :         if (!geom)
    8116             :             return nullptr;
    8117             : 
    8118             :         // Get WKB size and allocate buffer
    8119             :         size_t nSize = geom->WkbSize();
    8120             :         unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
    8121             : 
    8122             :         // Set export options with NDR byte order
    8123             :         OGRwkbExportOptions oOptions;
    8124             :         oOptions.eByteOrder = wkbNDR;
    8125             :         // and ISO to avoid wkb25DBit for Z geometries
    8126             :         oOptions.eWkbVariant = wkbVariantIso;
    8127             : 
    8128             :         // Export to WKB
    8129             :         sfcgal_geometry_t *sfcgalGeom = nullptr;
    8130             :         if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
    8131             :         {
    8132             :             sfcgalGeom = sfcgal_io_read_wkb(
    8133             :                 reinterpret_cast<const char *>(pabyWkb), nSize);
    8134             :         }
    8135             : 
    8136             :         CPLFree(pabyWkb);
    8137             :         return sfcgalGeom;
    8138             :     };
    8139             : 
    8140             :     // Handle special cases
    8141             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8142             :     {
    8143             :         std::unique_ptr<OGRLineString> poLS(
    8144             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8145             :         return exportToSFCGALViaWKB(poLS.get());
    8146             :     }
    8147             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8148             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8149             :     {
    8150             :         std::unique_ptr<OGRLineString> poLS(
    8151             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8152             :                 ->toLineString());
    8153             :         return exportToSFCGALViaWKB(poLS.get());
    8154             :     }
    8155             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8156             :     {
    8157             :         std::unique_ptr<OGRPolygon> poPolygon(
    8158             :             OGRGeometryFactory::forceToPolygon(
    8159             :                 poGeom->clone()->toCurvePolygon())
    8160             :                 ->toPolygon());
    8161             :         return exportToSFCGALViaWKB(poPolygon.get());
    8162             :     }
    8163             :     else
    8164             :     {
    8165             :         // Default case - direct export
    8166             :         return exportToSFCGALViaWKB(poGeom);
    8167             :     }
    8168             : #else
    8169             :     char *buffer = nullptr;
    8170             : 
    8171             :     // special cases - LinearRing, Circular String, Compound Curve, Curve
    8172             :     // Polygon
    8173             : 
    8174             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8175             :     {
    8176             :         // cast it to LineString and get the WKT
    8177             :         std::unique_ptr<OGRLineString> poLS(
    8178             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8179             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8180             :         {
    8181             :             sfcgal_geometry_t *_geometry =
    8182             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8183             :             CPLFree(buffer);
    8184             :             return _geometry;
    8185             :         }
    8186             :         else
    8187             :         {
    8188             :             CPLFree(buffer);
    8189             :             return nullptr;
    8190             :         }
    8191             :     }
    8192             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8193             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8194             :     {
    8195             :         // convert it to LineString and get the WKT
    8196             :         std::unique_ptr<OGRLineString> poLS(
    8197             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8198             :                 ->toLineString());
    8199             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8200             :         {
    8201             :             sfcgal_geometry_t *_geometry =
    8202             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8203             :             CPLFree(buffer);
    8204             :             return _geometry;
    8205             :         }
    8206             :         else
    8207             :         {
    8208             :             CPLFree(buffer);
    8209             :             return nullptr;
    8210             :         }
    8211             :     }
    8212             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8213             :     {
    8214             :         // convert it to Polygon and get the WKT
    8215             :         std::unique_ptr<OGRPolygon> poPolygon(
    8216             :             OGRGeometryFactory::forceToPolygon(
    8217             :                 poGeom->clone()->toCurvePolygon())
    8218             :                 ->toPolygon());
    8219             :         if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
    8220             :         {
    8221             :             sfcgal_geometry_t *_geometry =
    8222             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8223             :             CPLFree(buffer);
    8224             :             return _geometry;
    8225             :         }
    8226             :         else
    8227             :         {
    8228             :             CPLFree(buffer);
    8229             :             return nullptr;
    8230             :         }
    8231             :     }
    8232             :     else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
    8233             :     {
    8234             :         sfcgal_geometry_t *_geometry =
    8235             :             sfcgal_io_read_wkt(buffer, strlen(buffer));
    8236             :         CPLFree(buffer);
    8237             :         return _geometry;
    8238             :     }
    8239             :     else
    8240             :     {
    8241             :         CPLFree(buffer);
    8242             :         return nullptr;
    8243             :     }
    8244             : #endif
    8245             : #else
    8246           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8247           0 :     return nullptr;
    8248             : #endif
    8249             : }
    8250             : 
    8251             : //! @endcond
    8252             : 
    8253             : /************************************************************************/
    8254             : /*                          SFCGALexportToOGR()                         */
    8255             : /************************************************************************/
    8256             : 
    8257             : //! @cond Doxygen_Suppress
    8258           0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
    8259             :     UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
    8260             : {
    8261             : #ifdef HAVE_SFCGAL
    8262             :     if (geometry == nullptr)
    8263             :         return nullptr;
    8264             : 
    8265             :     sfcgal_init();
    8266             :     char *pabySFCGAL = nullptr;
    8267             :     size_t nLength = 0;
    8268             : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8269             : 
    8270             :     sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
    8271             : 
    8272             :     if (pabySFCGAL == nullptr || nLength == 0)
    8273             :         return nullptr;
    8274             : 
    8275             :     OGRGeometry *poGeom = nullptr;
    8276             :     OGRErr eErr = OGRGeometryFactory::createFromWkb(
    8277             :         reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
    8278             :         nLength);
    8279             : 
    8280             :     free(pabySFCGAL);
    8281             : 
    8282             :     if (eErr == OGRERR_NONE)
    8283             :     {
    8284             :         return poGeom;
    8285             :     }
    8286             :     else
    8287             :     {
    8288             :         return nullptr;
    8289             :     }
    8290             : #else
    8291             :     sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
    8292             :     char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
    8293             :     memcpy(pszWKT, pabySFCGAL, nLength);
    8294             :     pszWKT[nLength] = 0;
    8295             :     free(pabySFCGAL);
    8296             : 
    8297             :     sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
    8298             : 
    8299             :     OGRGeometry *poGeom = nullptr;
    8300             :     if (geom_type == SFCGAL_TYPE_POINT)
    8301             :     {
    8302             :         poGeom = new OGRPoint();
    8303             :     }
    8304             :     else if (geom_type == SFCGAL_TYPE_LINESTRING)
    8305             :     {
    8306             :         poGeom = new OGRLineString();
    8307             :     }
    8308             :     else if (geom_type == SFCGAL_TYPE_POLYGON)
    8309             :     {
    8310             :         poGeom = new OGRPolygon();
    8311             :     }
    8312             :     else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
    8313             :     {
    8314             :         poGeom = new OGRMultiPoint();
    8315             :     }
    8316             :     else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
    8317             :     {
    8318             :         poGeom = new OGRMultiLineString();
    8319             :     }
    8320             :     else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
    8321             :     {
    8322             :         poGeom = new OGRMultiPolygon();
    8323             :     }
    8324             :     else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
    8325             :     {
    8326             :         poGeom = new OGRGeometryCollection();
    8327             :     }
    8328             :     else if (geom_type == SFCGAL_TYPE_TRIANGLE)
    8329             :     {
    8330             :         poGeom = new OGRTriangle();
    8331             :     }
    8332             :     else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
    8333             :     {
    8334             :         poGeom = new OGRPolyhedralSurface();
    8335             :     }
    8336             :     else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
    8337             :     {
    8338             :         poGeom = new OGRTriangulatedSurface();
    8339             :     }
    8340             :     else
    8341             :     {
    8342             :         CPLFree(pszWKT);
    8343             :         return nullptr;
    8344             :     }
    8345             : 
    8346             :     const char *pszWKTTmp = pszWKT;
    8347             :     if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
    8348             :     {
    8349             :         CPLFree(pszWKT);
    8350             :         return poGeom;
    8351             :     }
    8352             :     else
    8353             :     {
    8354             :         delete poGeom;
    8355             :         CPLFree(pszWKT);
    8356             :         return nullptr;
    8357             :     }
    8358             : #endif
    8359             : #else
    8360           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8361           0 :     return nullptr;
    8362             : #endif
    8363             : }
    8364             : 
    8365             : //! @endcond
    8366             : 
    8367             : //! @cond Doxygen_Suppress
    8368        7606 : OGRBoolean OGRGeometry::IsSFCGALCompatible() const
    8369             : {
    8370        7606 :     const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
    8371        7552 :     if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
    8372             :         eGType == wkbTIN)
    8373             :     {
    8374           2 :         return TRUE;
    8375             :     }
    8376        7550 :     if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
    8377             :     {
    8378          52 :         const OGRGeometryCollection *poGC = toGeometryCollection();
    8379           9 :         bool bIsSFCGALCompatible = false;
    8380           9 :         for (auto &&poSubGeom : *poGC)
    8381             :         {
    8382             :             OGRwkbGeometryType eSubGeomType =
    8383           9 :                 wkbFlatten(poSubGeom->getGeometryType());
    8384           9 :             if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
    8385             :             {
    8386           0 :                 bIsSFCGALCompatible = true;
    8387             :             }
    8388           9 :             else if (eSubGeomType != wkbMultiPolygon)
    8389             :             {
    8390           9 :                 bIsSFCGALCompatible = false;
    8391           9 :                 break;
    8392             :             }
    8393             :         }
    8394           9 :         return bIsSFCGALCompatible;
    8395             :     }
    8396        7498 :     return FALSE;
    8397             : }
    8398             : 
    8399             : //! @endcond
    8400             : 
    8401             : /************************************************************************/
    8402             : /*                    roundCoordinatesIEEE754()                         */
    8403             : /************************************************************************/
    8404             : 
    8405             : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
    8406             :  * double-precision binary representation.
    8407             :  *
    8408             :  * Determines the number of bits (N) required to represent a coordinate value
    8409             :  * with a specified number of digits after the decimal point, and then sets all
    8410             :  * but the N most significant bits to zero. The resulting coordinate value will
    8411             :  * still round to the original value (e.g. after roundCoordinates()), but will
    8412             :  * have improved compressiblity.
    8413             :  *
    8414             :  * @param options Contains the precision requirements.
    8415             :  * @since GDAL 3.9
    8416             :  */
    8417           1 : void OGRGeometry::roundCoordinatesIEEE754(
    8418             :     const OGRGeomCoordinateBinaryPrecision &options)
    8419             : {
    8420             :     struct Quantizer : public OGRDefaultGeometryVisitor
    8421             :     {
    8422             :         const OGRGeomCoordinateBinaryPrecision &m_options;
    8423             : 
    8424           1 :         explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
    8425           1 :             : m_options(optionsIn)
    8426             :         {
    8427           1 :         }
    8428             : 
    8429             :         using OGRDefaultGeometryVisitor::visit;
    8430             : 
    8431           3 :         void visit(OGRPoint *poPoint) override
    8432             :         {
    8433           3 :             if (m_options.nXYBitPrecision != INT_MIN)
    8434             :             {
    8435             :                 uint64_t i;
    8436             :                 double d;
    8437           3 :                 d = poPoint->getX();
    8438           3 :                 memcpy(&i, &d, sizeof(i));
    8439           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8440           3 :                 memcpy(&d, &i, sizeof(i));
    8441           3 :                 poPoint->setX(d);
    8442           3 :                 d = poPoint->getY();
    8443           3 :                 memcpy(&i, &d, sizeof(i));
    8444           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8445           3 :                 memcpy(&d, &i, sizeof(i));
    8446           3 :                 poPoint->setY(d);
    8447             :             }
    8448           3 :             if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
    8449             :             {
    8450             :                 uint64_t i;
    8451             :                 double d;
    8452           3 :                 d = poPoint->getZ();
    8453           3 :                 memcpy(&i, &d, sizeof(i));
    8454           3 :                 i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
    8455           3 :                 memcpy(&d, &i, sizeof(i));
    8456           3 :                 poPoint->setZ(d);
    8457             :             }
    8458           3 :             if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
    8459             :             {
    8460             :                 uint64_t i;
    8461             :                 double d;
    8462           3 :                 d = poPoint->getM();
    8463           3 :                 memcpy(&i, &d, sizeof(i));
    8464           3 :                 i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
    8465           3 :                 memcpy(&d, &i, sizeof(i));
    8466           3 :                 poPoint->setM(d);
    8467             :             }
    8468           3 :         }
    8469             :     };
    8470             : 
    8471           2 :     Quantizer quantizer(options);
    8472           1 :     accept(&quantizer);
    8473           1 : }
    8474             : 
    8475             : /************************************************************************/
    8476             : /*                             visit()                                  */
    8477             : /************************************************************************/
    8478             : 
    8479         105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
    8480             : {
    8481        1248 :     for (auto &&oPoint : *poGeom)
    8482             :     {
    8483        1143 :         oPoint.accept(this);
    8484             :     }
    8485         105 : }
    8486             : 
    8487         104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
    8488             : {
    8489         104 :     _visit(poGeom);
    8490         104 : }
    8491             : 
    8492          80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
    8493             : {
    8494          80 :     visit(poGeom->toUpperClass());
    8495          80 : }
    8496             : 
    8497           1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
    8498             : {
    8499           1 :     _visit(poGeom);
    8500           1 : }
    8501             : 
    8502          78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
    8503             : {
    8504         159 :     for (auto &&poSubGeom : *poGeom)
    8505          81 :         poSubGeom->accept(this);
    8506          78 : }
    8507             : 
    8508          77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
    8509             : {
    8510          77 :     visit(poGeom->toUpperClass());
    8511          77 : }
    8512             : 
    8513           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
    8514             : {
    8515           1 :     visit(poGeom->toUpperClass());
    8516           1 : }
    8517             : 
    8518           8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
    8519             : {
    8520           8 :     visit(poGeom->toUpperClass());
    8521           8 : }
    8522             : 
    8523          14 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
    8524             : {
    8525          14 :     visit(poGeom->toUpperClass());
    8526          14 : }
    8527             : 
    8528          26 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
    8529             : {
    8530          75 :     for (auto &&poSubGeom : *poGeom)
    8531          49 :         poSubGeom->accept(this);
    8532          26 : }
    8533             : 
    8534           1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
    8535             : {
    8536           2 :     for (auto &&poSubGeom : *poGeom)
    8537           1 :         poSubGeom->accept(this);
    8538           1 : }
    8539             : 
    8540           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
    8541             : {
    8542           1 :     visit(poGeom->toUpperClass());
    8543           1 : }
    8544             : 
    8545           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
    8546             : {
    8547           1 :     visit(poGeom->toUpperClass());
    8548           1 : }
    8549             : 
    8550           2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
    8551             : {
    8552           2 :     visit(poGeom->toUpperClass());
    8553           2 : }
    8554             : 
    8555           2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
    8556             : {
    8557           4 :     for (auto &&poSubGeom : *poGeom)
    8558           2 :         poSubGeom->accept(this);
    8559           2 : }
    8560             : 
    8561           1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
    8562             : {
    8563           1 :     visit(poGeom->toUpperClass());
    8564           1 : }
    8565             : 
    8566         116 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
    8567             : {
    8568        2727 :     for (auto &&oPoint : *poGeom)
    8569             :     {
    8570        2611 :         oPoint.accept(this);
    8571             :     }
    8572         116 : }
    8573             : 
    8574         110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
    8575             : {
    8576         110 :     _visit(poGeom);
    8577         110 : }
    8578             : 
    8579          99 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
    8580             : {
    8581          99 :     visit(poGeom->toUpperClass());
    8582          99 : }
    8583             : 
    8584           6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
    8585             : {
    8586           6 :     _visit(poGeom);
    8587           6 : }
    8588             : 
    8589         101 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
    8590             : {
    8591         203 :     for (auto &&poSubGeom : *poGeom)
    8592         102 :         poSubGeom->accept(this);
    8593         101 : }
    8594             : 
    8595          98 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
    8596             : {
    8597          98 :     visit(poGeom->toUpperClass());
    8598          98 : }
    8599             : 
    8600          40 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
    8601             : {
    8602          40 :     visit(poGeom->toUpperClass());
    8603          40 : }
    8604             : 
    8605           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
    8606             : {
    8607           1 :     visit(poGeom->toUpperClass());
    8608           1 : }
    8609             : 
    8610           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
    8611             : {
    8612           1 :     visit(poGeom->toUpperClass());
    8613           1 : }
    8614             : 
    8615          45 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
    8616             : {
    8617         217 :     for (auto &&poSubGeom : *poGeom)
    8618         172 :         poSubGeom->accept(this);
    8619          45 : }
    8620             : 
    8621           3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
    8622             : {
    8623          14 :     for (auto &&poSubGeom : *poGeom)
    8624          11 :         poSubGeom->accept(this);
    8625           3 : }
    8626             : 
    8627           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
    8628             : {
    8629           1 :     visit(poGeom->toUpperClass());
    8630           1 : }
    8631             : 
    8632           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
    8633             : {
    8634           1 :     visit(poGeom->toUpperClass());
    8635           1 : }
    8636             : 
    8637           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
    8638             : {
    8639           2 :     visit(poGeom->toUpperClass());
    8640           2 : }
    8641             : 
    8642           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
    8643             : {
    8644           4 :     for (auto &&poSubGeom : *poGeom)
    8645           2 :         poSubGeom->accept(this);
    8646           2 : }
    8647             : 
    8648           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
    8649             : {
    8650           1 :     visit(poGeom->toUpperClass());
    8651           1 : }
    8652             : 
    8653             : /************************************************************************/
    8654             : /*                     OGRGeometryUniquePtrDeleter                      */
    8655             : /************************************************************************/
    8656             : 
    8657             : //! @cond Doxygen_Suppress
    8658        1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
    8659             : {
    8660        1333 :     delete poGeom;
    8661        1333 : }
    8662             : 
    8663             : //! @endcond
    8664             : 
    8665             : /************************************************************************/
    8666             : /*                  OGRPreparedGeometryUniquePtrDeleter                 */
    8667             : /************************************************************************/
    8668             : 
    8669             : //! @cond Doxygen_Suppress
    8670         145 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
    8671             :     OGRPreparedGeometry *poPreparedGeom) const
    8672             : {
    8673         145 :     OGRDestroyPreparedGeometry(poPreparedGeom);
    8674         145 : }
    8675             : 
    8676             : //! @endcond
    8677             : 
    8678             : /************************************************************************/
    8679             : /*                     HomogenizeDimensionalityWith()                  */
    8680             : /************************************************************************/
    8681             : 
    8682             : //! @cond Doxygen_Suppress
    8683     3150240 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
    8684             : {
    8685     3150240 :     if (poOtherGeom->Is3D() && !Is3D())
    8686     1293490 :         set3D(TRUE);
    8687             : 
    8688     3150220 :     if (poOtherGeom->IsMeasured() && !IsMeasured())
    8689         793 :         setMeasured(TRUE);
    8690             : 
    8691     3150220 :     if (!poOtherGeom->Is3D() && Is3D())
    8692         295 :         poOtherGeom->set3D(TRUE);
    8693             : 
    8694     3150220 :     if (!poOtherGeom->IsMeasured() && IsMeasured())
    8695          28 :         poOtherGeom->setMeasured(TRUE);
    8696     3150220 : }
    8697             : 
    8698             : //! @endcond
    8699             : 
    8700             : /************************************************************************/
    8701             : /*                  OGRGeomCoordinateBinaryPrecision::SetFrom()         */
    8702             : /************************************************************************/
    8703             : 
    8704             : /** Set binary precision options from resolution.
    8705             :  *
    8706             :  * @since GDAL 3.9
    8707             :  */
    8708          16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
    8709             :     const OGRGeomCoordinatePrecision &prec)
    8710             : {
    8711          16 :     if (prec.dfXYResolution != 0)
    8712             :     {
    8713          16 :         nXYBitPrecision =
    8714          16 :             static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
    8715             :     }
    8716          16 :     if (prec.dfZResolution != 0)
    8717             :     {
    8718          12 :         nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
    8719             :     }
    8720          16 :     if (prec.dfMResolution != 0)
    8721             :     {
    8722          12 :         nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
    8723             :     }
    8724          16 : }
    8725             : 
    8726             : /************************************************************************/
    8727             : /*                        OGRwkbExportOptionsCreate()                   */
    8728             : /************************************************************************/
    8729             : 
    8730             : /**
    8731             :  * \brief Create geometry WKB export options.
    8732             :  *
    8733             :  * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
    8734             :  *
    8735             :  * @return object to be freed with OGRwkbExportOptionsDestroy().
    8736             :  * @since GDAL 3.9
    8737             :  */
    8738           2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
    8739             : {
    8740           2 :     return new OGRwkbExportOptions;
    8741             : }
    8742             : 
    8743             : /************************************************************************/
    8744             : /*                        OGRwkbExportOptionsDestroy()                  */
    8745             : /************************************************************************/
    8746             : 
    8747             : /**
    8748             :  * \brief Destroy object returned by OGRwkbExportOptionsCreate()
    8749             :  *
    8750             :  * @param psOptions WKB export options
    8751             :  * @since GDAL 3.9
    8752             :  */
    8753             : 
    8754           2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
    8755             : {
    8756           2 :     delete psOptions;
    8757           2 : }
    8758             : 
    8759             : /************************************************************************/
    8760             : /*                   OGRwkbExportOptionsSetByteOrder()                  */
    8761             : /************************************************************************/
    8762             : 
    8763             : /**
    8764             :  * \brief Set the WKB byte order.
    8765             :  *
    8766             :  * @param psOptions WKB export options
    8767             :  * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
    8768             :  * Intel)
    8769             :  * @since GDAL 3.9
    8770             :  */
    8771             : 
    8772           1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
    8773             :                                      OGRwkbByteOrder eByteOrder)
    8774             : {
    8775           1 :     psOptions->eByteOrder = eByteOrder;
    8776           1 : }
    8777             : 
    8778             : /************************************************************************/
    8779             : /*                   OGRwkbExportOptionsSetVariant()                    */
    8780             : /************************************************************************/
    8781             : 
    8782             : /**
    8783             :  * \brief Set the WKB variant
    8784             :  *
    8785             :  * @param psOptions WKB export options
    8786             :  * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
    8787             :  * wkbVariantPostGIS1
    8788             :  * @since GDAL 3.9
    8789             :  */
    8790             : 
    8791           1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
    8792             :                                    OGRwkbVariant eWkbVariant)
    8793             : {
    8794           1 :     psOptions->eWkbVariant = eWkbVariant;
    8795           1 : }
    8796             : 
    8797             : /************************************************************************/
    8798             : /*                   OGRwkbExportOptionsSetPrecision()                  */
    8799             : /************************************************************************/
    8800             : 
    8801             : /**
    8802             :  * \brief Set precision options
    8803             :  *
    8804             :  * @param psOptions WKB export options
    8805             :  * @param hPrecisionOptions Precision options (might be null to reset them)
    8806             :  * @since GDAL 3.9
    8807             :  */
    8808             : 
    8809           1 : void OGRwkbExportOptionsSetPrecision(
    8810             :     OGRwkbExportOptions *psOptions,
    8811             :     OGRGeomCoordinatePrecisionH hPrecisionOptions)
    8812             : {
    8813           1 :     psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
    8814           1 :     if (hPrecisionOptions)
    8815           1 :         psOptions->sPrecision.SetFrom(*hPrecisionOptions);
    8816           1 : }
    8817             : 
    8818             : /************************************************************************/
    8819             : /*                             IsRectangle()                            */
    8820             : /************************************************************************/
    8821             : 
    8822             : /**
    8823             :  * \brief Returns whether the geometry is a polygon with 4 corners forming
    8824             :  * a rectangle.
    8825             :  *
    8826             :  * @since GDAL 3.10
    8827             :  */
    8828       52348 : bool OGRGeometry::IsRectangle() const
    8829             : {
    8830       52348 :     if (wkbFlatten(getGeometryType()) != wkbPolygon)
    8831         265 :         return false;
    8832             : 
    8833       52083 :     const OGRPolygon *poPoly = toPolygon();
    8834             : 
    8835       52083 :     if (poPoly->getNumInteriorRings() != 0)
    8836          19 :         return false;
    8837             : 
    8838       52064 :     const OGRLinearRing *poRing = poPoly->getExteriorRing();
    8839       52064 :     if (!poRing)
    8840           4 :         return false;
    8841             : 
    8842       52060 :     if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
    8843         190 :         return false;
    8844             : 
    8845             :     // If the ring has 5 points, the last should be the first.
    8846      103699 :     if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
    8847       51829 :                                         poRing->getY(0) != poRing->getY(4)))
    8848           1 :         return false;
    8849             : 
    8850             :     // Polygon with first segment in "y" direction.
    8851      103058 :     if (poRing->getX(0) == poRing->getX(1) &&
    8852      102377 :         poRing->getY(1) == poRing->getY(2) &&
    8853      154246 :         poRing->getX(2) == poRing->getX(3) &&
    8854       51188 :         poRing->getY(3) == poRing->getY(0))
    8855       51188 :         return true;
    8856             : 
    8857             :     // Polygon with first segment in "x" direction.
    8858        1297 :     if (poRing->getY(0) == poRing->getY(1) &&
    8859        1232 :         poRing->getX(1) == poRing->getX(2) &&
    8860        1913 :         poRing->getY(2) == poRing->getY(3) &&
    8861         616 :         poRing->getX(3) == poRing->getX(0))
    8862         616 :         return true;
    8863             : 
    8864          65 :     return false;
    8865             : }
    8866             : 
    8867             : /************************************************************************/
    8868             : /*                           hasEmptyParts()                            */
    8869             : /************************************************************************/
    8870             : 
    8871             : /**
    8872             :  * \brief Returns whether a geometry has empty parts/rings.
    8873             :  *
    8874             :  * Returns true if removeEmptyParts() will modify the geometry.
    8875             :  *
    8876             :  * This is different from IsEmpty().
    8877             :  *
    8878             :  * @since GDAL 3.10
    8879             :  */
    8880          20 : bool OGRGeometry::hasEmptyParts() const
    8881             : {
    8882          20 :     return false;
    8883             : }
    8884             : 
    8885             : /************************************************************************/
    8886             : /*                          removeEmptyParts()                          */
    8887             : /************************************************************************/
    8888             : 
    8889             : /**
    8890             :  * \brief Remove empty parts/rings from this geometry.
    8891             :  *
    8892             :  * @since GDAL 3.10
    8893             :  */
    8894          17 : void OGRGeometry::removeEmptyParts()
    8895             : {
    8896          17 : }
    8897             : 
    8898             : /************************************************************************/
    8899             : /*                      ~IOGRGeometryVisitor()                          */
    8900             : /************************************************************************/
    8901             : 
    8902             : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
    8903             : 
    8904             : /************************************************************************/
    8905             : /*                    ~IOGRConstGeometryVisitor()                       */
    8906             : /************************************************************************/
    8907             : 
    8908             : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;

Generated by: LCOV version 1.14