LCOV - code coverage report
Current view: top level - ogr - ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1900 2195 86.6 %
Date: 2026-04-09 00:01:40 Functions: 222 246 90.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements a few base methods on OGRGeometry.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_geometry.h"
      16             : 
      17             : #include <climits>
      18             : #include <cstdarg>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <limits>
      24             : #include <memory>
      25             : #include <optional>
      26             : #include <stdexcept>
      27             : #include <string>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_multiproc.h"
      32             : #include "cpl_string.h"
      33             : #include "ogr_api.h"
      34             : #include "ogr_core.h"
      35             : #include "ogr_geos.h"
      36             : #include "ogr_sfcgal.h"
      37             : #include "ogr_libs.h"
      38             : #include "ogr_p.h"
      39             : #include "ogr_spatialref.h"
      40             : #include "ogr_srs_api.h"
      41             : #include "ogr_wkb.h"
      42             : 
      43             : #define SFCGAL_MAKE_VERSION(major, minor, patch)                               \
      44             :     ((major) * 10000 + (minor) * 100 + (patch))
      45             : #define SFCGAL_VERSION                                                         \
      46             :     SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR,            \
      47             :                         SFCGAL_VERSION_PATCH)
      48             : 
      49             : //! @cond Doxygen_Suppress
      50             : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
      51             : //! @endcond
      52             : 
      53             : #ifdef HAVE_GEOS
      54         103 : static void OGRGEOSErrorHandler(const char *fmt, ...)
      55             : {
      56             :     va_list args;
      57             : 
      58         103 :     va_start(args, fmt);
      59         103 :     CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
      60         103 :     va_end(args);
      61         103 : }
      62             : 
      63        5527 : static void OGRGEOSWarningHandler(const char *fmt, ...)
      64             : {
      65             :     va_list args;
      66             : 
      67        5527 :     va_start(args, fmt);
      68        5527 :     CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
      69        5527 :     va_end(args);
      70        5527 : }
      71             : #endif
      72             : 
      73             : /************************************************************************/
      74             : /*                           OGRWktOptions()                            */
      75             : /************************************************************************/
      76             : 
      77       11212 : int OGRWktOptions::getDefaultPrecision()
      78             : {
      79       11212 :     return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
      80             : }
      81             : 
      82       11308 : bool OGRWktOptions::getDefaultRound()
      83             : {
      84       11308 :     return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
      85             : }
      86             : 
      87             : /************************************************************************/
      88             : /*                            OGRGeometry()                             */
      89             : /************************************************************************/
      90             : 
      91             : OGRGeometry::OGRGeometry() = default;
      92             : 
      93             : /************************************************************************/
      94             : /*                  OGRGeometry( const OGRGeometry& )                   */
      95             : /************************************************************************/
      96             : 
      97             : /**
      98             :  * \brief Copy constructor.
      99             :  */
     100             : 
     101     1825680 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
     102     1825680 :     : poSRS(other.poSRS), flags(other.flags)
     103             : {
     104     1825680 :     if (poSRS != nullptr)
     105       74903 :         const_cast<OGRSpatialReference *>(poSRS)->Reference();
     106     1825680 : }
     107             : 
     108             : /************************************************************************/
     109             : /*                     OGRGeometry( OGRGeometry&& )                     */
     110             : /************************************************************************/
     111             : 
     112             : /**
     113             :  * \brief Move constructor.
     114             :  *
     115             :  * @since GDAL 3.11
     116             :  */
     117             : 
     118      181600 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
     119      181600 :     : poSRS(other.poSRS), flags(other.flags)
     120             : {
     121      181600 :     other.poSRS = nullptr;
     122      181600 : }
     123             : 
     124             : /************************************************************************/
     125             : /*                            ~OGRGeometry()                            */
     126             : /************************************************************************/
     127             : 
     128    26081700 : OGRGeometry::~OGRGeometry()
     129             : 
     130             : {
     131    13040900 :     if (poSRS != nullptr)
     132     3582280 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     133    13040900 : }
     134             : 
     135             : /************************************************************************/
     136             : /*                    operator=( const OGRGeometry&)                    */
     137             : /************************************************************************/
     138             : 
     139             : /**
     140             :  * \brief Assignment operator.
     141             :  */
     142             : 
     143        1211 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
     144             : {
     145        1211 :     if (this != &other)
     146             :     {
     147        1211 :         empty();
     148        1211 :         assignSpatialReference(other.getSpatialReference());
     149        1211 :         flags = other.flags;
     150             :     }
     151        1211 :     return *this;
     152             : }
     153             : 
     154             : /************************************************************************/
     155             : /*                      operator=( OGRGeometry&&)                       */
     156             : /************************************************************************/
     157             : 
     158             : /**
     159             :  * \brief Move assignment operator.
     160             :  *
     161             :  * @since GDAL 3.11
     162             :  */
     163             : 
     164      123835 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
     165             : {
     166      123835 :     if (this != &other)
     167             :     {
     168      123835 :         poSRS = other.poSRS;
     169      123835 :         other.poSRS = nullptr;
     170      123835 :         flags = other.flags;
     171             :     }
     172      123835 :     return *this;
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                            dumpReadable()                            */
     177             : /************************************************************************/
     178             : 
     179             : /**
     180             :  * \brief Dump geometry in well known text format to indicated output file.
     181             :  *
     182             :  * A few options can be defined to change the default dump :
     183             :  * <ul>
     184             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     185             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     186             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     187             :  * </ul>
     188             :  *
     189             :  * This method is the same as the C function OGR_G_DumpReadable().
     190             :  *
     191             :  * @param fp the text file to write the geometry to.
     192             :  * @param pszPrefix the prefix to put on each line of output.
     193             :  * @param papszOptions NULL terminated list of options (may be NULL)
     194             :  */
     195             : 
     196           0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
     197             :                                CSLConstList papszOptions) const
     198             : 
     199             : {
     200           0 :     if (fp == nullptr)
     201           0 :         fp = stdout;
     202             : 
     203           0 :     const auto osStr = dumpReadable(pszPrefix, papszOptions);
     204           0 :     fprintf(fp, "%s", osStr.c_str());
     205           0 : }
     206             : 
     207             : /************************************************************************/
     208             : /*                            dumpReadable()                            */
     209             : /************************************************************************/
     210             : 
     211             : /**
     212             :  * \brief Dump geometry in well known text format to indicated output file.
     213             :  *
     214             :  * A few options can be defined to change the default dump :
     215             :  * <ul>
     216             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     217             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     218             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     219             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
     220             :  * in WKT (added in GDAL 3.9)</li>
     221             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
     222             :  * WKT (added in GDAL 3.9)</li>
     223             :  * </ul>
     224             :  *
     225             :  * @param pszPrefix the prefix to put on each line of output.
     226             :  * @param papszOptions NULL terminated list of options (may be NULL)
     227             :  * @return a string with the geometry representation.
     228             :  * @since GDAL 3.7
     229             :  */
     230             : 
     231         307 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
     232             :                                       CSLConstList papszOptions) const
     233             : 
     234             : {
     235         307 :     if (pszPrefix == nullptr)
     236         306 :         pszPrefix = "";
     237             : 
     238         307 :     std::string osRet;
     239             : 
     240             :     const auto exportToWktWithOpts =
     241        2044 :         [this, pszPrefix, papszOptions, &osRet](bool bIso)
     242             :     {
     243         292 :         OGRErr err(OGRERR_NONE);
     244         292 :         OGRWktOptions opts;
     245         292 :         if (const char *pszXYPrecision =
     246         292 :                 CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
     247             :         {
     248           1 :             opts.format = OGRWktFormat::F;
     249           1 :             opts.xyPrecision = atoi(pszXYPrecision);
     250             :         }
     251         292 :         if (const char *pszZPrecision =
     252         292 :                 CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
     253             :         {
     254           1 :             opts.format = OGRWktFormat::F;
     255           1 :             opts.zPrecision = atoi(pszZPrecision);
     256             :         }
     257         292 :         if (bIso)
     258         292 :             opts.variant = wkbVariantIso;
     259         584 :         std::string wkt = exportToWkt(opts, &err);
     260         292 :         if (err == OGRERR_NONE)
     261             :         {
     262         292 :             osRet = pszPrefix;
     263         292 :             osRet += wkt.data();
     264         292 :             osRet += '\n';
     265             :         }
     266         292 :     };
     267             : 
     268             :     const char *pszDisplayGeometry =
     269         307 :         CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
     270         307 :     if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
     271             :     {
     272          15 :         osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
     273          15 :         switch (getGeometryType())
     274             :         {
     275           1 :             case wkbUnknown:
     276             :             case wkbNone:
     277             :             case wkbPoint:
     278             :             case wkbPoint25D:
     279             :             case wkbPointM:
     280             :             case wkbPointZM:
     281           1 :                 break;
     282           0 :             case wkbPolyhedralSurface:
     283             :             case wkbTIN:
     284             :             case wkbPolyhedralSurfaceZ:
     285             :             case wkbTINZ:
     286             :             case wkbPolyhedralSurfaceM:
     287             :             case wkbTINM:
     288             :             case wkbPolyhedralSurfaceZM:
     289             :             case wkbTINZM:
     290             :             {
     291           0 :                 const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
     292             :                 osRet +=
     293           0 :                     CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
     294           0 :                 for (auto &&poSubGeom : *poPS)
     295             :                 {
     296           0 :                     osRet += pszPrefix;
     297           0 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     298             :                 }
     299           0 :                 break;
     300             :             }
     301           0 :             case wkbLineString:
     302             :             case wkbLineString25D:
     303             :             case wkbLineStringM:
     304             :             case wkbLineStringZM:
     305             :             case wkbCircularString:
     306             :             case wkbCircularStringZ:
     307             :             case wkbCircularStringM:
     308             :             case wkbCircularStringZM:
     309             :             {
     310           0 :                 const OGRSimpleCurve *poSC = toSimpleCurve();
     311           0 :                 osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
     312           0 :                 break;
     313             :             }
     314          11 :             case wkbPolygon:
     315             :             case wkbTriangle:
     316             :             case wkbTriangleZ:
     317             :             case wkbTriangleM:
     318             :             case wkbTriangleZM:
     319             :             case wkbPolygon25D:
     320             :             case wkbPolygonM:
     321             :             case wkbPolygonZM:
     322             :             case wkbCurvePolygon:
     323             :             case wkbCurvePolygonZ:
     324             :             case wkbCurvePolygonM:
     325             :             case wkbCurvePolygonZM:
     326             :             {
     327          11 :                 const OGRCurvePolygon *poPoly = toCurvePolygon();
     328          11 :                 const OGRCurve *poRing = poPoly->getExteriorRingCurve();
     329          11 :                 const int nRings = poPoly->getNumInteriorRings();
     330          11 :                 if (poRing == nullptr)
     331             :                 {
     332           0 :                     osRet += "empty";
     333             :                 }
     334             :                 else
     335             :                 {
     336          11 :                     osRet += CPLOPrintf("%d points", poRing->getNumPoints());
     337          11 :                     if (wkbFlatten(poRing->getGeometryType()) ==
     338             :                         wkbCompoundCurve)
     339             :                     {
     340           0 :                         osRet += " (";
     341           0 :                         osRet += poRing->dumpReadable(nullptr, papszOptions);
     342           0 :                         osRet += ")";
     343             :                     }
     344          11 :                     if (nRings)
     345             :                     {
     346           1 :                         osRet += CPLOPrintf(", %d inner rings (", nRings);
     347           8 :                         for (int ir = 0; ir < nRings; ir++)
     348             :                         {
     349           7 :                             poRing = poPoly->getInteriorRingCurve(ir);
     350           7 :                             if (ir)
     351           6 :                                 osRet += ", ";
     352             :                             osRet +=
     353           7 :                                 CPLOPrintf("%d points", poRing->getNumPoints());
     354           7 :                             if (wkbFlatten(poRing->getGeometryType()) ==
     355             :                                 wkbCompoundCurve)
     356             :                             {
     357           2 :                                 osRet += " (";
     358             :                                 osRet +=
     359           2 :                                     poRing->dumpReadable(nullptr, papszOptions);
     360           2 :                                 osRet += ")";
     361             :                             }
     362             :                         }
     363           1 :                         osRet += ")";
     364             :                     }
     365             :                 }
     366          11 :                 osRet += "\n";
     367          11 :                 break;
     368             :             }
     369           2 :             case wkbCompoundCurve:
     370             :             case wkbCompoundCurveZ:
     371             :             case wkbCompoundCurveM:
     372             :             case wkbCompoundCurveZM:
     373             :             {
     374           2 :                 const OGRCompoundCurve *poCC = toCompoundCurve();
     375           2 :                 if (poCC->getNumCurves() == 0)
     376             :                 {
     377           0 :                     osRet += "empty";
     378             :                 }
     379             :                 else
     380             :                 {
     381           6 :                     for (int i = 0; i < poCC->getNumCurves(); i++)
     382             :                     {
     383           4 :                         if (i)
     384           2 :                             osRet += ", ";
     385             :                         osRet +=
     386           8 :                             CPLOPrintf("%s (%d points)",
     387           4 :                                        poCC->getCurve(i)->getGeometryName(),
     388           8 :                                        poCC->getCurve(i)->getNumPoints());
     389             :                     }
     390             :                 }
     391           2 :                 break;
     392             :             }
     393             : 
     394           1 :             case wkbMultiPoint:
     395             :             case wkbMultiLineString:
     396             :             case wkbMultiPolygon:
     397             :             case wkbMultiCurve:
     398             :             case wkbMultiSurface:
     399             :             case wkbGeometryCollection:
     400             :             case wkbMultiPoint25D:
     401             :             case wkbMultiLineString25D:
     402             :             case wkbMultiPolygon25D:
     403             :             case wkbMultiCurveZ:
     404             :             case wkbMultiSurfaceZ:
     405             :             case wkbGeometryCollection25D:
     406             :             case wkbMultiPointM:
     407             :             case wkbMultiLineStringM:
     408             :             case wkbMultiPolygonM:
     409             :             case wkbMultiCurveM:
     410             :             case wkbMultiSurfaceM:
     411             :             case wkbGeometryCollectionM:
     412             :             case wkbMultiPointZM:
     413             :             case wkbMultiLineStringZM:
     414             :             case wkbMultiPolygonZM:
     415             :             case wkbMultiCurveZM:
     416             :             case wkbMultiSurfaceZM:
     417             :             case wkbGeometryCollectionZM:
     418             :             {
     419           1 :                 const OGRGeometryCollection *poColl = toGeometryCollection();
     420             :                 osRet +=
     421           1 :                     CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
     422           2 :                 for (auto &&poSubGeom : *poColl)
     423             :                 {
     424           1 :                     osRet += pszPrefix;
     425           1 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     426             :                 }
     427           1 :                 break;
     428             :             }
     429           0 :             case wkbLinearRing:
     430             :             case wkbCurve:
     431             :             case wkbSurface:
     432             :             case wkbCurveZ:
     433             :             case wkbSurfaceZ:
     434             :             case wkbCurveM:
     435             :             case wkbSurfaceM:
     436             :             case wkbCurveZM:
     437             :             case wkbSurfaceZM:
     438           0 :                 break;
     439          15 :         }
     440             :     }
     441         292 :     else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
     442             :     {
     443           0 :         exportToWktWithOpts(/* bIso=*/false);
     444             :     }
     445         292 :     else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
     446           0 :              EQUAL(pszDisplayGeometry, "ISO_WKT"))
     447             :     {
     448         292 :         exportToWktWithOpts(/* bIso=*/true);
     449             :     }
     450             : 
     451         614 :     return osRet;
     452             : }
     453             : 
     454             : /************************************************************************/
     455             : /*                         OGR_G_DumpReadable()                         */
     456             : /************************************************************************/
     457             : /**
     458             :  * \brief Dump geometry in well known text format to indicated output file.
     459             :  *
     460             :  * This method is the same as the CPP method OGRGeometry::dumpReadable.
     461             :  *
     462             :  * @param hGeom handle on the geometry to dump.
     463             :  * @param fp the text file to write the geometry to.
     464             :  * @param pszPrefix the prefix to put on each line of output.
     465             :  */
     466             : 
     467           0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
     468             : 
     469             : {
     470           0 :     VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
     471             : 
     472           0 :     OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
     473             : }
     474             : 
     475             : /************************************************************************/
     476             : /*                       assignSpatialReference()                       */
     477             : /************************************************************************/
     478             : 
     479             : /**
     480             :  * \brief Assign spatial reference to this object.
     481             :  *
     482             :  * Any existing spatial reference
     483             :  * is replaced, but under no circumstances does this result in the object
     484             :  * being reprojected.  It is just changing the interpretation of the existing
     485             :  * geometry.  Note that assigning a spatial reference increments the
     486             :  * reference count on the OGRSpatialReference, but does not copy it.
     487             :  *
     488             :  * This will also assign the spatial reference to
     489             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     490             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     491             :  * derived classes).
     492             :  *
     493             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     494             :  *
     495             :  * This method is the same as the C function OGR_G_AssignSpatialReference().
     496             :  *
     497             :  * @param poSR new spatial reference system to apply.
     498             :  */
     499             : 
     500     5658160 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
     501             : 
     502             : {
     503             :     // Do in that order to properly handle poSR == poSRS
     504     5658160 :     if (poSR != nullptr)
     505     3543380 :         const_cast<OGRSpatialReference *>(poSR)->Reference();
     506     5658160 :     if (poSRS != nullptr)
     507       36007 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     508             : 
     509     5658160 :     poSRS = poSR;
     510     5658160 : }
     511             : 
     512             : /************************************************************************/
     513             : /*                    OGR_G_AssignSpatialReference()                    */
     514             : /************************************************************************/
     515             : /**
     516             :  * \brief Assign spatial reference to this object.
     517             :  *
     518             :  * Any existing spatial reference
     519             :  * is replaced, but under no circumstances does this result in the object
     520             :  * being reprojected.  It is just changing the interpretation of the existing
     521             :  * geometry.  Note that assigning a spatial reference increments the
     522             :  * reference count on the OGRSpatialReference, but does not copy it.
     523             :  *
     524             :  * This will also assign the spatial reference to
     525             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     526             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     527             :  * derived classes).
     528             :  *
     529             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     530             :  *
     531             :  * This function is the same as the CPP method
     532             :  * OGRGeometry::assignSpatialReference.
     533             :  *
     534             :  * @param hGeom handle on the geometry to apply the new spatial reference
     535             :  * system.
     536             :  * @param hSRS handle on the new spatial reference system to apply.
     537             :  */
     538             : 
     539          80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     540             : 
     541             : {
     542          80 :     VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
     543             : 
     544         160 :     OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
     545          80 :         OGRSpatialReference::FromHandle(hSRS));
     546             : }
     547             : 
     548             : /************************************************************************/
     549             : /*                             Intersects()                             */
     550             : /************************************************************************/
     551             : 
     552             : /**
     553             :  * \brief Do these features intersect?
     554             :  *
     555             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     556             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     557             :  * envelopes (bounding boxes) of the two geometries overlap.
     558             :  *
     559             :  * The poOtherGeom argument may be safely NULL, but in this case the method
     560             :  * will always return TRUE.   That is, a NULL geometry is treated as being
     561             :  * everywhere.
     562             :  *
     563             :  * This method is the same as the C function OGR_G_Intersects().
     564             :  *
     565             :  * @param poOtherGeom the other geometry to test against.
     566             :  *
     567             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     568             :  */
     569             : 
     570          44 : OGRBoolean OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
     571             : 
     572             : {
     573          44 :     if (poOtherGeom == nullptr)
     574           0 :         return TRUE;
     575             : 
     576          44 :     OGREnvelope oEnv1;
     577          44 :     getEnvelope(&oEnv1);
     578             : 
     579          44 :     OGREnvelope oEnv2;
     580          44 :     poOtherGeom->getEnvelope(&oEnv2);
     581             : 
     582          44 :     if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
     583          26 :         oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
     584          18 :         return FALSE;
     585             : 
     586             : #ifndef HAVE_GEOS
     587             :     // Without GEOS we assume that envelope overlap is equivalent to
     588             :     // actual intersection.
     589             :     return TRUE;
     590             : #else
     591             : 
     592          26 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
     593          26 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
     594          26 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
     595             : 
     596          26 :     OGRBoolean bResult = FALSE;
     597          26 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
     598             :     {
     599          26 :         bResult =
     600          26 :             GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
     601             :     }
     602             : 
     603          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
     604          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
     605          26 :     freeGEOSContext(hGEOSCtxt);
     606             : 
     607          26 :     return bResult;
     608             : #endif  // HAVE_GEOS
     609             : }
     610             : 
     611             : // Old API compatibility function.
     612             : 
     613             : //! @cond Doxygen_Suppress
     614           0 : OGRBoolean OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
     615             : 
     616             : {
     617           0 :     return Intersects(poOtherGeom);
     618             : }
     619             : 
     620             : //! @endcond
     621             : 
     622             : /************************************************************************/
     623             : /*                          OGR_G_Intersects()                          */
     624             : /************************************************************************/
     625             : /**
     626             :  * \brief Do these features intersect?
     627             :  *
     628             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     629             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     630             :  * envelopes (bounding boxes) of the two geometries overlap.
     631             :  *
     632             :  * This function is the same as the CPP method OGRGeometry::Intersects.
     633             :  *
     634             :  * @param hGeom handle on the first geometry.
     635             :  * @param hOtherGeom handle on the other geometry to test against.
     636             :  *
     637             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     638             :  */
     639             : 
     640          11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     641             : 
     642             : {
     643          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
     644          11 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
     645             : 
     646          22 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     647          11 :         OGRGeometry::FromHandle(hOtherGeom));
     648             : }
     649             : 
     650             : //! @cond Doxygen_Suppress
     651           0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     652             : 
     653             : {
     654           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
     655           0 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
     656             : 
     657           0 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     658           0 :         OGRGeometry::FromHandle(hOtherGeom));
     659             : }
     660             : 
     661             : //! @endcond
     662             : 
     663             : /************************************************************************/
     664             : /*                            transformTo()                             */
     665             : /************************************************************************/
     666             : 
     667             : /**
     668             :  * \brief Transform geometry to new spatial reference system.
     669             :  *
     670             :  * This method will transform the coordinates of a geometry from
     671             :  * their current spatial reference system to a new target spatial
     672             :  * reference system.  Normally this means reprojecting the vectors,
     673             :  * but it could include datum shifts, and changes of units.
     674             :  *
     675             :  * This method will only work if the geometry already has an assigned
     676             :  * spatial reference system, and if it is transformable to the target
     677             :  * coordinate system.
     678             :  *
     679             :  * Because this method requires internal creation and initialization of an
     680             :  * OGRCoordinateTransformation object it is significantly more expensive to
     681             :  * use this method to transform many geometries than it is to create the
     682             :  * OGRCoordinateTransformation in advance, and call transform() with that
     683             :  * transformation.  This method exists primarily for convenience when only
     684             :  * transforming a single geometry.
     685             :  *
     686             :  * This method is the same as the C function OGR_G_TransformTo().
     687             :  *
     688             :  * @param poSR spatial reference system to transform to.
     689             :  *
     690             :  * @return OGRERR_NONE on success, or an error code.
     691             :  */
     692             : 
     693          27 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
     694             : 
     695             : {
     696          27 :     if (getSpatialReference() == nullptr)
     697             :     {
     698           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
     699           1 :         return OGRERR_FAILURE;
     700             :     }
     701             : 
     702          26 :     if (poSR == nullptr)
     703             :     {
     704           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
     705           0 :         return OGRERR_FAILURE;
     706             :     }
     707             : 
     708             :     OGRCoordinateTransformation *poCT =
     709          26 :         OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
     710          26 :     if (poCT == nullptr)
     711           0 :         return OGRERR_FAILURE;
     712             : 
     713          26 :     const OGRErr eErr = transform(poCT);
     714             : 
     715          26 :     delete poCT;
     716             : 
     717          26 :     return eErr;
     718             : }
     719             : 
     720             : /************************************************************************/
     721             : /*                         OGR_G_TransformTo()                          */
     722             : /************************************************************************/
     723             : /**
     724             :  * \brief Transform geometry to new spatial reference system.
     725             :  *
     726             :  * This function will transform the coordinates of a geometry from
     727             :  * their current spatial reference system to a new target spatial
     728             :  * reference system.  Normally this means reprojecting the vectors,
     729             :  * but it could include datum shifts, and changes of units.
     730             :  *
     731             :  * This function will only work if the geometry already has an assigned
     732             :  * spatial reference system, and if it is transformable to the target
     733             :  * coordinate system.
     734             :  *
     735             :  * Because this function requires internal creation and initialization of an
     736             :  * OGRCoordinateTransformation object it is significantly more expensive to
     737             :  * use this function to transform many geometries than it is to create the
     738             :  * OGRCoordinateTransformation in advance, and call transform() with that
     739             :  * transformation.  This function exists primarily for convenience when only
     740             :  * transforming a single geometry.
     741             :  *
     742             :  * This function is the same as the CPP method OGRGeometry::transformTo.
     743             :  *
     744             :  * @param hGeom handle on the geometry to apply the transform to.
     745             :  * @param hSRS handle on the spatial reference system to apply.
     746             :  *
     747             :  * @return OGRERR_NONE on success, or an error code.
     748             :  */
     749             : 
     750           9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     751             : 
     752             : {
     753           9 :     VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
     754             : 
     755          18 :     return OGRGeometry::FromHandle(hGeom)->transformTo(
     756          18 :         OGRSpatialReference::FromHandle(hSRS));
     757             : }
     758             : 
     759             : /**
     760             :  * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
     761             :  *
     762             :  * \brief Apply arbitrary coordinate transformation to geometry.
     763             :  *
     764             :  * This method will transform the coordinates of a geometry from
     765             :  * their current spatial reference system to a new target spatial
     766             :  * reference system.  Normally this means reprojecting the vectors,
     767             :  * but it could include datum shifts, and changes of units.
     768             :  *
     769             :  * Note that this method does not require that the geometry already
     770             :  * have a spatial reference system.  It will be assumed that they can
     771             :  * be treated as having the source spatial reference system of the
     772             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     773             :  * will be ignored.  On successful completion the output OGRSpatialReference
     774             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     775             :  *
     776             :  * This method only does reprojection on a point-by-point basis. It does not
     777             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     778             :  * For that, use the OGRGeometryFactory::transformWithOptions() method.
     779             :  *
     780             :  * This method is the same as the C function OGR_G_Transform().
     781             :  *
     782             :  * @param poCT the transformation to apply.
     783             :  *
     784             :  * @return OGRERR_NONE on success or an error code.
     785             :  */
     786             : 
     787             : /************************************************************************/
     788             : /*                          OGR_G_Transform()                           */
     789             : /************************************************************************/
     790             : /**
     791             :  * \brief Apply arbitrary coordinate transformation to geometry.
     792             :  *
     793             :  * This function will transform the coordinates of a geometry from
     794             :  * their current spatial reference system to a new target spatial
     795             :  * reference system.  Normally this means reprojecting the vectors,
     796             :  * but it could include datum shifts, and changes of units.
     797             :  *
     798             :  * Note that this function does not require that the geometry already
     799             :  * have a spatial reference system.  It will be assumed that they can
     800             :  * be treated as having the source spatial reference system of the
     801             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     802             :  * will be ignored.  On successful completion the output OGRSpatialReference
     803             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     804             :  *
     805             :  * This function only does reprojection on a point-by-point basis. It does not
     806             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     807             :  * For that, use the OGR_GeomTransformer_Create() and
     808             :  * OGR_GeomTransformer_Transform() functions.
     809             :  *
     810             :  * This function is the same as the CPP method OGRGeometry::transform.
     811             :  *
     812             :  * @param hGeom handle on the geometry to apply the transform to.
     813             :  * @param hTransform handle on the transformation to apply.
     814             :  *
     815             :  * @return OGRERR_NONE on success or an error code.
     816             :  */
     817             : 
     818          11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
     819             :                        OGRCoordinateTransformationH hTransform)
     820             : 
     821             : {
     822          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
     823             : 
     824          22 :     return OGRGeometry::FromHandle(hGeom)->transform(
     825          11 :         OGRCoordinateTransformation::FromHandle(hTransform));
     826             : }
     827             : 
     828             : /**
     829             :  * \fn int OGRGeometry::getDimension() const;
     830             :  *
     831             :  * \brief Get the dimension of this object.
     832             :  *
     833             :  * This method corresponds to the SFCOM IGeometry::GetDimension() method.
     834             :  * It indicates the dimension of the object, but does not indicate the
     835             :  * dimension of the underlying space (as indicated by
     836             :  * OGRGeometry::getCoordinateDimension()).
     837             :  *
     838             :  * This method is the same as the C function OGR_G_GetDimension().
     839             :  *
     840             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     841             :  */
     842             : 
     843             : /**
     844             :  * \brief Get the geometry type that conforms with ISO SQL/MM Part3
     845             :  *
     846             :  * @return the geometry type that conforms with ISO SQL/MM Part3
     847             :  */
     848      720723 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
     849             : {
     850      720723 :     OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
     851             : 
     852      720723 :     if (flags & OGR_G_3D)
     853      214215 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
     854      720723 :     if (flags & OGR_G_MEASURED)
     855       26025 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
     856             : 
     857      720723 :     return nGType;
     858             : }
     859             : 
     860             : /************************************************************************/
     861             : /*                      OGRGeometry::segmentize()                       */
     862             : /************************************************************************/
     863             : /**
     864             :  *
     865             :  * \brief Modify the geometry such it has no segment longer then the
     866             :  * given distance.
     867             :  *
     868             :  * This method modifies the geometry to add intermediate vertices if necessary
     869             :  * so that the maximum length between 2 consecutive vertices is lower than
     870             :  * dfMaxLength.
     871             :  *
     872             :  * Interpolated points will have Z and M values (if needed) set to 0.
     873             :  * Distance computation is performed in 2d only
     874             :  *
     875             :  * This function is the same as the C function OGR_G_Segmentize()
     876             :  *
     877             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     878             :  * @return (since 3.10) true in case of success, false in case of error.
     879             :  */
     880             : 
     881           0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
     882             : {
     883             :     // Do nothing.
     884           0 :     return true;
     885             : }
     886             : 
     887             : /************************************************************************/
     888             : /*                          OGR_G_Segmentize()                          */
     889             : /************************************************************************/
     890             : 
     891             : /**
     892             :  *
     893             :  * \brief Modify the geometry such it has no segment longer then the given
     894             :  * distance.
     895             :  *
     896             :  * Interpolated points will have Z and M values (if needed) set to 0.
     897             :  * Distance computation is performed in 2d only.
     898             :  *
     899             :  * This function is the same as the CPP method OGRGeometry::segmentize().
     900             :  *
     901             :  * @param hGeom handle on the geometry to segmentize
     902             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     903             :  */
     904             : 
     905          24 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
     906             : {
     907          24 :     VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
     908             : 
     909          24 :     if (dfMaxLength <= 0)
     910             :     {
     911           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     912             :                  "dfMaxLength must be strictly positive");
     913           0 :         return;
     914             :     }
     915          24 :     OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
     916             : }
     917             : 
     918             : /************************************************************************/
     919             : /*                         OGR_G_GetDimension()                         */
     920             : /************************************************************************/
     921             : /**
     922             :  *
     923             :  * \brief Get the dimension of this geometry.
     924             :  *
     925             :  * This function corresponds to the SFCOM IGeometry::GetDimension() method.
     926             :  * It indicates the dimension of the geometry, but does not indicate the
     927             :  * dimension of the underlying space (as indicated by
     928             :  * OGR_G_GetCoordinateDimension() function).
     929             :  *
     930             :  * This function is the same as the CPP method OGRGeometry::getDimension().
     931             :  *
     932             :  * @param hGeom handle on the geometry to get the dimension from.
     933             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     934             :  */
     935             : 
     936          21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
     937             : 
     938             : {
     939          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
     940             : 
     941          21 :     return OGRGeometry::FromHandle(hGeom)->getDimension();
     942             : }
     943             : 
     944             : /************************************************************************/
     945             : /*                       getCoordinateDimension()                       */
     946             : /************************************************************************/
     947             : /**
     948             :  * \brief Get the dimension of the coordinates in this object.
     949             :  *
     950             :  * This method is the same as the C function OGR_G_GetCoordinateDimension().
     951             :  *
     952             :  * @deprecated use CoordinateDimension().
     953             :  *
     954             :  * @return this will return 2 or 3.
     955             :  */
     956             : 
     957      607261 : int OGRGeometry::getCoordinateDimension() const
     958             : 
     959             : {
     960      607261 :     return (flags & OGR_G_3D) ? 3 : 2;
     961             : }
     962             : 
     963             : /************************************************************************/
     964             : /*                        CoordinateDimension()                         */
     965             : /************************************************************************/
     966             : /**
     967             :  * \brief Get the dimension of the coordinates in this object.
     968             :  *
     969             :  * This method is the same as the C function OGR_G_CoordinateDimension().
     970             :  *
     971             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
     972             :  *
     973             :  */
     974             : 
     975       51750 : int OGRGeometry::CoordinateDimension() const
     976             : 
     977             : {
     978       51750 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     979        7375 :         return 4;
     980       44375 :     else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
     981        6863 :         return 3;
     982             :     else
     983       37512 :         return 2;
     984             : }
     985             : 
     986             : /************************************************************************/
     987             : /*                    OGR_G_GetCoordinateDimension()                    */
     988             : /************************************************************************/
     989             : /**
     990             :  *
     991             :  * \brief Get the dimension of the coordinates in this geometry.
     992             :  *
     993             :  * This function is the same as the CPP method
     994             :  * OGRGeometry::getCoordinateDimension().
     995             :  *
     996             :  * @param hGeom handle on the geometry to get the dimension of the
     997             :  * coordinates from.
     998             :  *
     999             :  * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
    1000             :  * OGR_G_IsMeasured().
    1001             :  *
    1002             :  * @return this will return 2 or 3.
    1003             :  */
    1004             : 
    1005         710 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
    1006             : 
    1007             : {
    1008         710 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
    1009             : 
    1010         710 :     return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
    1011             : }
    1012             : 
    1013             : /************************************************************************/
    1014             : /*                     OGR_G_CoordinateDimension()                      */
    1015             : /************************************************************************/
    1016             : /**
    1017             :  *
    1018             :  * \brief Get the dimension of the coordinates in this geometry.
    1019             :  *
    1020             :  * This function is the same as the CPP method
    1021             :  * OGRGeometry::CoordinateDimension().
    1022             :  *
    1023             :  * @param hGeom handle on the geometry to get the dimension of the
    1024             :  * coordinates from.
    1025             :  *
    1026             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
    1027             :  *
    1028             :  */
    1029             : 
    1030           4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
    1031             : 
    1032             : {
    1033           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
    1034             : 
    1035           4 :     return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
    1036             : }
    1037             : 
    1038             : /**
    1039             :  *
    1040             :  * \brief See whether this geometry has Z coordinates.
    1041             :  *
    1042             :  * This function is the same as the CPP method
    1043             :  * OGRGeometry::Is3D().
    1044             :  *
    1045             :  * @param hGeom handle on the geometry to check whether it has Z coordinates.
    1046             :  *
    1047             :  * @return TRUE if the geometry has Z coordinates.
    1048             :  */
    1049             : 
    1050       37714 : int OGR_G_Is3D(OGRGeometryH hGeom)
    1051             : 
    1052             : {
    1053       37714 :     VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
    1054             : 
    1055       37714 :     return OGRGeometry::FromHandle(hGeom)->Is3D();
    1056             : }
    1057             : 
    1058             : /**
    1059             :  *
    1060             :  * \brief See whether this geometry is measured.
    1061             :  *
    1062             :  * This function is the same as the CPP method
    1063             :  * OGRGeometry::IsMeasured().
    1064             :  *
    1065             :  * @param hGeom handle on the geometry to check whether it is measured.
    1066             :  *
    1067             :  * @return TRUE if the geometry has M coordinates.
    1068             :  */
    1069             : 
    1070       40125 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
    1071             : 
    1072             : {
    1073       40125 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
    1074             : 
    1075       40125 :     return OGRGeometry::FromHandle(hGeom)->IsMeasured();
    1076             : }
    1077             : 
    1078             : /************************************************************************/
    1079             : /*                       setCoordinateDimension()                       */
    1080             : /************************************************************************/
    1081             : 
    1082             : /**
    1083             :  * \brief Set the coordinate dimension.
    1084             :  *
    1085             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1086             :  * dimension of a geometry to 2 should zero out any existing Z values.  Setting
    1087             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1088             :  * will affect the children geometries.
    1089             :  * This will also remove the M dimension if present before this call.
    1090             :  *
    1091             :  * @deprecated use set3D() or setMeasured().
    1092             :  *
    1093             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1094             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1095             :  */
    1096             : 
    1097       82718 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
    1098             : 
    1099             : {
    1100       82718 :     if (nNewDimension == 2)
    1101       82226 :         flags &= ~OGR_G_3D;
    1102             :     else
    1103         492 :         flags |= OGR_G_3D;
    1104       82718 :     return setMeasured(FALSE);
    1105             : }
    1106             : 
    1107             : /**
    1108             :  * \brief Add or remove the Z coordinate dimension.
    1109             :  *
    1110             :  * This method adds or removes the explicit Z coordinate dimension.
    1111             :  * Removing the Z coordinate dimension of a geometry will remove any
    1112             :  * existing Z values.  Adding the Z dimension to a geometry
    1113             :  * collection, a compound curve, a polygon, etc.  will affect the
    1114             :  * children geometries.
    1115             :  *
    1116             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1117             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1118             :  */
    1119             : 
    1120     1617930 : bool OGRGeometry::set3D(OGRBoolean bIs3D)
    1121             : 
    1122             : {
    1123     1617930 :     if (bIs3D)
    1124     1612580 :         flags |= OGR_G_3D;
    1125             :     else
    1126        5346 :         flags &= ~OGR_G_3D;
    1127     1617930 :     return true;
    1128             : }
    1129             : 
    1130             : /**
    1131             :  * \brief Add or remove the M coordinate dimension.
    1132             :  *
    1133             :  * This method adds or removes the explicit M coordinate dimension.
    1134             :  * Removing the M coordinate dimension of a geometry will remove any
    1135             :  * existing M values.  Adding the M dimension to a geometry
    1136             :  * collection, a compound curve, a polygon, etc.  will affect the
    1137             :  * children geometries.
    1138             :  *
    1139             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1140             :  * TRUE or FALSE.
    1141             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1142             :  */
    1143             : 
    1144      429332 : bool OGRGeometry::setMeasured(OGRBoolean bIsMeasured)
    1145             : 
    1146             : {
    1147      429332 :     if (bIsMeasured)
    1148      137729 :         flags |= OGR_G_MEASURED;
    1149             :     else
    1150      291603 :         flags &= ~OGR_G_MEASURED;
    1151      429332 :     return true;
    1152             : }
    1153             : 
    1154             : /************************************************************************/
    1155             : /*                    OGR_G_SetCoordinateDimension()                    */
    1156             : /************************************************************************/
    1157             : 
    1158             : /**
    1159             :  * \brief Set the coordinate dimension.
    1160             :  *
    1161             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1162             :  * dimension of a geometry to 2 should zero out any existing Z values. Setting
    1163             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1164             :  * will affect the children geometries.
    1165             :  * This will also remove the M dimension if present before this call.
    1166             :  *
    1167             :  * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
    1168             :  *
    1169             :  * @param hGeom handle on the geometry to set the dimension of the
    1170             :  * coordinates.
    1171             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1172             :  */
    1173             : 
    1174          56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
    1175             : 
    1176             : {
    1177          56 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
    1178             : 
    1179          56 :     OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
    1180             : }
    1181             : 
    1182             : /************************************************************************/
    1183             : /*                            OGR_G_Set3D()                             */
    1184             : /************************************************************************/
    1185             : 
    1186             : /**
    1187             :  * \brief Add or remove the Z coordinate dimension.
    1188             :  *
    1189             :  * This method adds or removes the explicit Z coordinate dimension.
    1190             :  * Removing the Z coordinate dimension of a geometry will remove any
    1191             :  * existing Z values.  Adding the Z dimension to a geometry
    1192             :  * collection, a compound curve, a polygon, etc.  will affect the
    1193             :  * children geometries.
    1194             :  *
    1195             :  * @param hGeom handle on the geometry to set or unset the Z dimension.
    1196             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1197             :  */
    1198             : 
    1199         154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
    1200             : 
    1201             : {
    1202         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
    1203             : 
    1204         154 :     OGRGeometry::FromHandle(hGeom)->set3D(bIs3D);
    1205             : }
    1206             : 
    1207             : /************************************************************************/
    1208             : /*                         OGR_G_SetMeasured()                          */
    1209             : /************************************************************************/
    1210             : 
    1211             : /**
    1212             :  * \brief Add or remove the M coordinate dimension.
    1213             :  *
    1214             :  * This method adds or removes the explicit M coordinate dimension.
    1215             :  * Removing the M coordinate dimension of a geometry will remove any
    1216             :  * existing M values.  Adding the M dimension to a geometry
    1217             :  * collection, a compound curve, a polygon, etc.  will affect the
    1218             :  * children geometries.
    1219             :  *
    1220             :  * @param hGeom handle on the geometry to set or unset the M dimension.
    1221             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1222             :  * TRUE or FALSE.
    1223             :  */
    1224             : 
    1225         154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
    1226             : 
    1227             : {
    1228         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
    1229             : 
    1230         154 :     OGRGeometry::FromHandle(hGeom)->setMeasured(bIsMeasured);
    1231             : }
    1232             : 
    1233             : /**
    1234             :  * \fn int OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
    1235             :  *
    1236             :  * \brief Returns TRUE if two geometries are equivalent.
    1237             :  *
    1238             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1239             :  *
    1240             :  * The comparison is done in a structural way, that is to say that the geometry
    1241             :  * types must be identical, as well as the number and ordering of sub-geometries
    1242             :  * and vertices.
    1243             :  * Or equivalently, two geometries are considered equal by this method if their
    1244             :  * WKT/WKB representation is equal.
    1245             :  * Note: this must be distinguished for equality in a spatial way (which is
    1246             :  * the purpose of the ST_Equals() operation).
    1247             :  *
    1248             :  * This method is the same as the C function OGR_G_Equals().
    1249             :  *
    1250             :  * @return TRUE if equivalent or FALSE otherwise.
    1251             :  */
    1252             : 
    1253             : // Backward compatibility method.
    1254             : 
    1255             : //! @cond Doxygen_Suppress
    1256           0 : int OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
    1257             : {
    1258           0 :     return Equals(poOtherGeom);
    1259             : }
    1260             : 
    1261             : //! @endcond
    1262             : 
    1263             : /************************************************************************/
    1264             : /*                            OGR_G_Equals()                            */
    1265             : /************************************************************************/
    1266             : 
    1267             : /**
    1268             :  * \brief Returns TRUE if two geometries are equivalent.
    1269             :  *
    1270             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1271             :  *
    1272             :  * The comparison is done in a structural way, that is to say that the geometry
    1273             :  * types must be identical, as well as the number and ordering of sub-geometries
    1274             :  * and vertices.
    1275             :  * Or equivalently, two geometries are considered equal by this method if their
    1276             :  * WKT/WKB representation is equal.
    1277             :  * Note: this must be distinguished for equality in a spatial way (which is
    1278             :  * the purpose of the ST_Equals() operation).
    1279             :  *
    1280             :  * This function is the same as the CPP method OGRGeometry::Equals() method.
    1281             :  *
    1282             :  * @param hGeom handle on the first geometry.
    1283             :  * @param hOther handle on the other geometry to test against.
    1284             :  * @return TRUE if equivalent or FALSE otherwise.
    1285             :  */
    1286             : 
    1287       28155 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
    1288             : 
    1289             : {
    1290       28155 :     VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
    1291             : 
    1292       28155 :     if (hOther == nullptr)
    1293             :     {
    1294           0 :         CPLError(CE_Failure, CPLE_ObjectNull,
    1295             :                  "hOther was NULL in OGR_G_Equals");
    1296           0 :         return 0;
    1297             :     }
    1298             : 
    1299       56310 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1300       28155 :         OGRGeometry::FromHandle(hOther));
    1301             : }
    1302             : 
    1303             : //! @cond Doxygen_Suppress
    1304           0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
    1305             : 
    1306             : {
    1307           0 :     if (hGeom == nullptr)
    1308             :     {
    1309           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
    1310           0 :         return 0;
    1311             :     }
    1312             : 
    1313           0 :     if (hOther == nullptr)
    1314             :     {
    1315           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
    1316           0 :         return 0;
    1317             :     }
    1318             : 
    1319           0 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1320           0 :         OGRGeometry::FromHandle(hOther));
    1321             : }
    1322             : 
    1323             : //! @endcond
    1324             : 
    1325             : /**
    1326             :  * \fn int OGRGeometry::WkbSize() const;
    1327             :  *
    1328             :  * \brief Returns size of related binary representation.
    1329             :  *
    1330             :  * This method returns the exact number of bytes required to hold the
    1331             :  * well known binary representation of this geometry object.  Its computation
    1332             :  * may be slightly expensive for complex geometries.
    1333             :  *
    1334             :  * This method relates to the SFCOM IWks::WkbSize() method.
    1335             :  *
    1336             :  * This method is the same as the C function OGR_G_WkbSize().
    1337             :  *
    1338             :  * @return size of binary representation in bytes.
    1339             :  */
    1340             : 
    1341             : /************************************************************************/
    1342             : /*                           OGR_G_WkbSize()                            */
    1343             : /************************************************************************/
    1344             : /**
    1345             :  * \brief Returns size of related binary representation.
    1346             :  *
    1347             :  * This function returns the exact number of bytes required to hold the
    1348             :  * well known binary representation of this geometry object.  Its computation
    1349             :  * may be slightly expensive for complex geometries.
    1350             :  *
    1351             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1352             :  *
    1353             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1354             :  *
    1355             :  * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
    1356             :  *
    1357             :  * @param hGeom handle on the geometry to get the binary size from.
    1358             :  * @return size of binary representation in bytes.
    1359             :  */
    1360             : 
    1361           1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
    1362             : 
    1363             : {
    1364           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
    1365             : 
    1366           1 :     const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
    1367           1 :     if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
    1368             :     {
    1369           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1370             :                  "OGR_G_WkbSize() would return a value beyond int range. "
    1371             :                  "Use OGR_G_WkbSizeEx() instead");
    1372           0 :         return 0;
    1373             :     }
    1374           1 :     return static_cast<int>(nSize);
    1375             : }
    1376             : 
    1377             : /************************************************************************/
    1378             : /*                          OGR_G_WkbSizeEx()                           */
    1379             : /************************************************************************/
    1380             : /**
    1381             :  * \brief Returns size of related binary representation.
    1382             :  *
    1383             :  * This function returns the exact number of bytes required to hold the
    1384             :  * well known binary representation of this geometry object.  Its computation
    1385             :  * may be slightly expensive for complex geometries.
    1386             :  *
    1387             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1388             :  *
    1389             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1390             :  *
    1391             :  * @param hGeom handle on the geometry to get the binary size from.
    1392             :  * @return size of binary representation in bytes.
    1393             :  * @since GDAL 3.3
    1394             :  */
    1395             : 
    1396       10679 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
    1397             : 
    1398             : {
    1399       10679 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
    1400             : 
    1401       10679 :     return OGRGeometry::FromHandle(hGeom)->WkbSize();
    1402             : }
    1403             : 
    1404             : /**
    1405             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
    1406             :  *
    1407             :  * \brief Computes and returns the bounding envelope for this geometry
    1408             :  * in the passed psEnvelope structure.
    1409             :  *
    1410             :  * This method is the same as the C function OGR_G_GetEnvelope().
    1411             :  *
    1412             :  * @param psEnvelope the structure in which to place the results.
    1413             :  */
    1414             : 
    1415             : /************************************************************************/
    1416             : /*                         OGR_G_GetEnvelope()                          */
    1417             : /************************************************************************/
    1418             : /**
    1419             :  * \brief Computes and returns the bounding envelope for this geometry
    1420             :  * in the passed psEnvelope structure.
    1421             :  *
    1422             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1423             :  *
    1424             :  * @param hGeom handle of the geometry to get envelope from.
    1425             :  * @param psEnvelope the structure in which to place the results.
    1426             :  */
    1427             : 
    1428       13361 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
    1429             : 
    1430             : {
    1431       13361 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
    1432             : 
    1433       13361 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1434             : }
    1435             : 
    1436             : /**
    1437             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
    1438             :  *
    1439             :  * \brief Computes and returns the bounding envelope (3D) for this
    1440             :  * geometry in the passed psEnvelope structure.
    1441             :  *
    1442             :  * This method is the same as the C function OGR_G_GetEnvelope3D().
    1443             :  *
    1444             :  * @param psEnvelope the structure in which to place the results.
    1445             :  *
    1446             :  */
    1447             : 
    1448             : /************************************************************************/
    1449             : /*                        OGR_G_GetEnvelope3D()                         */
    1450             : /************************************************************************/
    1451             : /**
    1452             :  * \brief Computes and returns the bounding envelope (3D) for this
    1453             :  * geometry in the passed psEnvelope structure.
    1454             :  *
    1455             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1456             :  *
    1457             :  * @param hGeom handle of the geometry to get envelope from.
    1458             :  * @param psEnvelope the structure in which to place the results.
    1459             :  *
    1460             :  */
    1461             : 
    1462          10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
    1463             : 
    1464             : {
    1465          10 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
    1466             : 
    1467          10 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1468             : }
    1469             : 
    1470             : /************************************************************************/
    1471             : /*                           importFromWkb()                            */
    1472             : /************************************************************************/
    1473             : 
    1474             : /**
    1475             :  * \brief Assign geometry from well known binary data.
    1476             :  *
    1477             :  * The object must have already been instantiated as the correct derived
    1478             :  * type of geometry object to match the binaries type.  This method is used
    1479             :  * by the OGRGeometryFactory class, but not normally called by application
    1480             :  * code.
    1481             :  *
    1482             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1483             :  *
    1484             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1485             :  *
    1486             :  * @param pabyData the binary input data.
    1487             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1488             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1489             :  * done for curve geometries code
    1490             :  *
    1491             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1492             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1493             :  * OGRERR_CORRUPT_DATA may be returned.
    1494             :  */
    1495             : 
    1496         492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
    1497             :                                   OGRwkbVariant eWkbVariant)
    1498             : {
    1499         492 :     size_t nBytesConsumedOutIgnored = 0;
    1500         492 :     return importFromWkb(pabyData, nSize, eWkbVariant,
    1501         984 :                          nBytesConsumedOutIgnored);
    1502             : }
    1503             : 
    1504             : /**
    1505             :  * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
    1506             :  * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
    1507             :  *
    1508             :  * \brief Assign geometry from well known binary data.
    1509             :  *
    1510             :  * The object must have already been instantiated as the correct derived
    1511             :  * type of geometry object to match the binaries type.  This method is used
    1512             :  * by the OGRGeometryFactory class, but not normally called by application
    1513             :  * code.
    1514             :  *
    1515             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1516             :  *
    1517             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1518             :  *
    1519             :  * @param pabyData the binary input data.
    1520             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1521             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1522             :  * done for curve geometries code
    1523             :  * @param nBytesConsumedOut output parameter. Number of bytes consumed.
    1524             :  *
    1525             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1526             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1527             :  * OGRERR_CORRUPT_DATA may be returned.
    1528             :  *
    1529             :  */
    1530             : 
    1531             : /************************************************************************/
    1532             : /*                        OGR_G_ImportFromWkb()                         */
    1533             : /************************************************************************/
    1534             : /**
    1535             :  * \brief Assign geometry from well known binary data.
    1536             :  *
    1537             :  * The object must have already been instantiated as the correct derived
    1538             :  * type of geometry object to match the binaries type.
    1539             :  *
    1540             :  * This function relates to the SFCOM IWks::ImportFromWKB() method.
    1541             :  *
    1542             :  * This function is the same as the CPP method OGRGeometry::importFromWkb().
    1543             :  *
    1544             :  * @param hGeom handle on the geometry to assign the well know binary data to.
    1545             :  * @param pabyData the binary input data.
    1546             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1547             :  *
    1548             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1549             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1550             :  * OGRERR_CORRUPT_DATA may be returned.
    1551             :  */
    1552             : 
    1553           0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
    1554             : 
    1555             : {
    1556           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
    1557             : 
    1558           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkb(
    1559           0 :         static_cast<const GByte *>(pabyData), nSize);
    1560             : }
    1561             : 
    1562             : /************************************************************************/
    1563             : /*                      OGRGeometry::exportToWkb()                      */
    1564             : /************************************************************************/
    1565             : 
    1566             : /* clang-format off */
    1567             : /**
    1568             :  * \brief Convert a geometry into well known binary format.
    1569             :  *
    1570             :  * This method relates to the SFCOM IWks::ExportToWKB() method.
    1571             :  *
    1572             :  * This method is the same as the C function OGR_G_ExportToWkb() or
    1573             :  * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
    1574             :  *
    1575             :  * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1576             :  *               respectively.
    1577             :  * @param pabyData a buffer into which the binary representation is
    1578             :  *                      written.  This buffer must be at least
    1579             :  *                      OGRGeometry::WkbSize() byte in size.
    1580             :  * @param eWkbVariant What standard to use when exporting geometries
    1581             :  *                      with three dimensions (or more). The default
    1582             :  *                      wkbVariantOldOgc is the historical OGR
    1583             :  *                      variant. wkbVariantIso is the variant defined
    1584             :  *                      in ISO SQL/MM and adopted by OGC for SFSQL
    1585             :  *                      1.2.
    1586             :  *
    1587             :  * @return Currently OGRERR_NONE is always returned.
    1588             :  */
    1589             : /* clang-format on */
    1590             : 
    1591      279927 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
    1592             :                                 unsigned char *pabyData,
    1593             :                                 OGRwkbVariant eWkbVariant) const
    1594             : {
    1595      279927 :     OGRwkbExportOptions sOptions;
    1596      279927 :     sOptions.eByteOrder = eByteOrder;
    1597      279927 :     sOptions.eWkbVariant = eWkbVariant;
    1598      559854 :     return exportToWkb(pabyData, &sOptions);
    1599             : }
    1600             : 
    1601             : /************************************************************************/
    1602             : /*                         OGR_G_ExportToWkb()                          */
    1603             : /************************************************************************/
    1604             : /**
    1605             :  * \brief Convert a geometry well known binary format
    1606             :  *
    1607             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1608             :  *
    1609             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1610             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1611             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1612             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
    1613             :  *
    1614             :  * This function is the same as the CPP method
    1615             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
    1616             :  * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
    1617             :  *
    1618             :  * @param hGeom handle on the geometry to convert to a well know binary
    1619             :  * data from.
    1620             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1621             :  *               respectively.
    1622             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1623             :  *                      written.  This buffer must be at least
    1624             :  *                      OGR_G_WkbSize() byte in size.
    1625             :  *
    1626             :  * @return Currently OGRERR_NONE is always returned.
    1627             :  */
    1628             : 
    1629         109 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1630             :                          unsigned char *pabyDstBuffer)
    1631             : 
    1632             : {
    1633         109 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
    1634             : 
    1635         109 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
    1636             : }
    1637             : 
    1638             : /************************************************************************/
    1639             : /*                        OGR_G_ExportToIsoWkb()                        */
    1640             : /************************************************************************/
    1641             : /**
    1642             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
    1643             :  * binary format
    1644             :  *
    1645             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1646             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
    1647             :  * types.
    1648             :  *
    1649             :  * This function is the same as the CPP method
    1650             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
    1651             :  * with eWkbVariant = wkbVariantIso.
    1652             :  *
    1653             :  * @param hGeom handle on the geometry to convert to a well know binary
    1654             :  * data from.
    1655             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1656             :  *               respectively.
    1657             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1658             :  *                      written.  This buffer must be at least
    1659             :  *                      OGR_G_WkbSize() byte in size.
    1660             :  *
    1661             :  * @return Currently OGRERR_NONE is always returned.
    1662             :  *
    1663             :  */
    1664             : 
    1665       10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1666             :                             unsigned char *pabyDstBuffer)
    1667             : 
    1668             : {
    1669       10571 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
    1670             : 
    1671       10571 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
    1672       10571 :                                                        wkbVariantIso);
    1673             : }
    1674             : 
    1675             : /************************************************************************/
    1676             : /*                        OGR_G_ExportToWkbEx()                         */
    1677             : /************************************************************************/
    1678             : 
    1679             : /* clang-format off */
    1680             : /**
    1681             :  * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
    1682             :  *
    1683             :  * \brief Convert a geometry into well known binary format
    1684             :  *
    1685             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1686             :  *
    1687             :  * This function is the same as the C function OGR_G_ExportToWkbEx().
    1688             :  *
    1689             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1690             :  *                      written.  This buffer must be at least
    1691             :  *                      OGR_G_WkbSize() byte in size.
    1692             :  * @param psOptions WKB export options.
    1693             : 
    1694             :  * @return Currently OGRERR_NONE is always returned.
    1695             :  *
    1696             :  * @since GDAL 3.9
    1697             :  */
    1698             : /* clang-format on */
    1699             : 
    1700             : /**
    1701             :  * \brief Convert a geometry into well known binary format
    1702             :  *
    1703             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1704             :  *
    1705             :  * This function is the same as the CPP method
    1706             :  * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
    1707             :  *
    1708             :  * @param hGeom handle on the geometry to convert to a well know binary
    1709             :  * data from.
    1710             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1711             :  *                      written.  This buffer must be at least
    1712             :  *                      OGR_G_WkbSize() byte in size.
    1713             :  * @param psOptions WKB export options.
    1714             : 
    1715             :  * @return Currently OGRERR_NONE is always returned.
    1716             :  *
    1717             :  * @since GDAL 3.9
    1718             :  */
    1719             : 
    1720           2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
    1721             :                            const OGRwkbExportOptions *psOptions)
    1722             : {
    1723           2 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
    1724             : 
    1725           4 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
    1726           2 :                                                        psOptions);
    1727             : }
    1728             : 
    1729             : /**
    1730             :  * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
    1731             :  *
    1732             :  * \brief Assign geometry from well known text data.
    1733             :  *
    1734             :  * The object must have already been instantiated as the correct derived
    1735             :  * type of geometry object to match the text type.  This method is used
    1736             :  * by the OGRGeometryFactory class, but not normally called by application
    1737             :  * code.
    1738             :  *
    1739             :  * This method relates to the SFCOM IWks::ImportFromWKT() method.
    1740             :  *
    1741             :  * This method is the same as the C function OGR_G_ImportFromWkt().
    1742             :  *
    1743             :  * @param ppszInput pointer to a pointer to the source text.  The pointer is
    1744             :  *                    updated to pointer after the consumed text.
    1745             :  *
    1746             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1747             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1748             :  * OGRERR_CORRUPT_DATA may be returned.
    1749             :  */
    1750             : 
    1751             : /************************************************************************/
    1752             : /*                        OGR_G_ImportFromWkt()                         */
    1753             : /************************************************************************/
    1754             : /**
    1755             :  * \brief Assign geometry from well known text data.
    1756             :  *
    1757             :  * The object must have already been instantiated as the correct derived
    1758             :  * type of geometry object to match the text type.
    1759             :  *
    1760             :  * This function relates to the SFCOM IWks::ImportFromWKT() method.
    1761             :  *
    1762             :  * This function is the same as the CPP method OGRGeometry::importFromWkt().
    1763             :  *
    1764             :  * @param hGeom handle on the geometry to assign well know text data to.
    1765             :  * @param ppszSrcText pointer to a pointer to the source text.  The pointer is
    1766             :  *                    updated to pointer after the consumed text.
    1767             :  *
    1768             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1769             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1770             :  * OGRERR_CORRUPT_DATA may be returned.
    1771             :  */
    1772             : 
    1773           0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
    1774             : 
    1775             : {
    1776           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
    1777             : 
    1778           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkt(
    1779           0 :         const_cast<const char **>(ppszSrcText));
    1780             : }
    1781             : 
    1782             : /************************************************************************/
    1783             : /*                       importPreambleFromWkt()                        */
    1784             : /************************************************************************/
    1785             : 
    1786             : // Returns -1 if processing must continue.
    1787             : //! @cond Doxygen_Suppress
    1788      123564 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
    1789             :                                           int *pbHasM, bool *pbIsEmpty)
    1790             : {
    1791      123564 :     const char *pszInput = *ppszInput;
    1792             : 
    1793             :     /* -------------------------------------------------------------------- */
    1794             :     /*      Clear existing Geoms.                                           */
    1795             :     /* -------------------------------------------------------------------- */
    1796      123564 :     empty();
    1797      123564 :     *pbIsEmpty = false;
    1798             : 
    1799             :     /* -------------------------------------------------------------------- */
    1800             :     /*      Read and verify the type keyword, and ensure it matches the     */
    1801             :     /*      actual type of this container.                                  */
    1802             :     /* -------------------------------------------------------------------- */
    1803      123564 :     bool bHasM = false;
    1804      123564 :     bool bHasZ = false;
    1805      123564 :     bool bAlreadyGotDimension = false;
    1806             : 
    1807      123564 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
    1808      123564 :     pszInput = OGRWktReadToken(pszInput, szToken);
    1809      123564 :     if (szToken[0] != '\0')
    1810             :     {
    1811             :         // Postgis EWKT: POINTM instead of POINT M.
    1812             :         // Current QGIS versions (at least <= 3.38) also export POINTZ.
    1813      123564 :         const size_t nTokenLen = strlen(szToken);
    1814      123564 :         if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
    1815             :         {
    1816          11 :             szToken[nTokenLen - 1] = '\0';
    1817          11 :             bHasM = true;
    1818          11 :             bAlreadyGotDimension = true;
    1819             : 
    1820          11 :             if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
    1821           9 :                                   szToken[nTokenLen - 2] == 'z'))
    1822             :             {
    1823           4 :                 bHasZ = true;
    1824           4 :                 szToken[nTokenLen - 2] = '\0';
    1825             :             }
    1826             :         }
    1827      123553 :         else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
    1828             :         {
    1829           6 :             szToken[nTokenLen - 1] = '\0';
    1830           6 :             bHasZ = true;
    1831           6 :             bAlreadyGotDimension = true;
    1832             :         }
    1833             :     }
    1834             : 
    1835      123564 :     if (!EQUAL(szToken, getGeometryName()))
    1836           0 :         return OGRERR_CORRUPT_DATA;
    1837             : 
    1838             :     /* -------------------------------------------------------------------- */
    1839             :     /*      Check for Z, M or ZM                                            */
    1840             :     /* -------------------------------------------------------------------- */
    1841      123564 :     if (!bAlreadyGotDimension)
    1842             :     {
    1843      123547 :         const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1844      123547 :         if (EQUAL(szToken, "Z"))
    1845             :         {
    1846        1418 :             pszInput = pszNewInput;
    1847        1418 :             bHasZ = true;
    1848             :         }
    1849      122129 :         else if (EQUAL(szToken, "M"))
    1850             :         {
    1851         353 :             pszInput = pszNewInput;
    1852         353 :             bHasM = true;
    1853             :         }
    1854      121776 :         else if (EQUAL(szToken, "ZM"))
    1855             :         {
    1856         494 :             pszInput = pszNewInput;
    1857         494 :             bHasZ = true;
    1858         494 :             bHasM = true;
    1859             :         }
    1860             :     }
    1861      123564 :     *pbHasZ = bHasZ;
    1862      123564 :     *pbHasM = bHasM;
    1863             : 
    1864             :     /* -------------------------------------------------------------------- */
    1865             :     /*      Check for EMPTY ...                                             */
    1866             :     /* -------------------------------------------------------------------- */
    1867      123564 :     const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1868      123564 :     if (EQUAL(szToken, "EMPTY"))
    1869             :     {
    1870        1577 :         *ppszInput = pszNewInput;
    1871        1577 :         *pbIsEmpty = true;
    1872        1577 :         if (bHasZ)
    1873         137 :             set3D(TRUE);
    1874        1577 :         if (bHasM)
    1875          84 :             setMeasured(TRUE);
    1876        1577 :         return OGRERR_NONE;
    1877             :     }
    1878             : 
    1879      121987 :     if (!EQUAL(szToken, "("))
    1880          35 :         return OGRERR_CORRUPT_DATA;
    1881             : 
    1882      121952 :     if (!bHasZ && !bHasM)
    1883             :     {
    1884             :         // Test for old-style XXXXXXXXX(EMPTY).
    1885      119850 :         pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1886      119850 :         if (EQUAL(szToken, "EMPTY"))
    1887             :         {
    1888          69 :             pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1889             : 
    1890          69 :             if (EQUAL(szToken, ","))
    1891             :             {
    1892             :                 // This is OK according to SFSQL SPEC.
    1893             :             }
    1894          44 :             else if (!EQUAL(szToken, ")"))
    1895             :             {
    1896           9 :                 return OGRERR_CORRUPT_DATA;
    1897             :             }
    1898             :             else
    1899             :             {
    1900          35 :                 *ppszInput = pszNewInput;
    1901          35 :                 empty();
    1902          35 :                 *pbIsEmpty = true;
    1903          35 :                 return OGRERR_NONE;
    1904             :             }
    1905             :         }
    1906             :     }
    1907             : 
    1908      121908 :     *ppszInput = pszInput;
    1909             : 
    1910      121908 :     return OGRERR_NONE;
    1911             : }
    1912             : 
    1913             : //! @endcond
    1914             : 
    1915             : /************************************************************************/
    1916             : /*                           wktTypeString()                            */
    1917             : /************************************************************************/
    1918             : 
    1919             : //! @cond Doxygen_Suppress
    1920             : /** Get a type string for WKT, padded with a space at the end.
    1921             :  *
    1922             :  * @param variant  OGR type variant
    1923             :  * @return  "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
    1924             :  */
    1925       14230 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
    1926             : {
    1927       14230 :     std::string s(" ");
    1928             : 
    1929       14230 :     if (variant == wkbVariantIso)
    1930             :     {
    1931        9346 :         if (flags & OGR_G_3D)
    1932        1957 :             s += "Z";
    1933        9346 :         if (flags & OGR_G_MEASURED)
    1934        1200 :             s += "M";
    1935             :     }
    1936       14230 :     if (s.size() > 1)
    1937        2518 :         s += " ";
    1938       14230 :     return s;
    1939             : }
    1940             : 
    1941             : //! @endcond
    1942             : 
    1943             : /**
    1944             :  * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
    1945             :  * OGRwkbVariant variant = wkbVariantOldOgc ) const;
    1946             :  *
    1947             :  * \brief Convert a geometry into well known text format.
    1948             :  *
    1949             :  * This method relates to the SFCOM IWks::ExportToWKT() method.
    1950             :  *
    1951             :  * This method is the same as the C function OGR_G_ExportToWkt().
    1952             :  *
    1953             :  * @param ppszDstText a text buffer is allocated by the program, and assigned
    1954             :  *                    to the passed pointer. After use, *ppszDstText should be
    1955             :  *                    freed with CPLFree().
    1956             :  * @param variant the specification that must be conformed too :
    1957             :  *                    - wkbVariantOgc for old-style 99-402 extended
    1958             :  *                      dimension (Z) WKB types
    1959             :  *                    - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
    1960             :  *
    1961             :  * @return Currently OGRERR_NONE is always returned.
    1962             :  */
    1963        8879 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
    1964             : {
    1965        8879 :     OGRWktOptions opts;
    1966        8879 :     opts.variant = variant;
    1967        8879 :     OGRErr err(OGRERR_NONE);
    1968             : 
    1969        8879 :     std::string wkt = exportToWkt(opts, &err);
    1970        8879 :     *ppszDstText = CPLStrdup(wkt.data());
    1971       17758 :     return err;
    1972             : }
    1973             : 
    1974             : /************************************************************************/
    1975             : /*                         OGR_G_ExportToWkt()                          */
    1976             : /************************************************************************/
    1977             : 
    1978             : /**
    1979             :  * \brief Convert a geometry into well known text format.
    1980             :  *
    1981             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    1982             :  *
    1983             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1984             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1985             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1986             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
    1987             :  *
    1988             :  * This function is the same as the CPP method OGRGeometry::exportToWkt().
    1989             :  *
    1990             :  * @param hGeom handle on the geometry to convert to a text format from.
    1991             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    1992             :  *                    to the passed pointer. After use, *ppszDstText should be
    1993             :  *                    freed with CPLFree().
    1994             :  *
    1995             :  * @return Currently OGRERR_NONE is always returned.
    1996             :  */
    1997             : 
    1998        2551 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
    1999             : 
    2000             : {
    2001        2551 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
    2002             : 
    2003        2551 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
    2004             : }
    2005             : 
    2006             : /************************************************************************/
    2007             : /*                        OGR_G_ExportToIsoWkt()                        */
    2008             : /************************************************************************/
    2009             : 
    2010             : /**
    2011             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
    2012             :  * known text format.
    2013             :  *
    2014             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    2015             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
    2016             :  * (Z&M) WKB types.
    2017             :  *
    2018             :  * This function is the same as the CPP method
    2019             :  * OGRGeometry::exportToWkt(wkbVariantIso).
    2020             :  *
    2021             :  * @param hGeom handle on the geometry to convert to a text format from.
    2022             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    2023             :  *                    to the passed pointer. After use, *ppszDstText should be
    2024             :  *                    freed with CPLFree().
    2025             :  *
    2026             :  * @return Currently OGRERR_NONE is always returned.
    2027             :  *
    2028             :  */
    2029             : 
    2030        5500 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2031             : 
    2032             : {
    2033        5500 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
    2034             : 
    2035        5500 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
    2036        5500 :                                                        wkbVariantIso);
    2037             : }
    2038             : 
    2039             : /**
    2040             :  * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
    2041             :  *
    2042             :  * \brief Fetch geometry type.
    2043             :  *
    2044             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2045             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2046             :  * to the return result.
    2047             :  *
    2048             :  * This method is the same as the C function OGR_G_GetGeometryType().
    2049             :  *
    2050             :  * @return the geometry type code.
    2051             :  */
    2052             : 
    2053             : /************************************************************************/
    2054             : /*                       OGR_G_GetGeometryType()                        */
    2055             : /************************************************************************/
    2056             : /**
    2057             :  * \brief Fetch geometry type.
    2058             :  *
    2059             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2060             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2061             :  * to the return result.
    2062             :  *
    2063             :  * This function is the same as the CPP method OGRGeometry::getGeometryType().
    2064             :  *
    2065             :  * @param hGeom handle on the geometry to get type from.
    2066             :  * @return the geometry type code.
    2067             :  */
    2068             : 
    2069        5789 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
    2070             : 
    2071             : {
    2072        5789 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
    2073             : 
    2074        5789 :     return OGRGeometry::FromHandle(hGeom)->getGeometryType();
    2075             : }
    2076             : 
    2077             : /**
    2078             :  * \fn const char * OGRGeometry::getGeometryName() const;
    2079             :  *
    2080             :  * \brief Fetch WKT name for geometry type.
    2081             :  *
    2082             :  * There is no SFCOM analog to this method.
    2083             :  *
    2084             :  * This method is the same as the C function OGR_G_GetGeometryName().
    2085             :  *
    2086             :  * @return name used for this geometry type in well known text format.  The
    2087             :  * returned pointer is to a static internal string and should not be modified
    2088             :  * or freed.
    2089             :  */
    2090             : 
    2091             : /************************************************************************/
    2092             : /*                       OGR_G_GetGeometryName()                        */
    2093             : /************************************************************************/
    2094             : /**
    2095             :  * \brief Fetch WKT name for geometry type.
    2096             :  *
    2097             :  * There is no SFCOM analog to this function.
    2098             :  *
    2099             :  * This function is the same as the CPP method OGRGeometry::getGeometryName().
    2100             :  *
    2101             :  * @param hGeom handle on the geometry to get name from.
    2102             :  * @return name used for this geometry type in well known text format.
    2103             :  */
    2104             : 
    2105       18909 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
    2106             : 
    2107             : {
    2108       18909 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
    2109             : 
    2110       18909 :     return OGRGeometry::FromHandle(hGeom)->getGeometryName();
    2111             : }
    2112             : 
    2113             : /**
    2114             :  * \fn OGRGeometry *OGRGeometry::clone() const;
    2115             :  *
    2116             :  * \brief Make a copy of this object.
    2117             :  *
    2118             :  * This method relates to the SFCOM IGeometry::clone() method.
    2119             :  *
    2120             :  * This method is the same as the C function OGR_G_Clone().
    2121             :  *
    2122             :  * @return a new object instance with the same geometry, and spatial
    2123             :  * reference system as the original.
    2124             :  */
    2125             : 
    2126             : /************************************************************************/
    2127             : /*                            OGR_G_Clone()                             */
    2128             : /************************************************************************/
    2129             : /**
    2130             :  * \brief Make a copy of this object.
    2131             :  *
    2132             :  * This function relates to the SFCOM IGeometry::clone() method.
    2133             :  *
    2134             :  * This function is the same as the CPP method OGRGeometry::clone().
    2135             :  *
    2136             :  * @param hGeom handle on the geometry to clone from.
    2137             :  * @return a handle on the copy of the geometry with the spatial
    2138             :  * reference system as the original.
    2139             :  */
    2140             : 
    2141       13564 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
    2142             : 
    2143             : {
    2144       13564 :     VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
    2145             : 
    2146       13564 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
    2147             : }
    2148             : 
    2149             : /**
    2150             :  * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
    2151             :  *
    2152             :  * \brief Returns spatial reference system for object.
    2153             :  *
    2154             :  * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
    2155             :  *
    2156             :  * This method is the same as the C function OGR_G_GetSpatialReference().
    2157             :  *
    2158             :  * @return a reference to the spatial reference object.  The object may be
    2159             :  * shared with many geometry objects, and should not be modified.
    2160             :  */
    2161             : 
    2162             : /************************************************************************/
    2163             : /*                     OGR_G_GetSpatialReference()                      */
    2164             : /************************************************************************/
    2165             : /**
    2166             :  * \brief Returns spatial reference system for geometry.
    2167             :  *
    2168             :  * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
    2169             :  *
    2170             :  * This function is the same as the CPP method
    2171             :  * OGRGeometry::getSpatialReference().
    2172             :  *
    2173             :  * @param hGeom handle on the geometry to get spatial reference from.
    2174             :  * @return a reference to the spatial reference geometry, which should not be
    2175             :  * modified.
    2176             :  */
    2177             : 
    2178          84 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
    2179             : 
    2180             : {
    2181          84 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
    2182             : 
    2183          84 :     return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
    2184         168 :         OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
    2185             : }
    2186             : 
    2187             : /**
    2188             :  * \fn void OGRGeometry::empty();
    2189             :  *
    2190             :  * \brief Clear geometry information.
    2191             :  * This restores the geometry to its initial
    2192             :  * state after construction, and before assignment of actual geometry.
    2193             :  *
    2194             :  * This method relates to the SFCOM IGeometry::Empty() method.
    2195             :  *
    2196             :  * This method is the same as the C function OGR_G_Empty().
    2197             :  */
    2198             : 
    2199             : /************************************************************************/
    2200             : /*                            OGR_G_Empty()                             */
    2201             : /************************************************************************/
    2202             : /**
    2203             :  * \brief Clear geometry information.
    2204             :  * This restores the geometry to its initial
    2205             :  * state after construction, and before assignment of actual geometry.
    2206             :  *
    2207             :  * This function relates to the SFCOM IGeometry::Empty() method.
    2208             :  *
    2209             :  * This function is the same as the CPP method OGRGeometry::empty().
    2210             :  *
    2211             :  * @param hGeom handle on the geometry to empty.
    2212             :  */
    2213             : 
    2214           4 : void OGR_G_Empty(OGRGeometryH hGeom)
    2215             : 
    2216             : {
    2217           4 :     VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
    2218             : 
    2219           4 :     OGRGeometry::FromHandle(hGeom)->empty();
    2220             : }
    2221             : 
    2222             : /**
    2223             :  * \fn OGRBoolean OGRGeometry::IsEmpty() const;
    2224             :  *
    2225             :  * \brief Returns TRUE (non-zero) if the object has no points.
    2226             :  *
    2227             :  * Normally this
    2228             :  * returns FALSE except between when an object is instantiated and points
    2229             :  * have been assigned.
    2230             :  *
    2231             :  * This method relates to the SFCOM IGeometry::IsEmpty() method.
    2232             :  *
    2233             :  * @return TRUE if object is empty, otherwise FALSE.
    2234             :  */
    2235             : 
    2236             : /************************************************************************/
    2237             : /*                           OGR_G_IsEmpty()                            */
    2238             : /************************************************************************/
    2239             : 
    2240             : /**
    2241             :  * \brief Test if the geometry is empty.
    2242             :  *
    2243             :  * This method is the same as the CPP method OGRGeometry::IsEmpty().
    2244             :  *
    2245             :  * @param hGeom The Geometry to test.
    2246             :  *
    2247             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2248             :  */
    2249             : 
    2250        2391 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
    2251             : 
    2252             : {
    2253        2391 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
    2254             : 
    2255        2391 :     return OGRGeometry::FromHandle(hGeom)->IsEmpty();
    2256             : }
    2257             : 
    2258             : /************************************************************************/
    2259             : /*                              IsValid()                               */
    2260             : /************************************************************************/
    2261             : 
    2262             : /**
    2263             :  * \brief Test if the geometry is valid.
    2264             :  *
    2265             :  * This method is the same as the C function OGR_G_IsValid().
    2266             :  *
    2267             :  * This method is built on the GEOS library, check it for the definition
    2268             :  * of the geometry operation.
    2269             :  * If OGR is built without the GEOS library, this method will always return
    2270             :  * FALSE.
    2271             :  *
    2272             :  *
    2273             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2274             :  */
    2275             : 
    2276        7485 : OGRBoolean OGRGeometry::IsValid() const
    2277             : 
    2278             : {
    2279        7485 :     if (IsSFCGALCompatible())
    2280             :     {
    2281             : #ifndef HAVE_SFCGAL
    2282             : 
    2283             : #ifdef HAVE_GEOS
    2284           2 :         if (wkbFlatten(getGeometryType()) == wkbTriangle)
    2285             :         {
    2286             :             // go on
    2287             :         }
    2288             :         else
    2289             : #endif
    2290             :         {
    2291           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2292             :                      "SFCGAL support not enabled.");
    2293           1 :             return FALSE;
    2294             :         }
    2295             : #else
    2296             :         sfcgal_init();
    2297             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    2298             :         if (poThis == nullptr)
    2299             :         {
    2300             :             CPLError(CE_Failure, CPLE_IllegalArg,
    2301             :                      "SFCGAL geometry returned is NULL");
    2302             :             return FALSE;
    2303             :         }
    2304             : 
    2305             :         const int res = sfcgal_geometry_is_valid(poThis);
    2306             :         sfcgal_geometry_delete(poThis);
    2307             :         return res == 1;
    2308             : #endif
    2309             :     }
    2310             : 
    2311             :     {
    2312             : #ifndef HAVE_GEOS
    2313             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2314             :         return FALSE;
    2315             : 
    2316             : #else
    2317        7484 :         OGRBoolean bResult = FALSE;
    2318             : 
    2319             :         // Some invalid geometries, such as lines with one point, or
    2320             :         // rings that do not close, cannot be converted to GEOS.
    2321             :         // For validity checking we initialize the GEOS context with
    2322             :         // the warning handler as the error handler to avoid emitting
    2323             :         // CE_Failure when a geometry cannot be converted to GEOS.
    2324             :         GEOSContextHandle_t hGEOSCtxt =
    2325        7484 :             initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
    2326             : 
    2327        7484 :         GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2328             : 
    2329        7484 :         if (hThisGeosGeom != nullptr)
    2330             :         {
    2331        7483 :             bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2332             : #ifdef DEBUG_VERBOSE
    2333             :             if (!bResult)
    2334             :             {
    2335             :                 char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
    2336             :                 CPLDebug("OGR", "%s", pszReason);
    2337             :                 GEOSFree_r(hGEOSCtxt, pszReason);
    2338             :             }
    2339             : #endif
    2340        7483 :             GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2341             :         }
    2342        7484 :         freeGEOSContext(hGEOSCtxt);
    2343             : 
    2344        7484 :         return bResult;
    2345             : 
    2346             : #endif  // HAVE_GEOS
    2347             :     }
    2348             : }
    2349             : 
    2350             : /************************************************************************/
    2351             : /*                           OGR_G_IsValid()                            */
    2352             : /************************************************************************/
    2353             : 
    2354             : /**
    2355             :  * \brief Test if the geometry is valid.
    2356             :  *
    2357             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2358             :  *
    2359             :  * This function is built on the GEOS library, check it for the definition
    2360             :  * of the geometry operation.
    2361             :  * If OGR is built without the GEOS library, this function will always return
    2362             :  * FALSE.
    2363             :  *
    2364             :  * @param hGeom The Geometry to test.
    2365             :  *
    2366             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2367             :  */
    2368             : 
    2369          23 : int OGR_G_IsValid(OGRGeometryH hGeom)
    2370             : 
    2371             : {
    2372          23 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
    2373             : 
    2374          23 :     return OGRGeometry::FromHandle(hGeom)->IsValid();
    2375             : }
    2376             : 
    2377             : /************************************************************************/
    2378             : /*                              IsSimple()                              */
    2379             : /************************************************************************/
    2380             : 
    2381             : /**
    2382             :  * \brief Test if the geometry is simple.
    2383             :  *
    2384             :  * This method is the same as the C function OGR_G_IsSimple().
    2385             :  *
    2386             :  * This method 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 method will always return
    2389             :  * FALSE.
    2390             :  *
    2391             :  *
    2392             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2393             :  */
    2394             : 
    2395           5 : OGRBoolean OGRGeometry::IsSimple() const
    2396             : 
    2397             : {
    2398             : #ifndef HAVE_GEOS
    2399             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2400             :     return FALSE;
    2401             : 
    2402             : #else
    2403             : 
    2404           5 :     OGRBoolean bResult = FALSE;
    2405             : 
    2406           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2407           5 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2408             : 
    2409           5 :     if (hThisGeosGeom != nullptr)
    2410             :     {
    2411           5 :         bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2412           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2413             :     }
    2414           5 :     freeGEOSContext(hGEOSCtxt);
    2415             : 
    2416           5 :     return bResult;
    2417             : 
    2418             : #endif  // HAVE_GEOS
    2419             : }
    2420             : 
    2421             : /**
    2422             :  * \brief Returns TRUE if the geometry is simple.
    2423             :  *
    2424             :  * Returns TRUE if the geometry has no anomalous geometric points, such
    2425             :  * as self intersection or self tangency. The description of each
    2426             :  * instantiable geometric class will include the specific conditions that
    2427             :  * cause an instance of that class to be classified as not simple.
    2428             :  *
    2429             :  * This function is the same as the C++ method OGRGeometry::IsSimple() method.
    2430             :  *
    2431             :  * If OGR is built without the GEOS library, this function will always return
    2432             :  * FALSE.
    2433             :  *
    2434             :  * @param hGeom The Geometry to test.
    2435             :  *
    2436             :  * @return TRUE if object is simple, otherwise FALSE.
    2437             :  */
    2438             : 
    2439           5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
    2440             : 
    2441             : {
    2442           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
    2443             : 
    2444           5 :     return OGRGeometry::FromHandle(hGeom)->IsSimple();
    2445             : }
    2446             : 
    2447             : /************************************************************************/
    2448             : /*                               IsRing()                               */
    2449             : /************************************************************************/
    2450             : 
    2451             : /**
    2452             :  * \brief Test if the geometry is a ring
    2453             :  *
    2454             :  * This method is the same as the C function OGR_G_IsRing().
    2455             :  *
    2456             :  * This method is built on the GEOS library, check it for the definition
    2457             :  * of the geometry operation.
    2458             :  * If OGR is built without the GEOS library, this method will always return
    2459             :  * FALSE.
    2460             :  *
    2461             :  *
    2462             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2463             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2464             :  */
    2465             : 
    2466           1 : OGRBoolean OGRGeometry::IsRing() const
    2467             : 
    2468             : {
    2469             : #ifndef HAVE_GEOS
    2470             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2471             :     return FALSE;
    2472             : 
    2473             : #else
    2474             : 
    2475           1 :     OGRBoolean bResult = FALSE;
    2476             : 
    2477           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2478           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2479             : 
    2480           1 :     if (hThisGeosGeom != nullptr)
    2481             :     {
    2482           1 :         bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2483           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2484             :     }
    2485           1 :     freeGEOSContext(hGEOSCtxt);
    2486             : 
    2487           1 :     return bResult;
    2488             : 
    2489             : #endif  // HAVE_GEOS
    2490             : }
    2491             : 
    2492             : /************************************************************************/
    2493             : /*                            OGR_G_IsRing()                            */
    2494             : /************************************************************************/
    2495             : 
    2496             : /**
    2497             :  * \brief Test if the geometry is a ring
    2498             :  *
    2499             :  * This function is the same as the C++ method OGRGeometry::IsRing().
    2500             :  *
    2501             :  * This function is built on the GEOS library, check it for the definition
    2502             :  * of the geometry operation.
    2503             :  * If OGR is built without the GEOS library, this function will always return
    2504             :  * FALSE.
    2505             :  *
    2506             :  * @param hGeom The Geometry to test.
    2507             :  *
    2508             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2509             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2510             :  */
    2511             : 
    2512           1 : int OGR_G_IsRing(OGRGeometryH hGeom)
    2513             : 
    2514             : {
    2515           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
    2516             : 
    2517           1 :     return OGRGeometry::FromHandle(hGeom)->IsRing();
    2518             : }
    2519             : 
    2520             : /************************************************************************/
    2521             : /*                         OGRFromOGCGeomType()                         */
    2522             : /************************************************************************/
    2523             : 
    2524             : /** Map OGC geometry format type to corresponding OGR constants.
    2525             :  * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
    2526             :  * @return OGR constant.
    2527             :  */
    2528        3210 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
    2529             : {
    2530        3210 :     OGRwkbGeometryType eType = wkbUnknown;
    2531        3210 :     bool bConvertTo3D = false;
    2532        3210 :     bool bIsMeasured = false;
    2533        3210 :     if (*pszGeomType != '\0')
    2534             :     {
    2535        3204 :         char ch = pszGeomType[strlen(pszGeomType) - 1];
    2536        3204 :         if (ch == 'm' || ch == 'M')
    2537             :         {
    2538           2 :             bIsMeasured = true;
    2539           2 :             if (strlen(pszGeomType) > 1)
    2540           2 :                 ch = pszGeomType[strlen(pszGeomType) - 2];
    2541             :         }
    2542        3204 :         if (ch == 'z' || ch == 'Z')
    2543             :         {
    2544          34 :             bConvertTo3D = true;
    2545             :         }
    2546             :     }
    2547             : 
    2548        3210 :     if (STARTS_WITH_CI(pszGeomType, "POINT"))
    2549         971 :         eType = wkbPoint;
    2550        2239 :     else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
    2551         184 :         eType = wkbLineString;
    2552        2055 :     else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
    2553         891 :         eType = wkbPolygon;
    2554        1164 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
    2555          24 :         eType = wkbMultiPoint;
    2556        1140 :     else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
    2557          13 :         eType = wkbMultiLineString;
    2558        1127 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
    2559          82 :         eType = wkbMultiPolygon;
    2560        1045 :     else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
    2561           4 :         eType = wkbGeometryCollection;
    2562        1041 :     else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
    2563         351 :         eType = wkbCircularString;
    2564         690 :     else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
    2565           0 :         eType = wkbCompoundCurve;
    2566         690 :     else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
    2567          16 :         eType = wkbCurvePolygon;
    2568         674 :     else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
    2569           2 :         eType = wkbMultiCurve;
    2570         672 :     else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
    2571           0 :         eType = wkbMultiSurface;
    2572         672 :     else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
    2573           0 :         eType = wkbTriangle;
    2574         672 :     else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
    2575           1 :         eType = wkbPolyhedralSurface;
    2576         671 :     else if (STARTS_WITH_CI(pszGeomType, "TIN"))
    2577           5 :         eType = wkbTIN;
    2578         666 :     else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
    2579           3 :         eType = wkbCurve;
    2580         663 :     else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
    2581           3 :         eType = wkbSurface;
    2582             :     else
    2583         660 :         eType = wkbUnknown;
    2584             : 
    2585        3210 :     if (bConvertTo3D)
    2586          34 :         eType = wkbSetZ(eType);
    2587        3210 :     if (bIsMeasured)
    2588           2 :         eType = wkbSetM(eType);
    2589             : 
    2590        3210 :     return eType;
    2591             : }
    2592             : 
    2593             : /************************************************************************/
    2594             : /*                          OGRToOGCGeomType()                          */
    2595             : /************************************************************************/
    2596             : 
    2597             : /** Map OGR geometry format constants to corresponding OGC geometry type.
    2598             :  * @param eGeomType OGR geometry type
    2599             :  * @param bCamelCase Whether the return should be like "MultiPoint"
    2600             :  *        (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
    2601             :  * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
    2602             :  *               Default is false.
    2603             :  * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
    2604             :  *                       suffix. Default is false.
    2605             :  * @return string with OGC geometry type (without dimensionality)
    2606             :  */
    2607        2986 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
    2608             :                              bool bAddZM, bool bSpaceBeforeZM)
    2609             : {
    2610        2986 :     const char *pszRet = "";
    2611        2986 :     switch (wkbFlatten(eGeomType))
    2612             :     {
    2613        1568 :         case wkbUnknown:
    2614        1568 :             pszRet = "Geometry";
    2615        1568 :             break;
    2616         560 :         case wkbPoint:
    2617         560 :             pszRet = "Point";
    2618         560 :             break;
    2619         153 :         case wkbLineString:
    2620         153 :             pszRet = "LineString";
    2621         153 :             break;
    2622         370 :         case wkbPolygon:
    2623         370 :             pszRet = "Polygon";
    2624         370 :             break;
    2625          48 :         case wkbMultiPoint:
    2626          48 :             pszRet = "MultiPoint";
    2627          48 :             break;
    2628          56 :         case wkbMultiLineString:
    2629          56 :             pszRet = "MultiLineString";
    2630          56 :             break;
    2631          74 :         case wkbMultiPolygon:
    2632          74 :             pszRet = "MultiPolygon";
    2633          74 :             break;
    2634          54 :         case wkbGeometryCollection:
    2635          54 :             pszRet = "GeometryCollection";
    2636          54 :             break;
    2637           9 :         case wkbCircularString:
    2638           9 :             pszRet = "CircularString";
    2639           9 :             break;
    2640           3 :         case wkbCompoundCurve:
    2641           3 :             pszRet = "CompoundCurve";
    2642           3 :             break;
    2643          12 :         case wkbCurvePolygon:
    2644          12 :             pszRet = "CurvePolygon";
    2645          12 :             break;
    2646           2 :         case wkbMultiCurve:
    2647           2 :             pszRet = "MultiCurve";
    2648           2 :             break;
    2649           3 :         case wkbMultiSurface:
    2650           3 :             pszRet = "MultiSurface";
    2651           3 :             break;
    2652           3 :         case wkbTriangle:
    2653           3 :             pszRet = "Triangle";
    2654           3 :             break;
    2655           5 :         case wkbPolyhedralSurface:
    2656           5 :             pszRet = "PolyhedralSurface";
    2657           5 :             break;
    2658           1 :         case wkbTIN:
    2659           1 :             pszRet = "Tin";
    2660           1 :             break;
    2661           3 :         case wkbCurve:
    2662           3 :             pszRet = "Curve";
    2663           3 :             break;
    2664           3 :         case wkbSurface:
    2665           3 :             pszRet = "Surface";
    2666           3 :             break;
    2667          59 :         default:
    2668          59 :             break;
    2669             :     }
    2670        2986 :     if (bAddZM)
    2671             :     {
    2672          67 :         const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    2673          67 :         const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    2674          67 :         if (bHasZ || bHasM)
    2675             :         {
    2676          10 :             if (bSpaceBeforeZM)
    2677           1 :                 pszRet = CPLSPrintf("%s ", pszRet);
    2678          10 :             if (bHasZ)
    2679           9 :                 pszRet = CPLSPrintf("%sZ", pszRet);
    2680          10 :             if (bHasM)
    2681           5 :                 pszRet = CPLSPrintf("%sM", pszRet);
    2682             :         }
    2683             :     }
    2684        2986 :     if (!bCamelCase)
    2685        2918 :         pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
    2686        2986 :     return pszRet;
    2687             : }
    2688             : 
    2689             : /************************************************************************/
    2690             : /*                       OGRGeometryTypeToName()                        */
    2691             : /************************************************************************/
    2692             : 
    2693             : /**
    2694             :  * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
    2695             :  * value.  The returned value should not be modified, or freed by the
    2696             :  * application.
    2697             :  *
    2698             :  * This function is C callable.
    2699             :  *
    2700             :  * @param eType the geometry type.
    2701             :  *
    2702             :  * @return internal human readable string, or NULL on failure.
    2703             :  */
    2704             : 
    2705         393 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
    2706             : 
    2707             : {
    2708         393 :     bool b3D = wkbHasZ(eType);
    2709         393 :     bool bMeasured = wkbHasM(eType);
    2710             : 
    2711         393 :     switch (wkbFlatten(eType))
    2712             :     {
    2713          34 :         case wkbUnknown:
    2714          34 :             if (b3D && bMeasured)
    2715           0 :                 return "3D Measured Unknown (any)";
    2716          34 :             else if (b3D)
    2717           1 :                 return "3D Unknown (any)";
    2718          33 :             else if (bMeasured)
    2719           0 :                 return "Measured Unknown (any)";
    2720             :             else
    2721          33 :                 return "Unknown (any)";
    2722             : 
    2723          59 :         case wkbPoint:
    2724          59 :             if (b3D && bMeasured)
    2725           3 :                 return "3D Measured Point";
    2726          56 :             else if (b3D)
    2727          12 :                 return "3D Point";
    2728          44 :             else if (bMeasured)
    2729           5 :                 return "Measured Point";
    2730             :             else
    2731          39 :                 return "Point";
    2732             : 
    2733          37 :         case wkbLineString:
    2734          37 :             if (b3D && bMeasured)
    2735           0 :                 return "3D Measured Line String";
    2736          37 :             else if (b3D)
    2737           9 :                 return "3D Line String";
    2738          28 :             else if (bMeasured)
    2739           0 :                 return "Measured Line String";
    2740             :             else
    2741          28 :                 return "Line String";
    2742             : 
    2743          62 :         case wkbPolygon:
    2744          62 :             if (b3D && bMeasured)
    2745           0 :                 return "3D Measured Polygon";
    2746          62 :             else if (b3D)
    2747           8 :                 return "3D Polygon";
    2748          54 :             else if (bMeasured)
    2749           0 :                 return "Measured Polygon";
    2750             :             else
    2751          54 :                 return "Polygon";
    2752             : 
    2753          19 :         case wkbMultiPoint:
    2754          19 :             if (b3D && bMeasured)
    2755           0 :                 return "3D Measured Multi Point";
    2756          19 :             else if (b3D)
    2757           9 :                 return "3D Multi Point";
    2758          10 :             else if (bMeasured)
    2759           0 :                 return "Measured Multi Point";
    2760             :             else
    2761          10 :                 return "Multi Point";
    2762             : 
    2763          14 :         case wkbMultiLineString:
    2764          14 :             if (b3D && bMeasured)
    2765           0 :                 return "3D Measured Multi Line String";
    2766          14 :             else if (b3D)
    2767           6 :                 return "3D Multi Line String";
    2768           8 :             else if (bMeasured)
    2769           0 :                 return "Measured Multi Line String";
    2770             :             else
    2771           8 :                 return "Multi Line String";
    2772             : 
    2773          19 :         case wkbMultiPolygon:
    2774          19 :             if (b3D && bMeasured)
    2775           0 :                 return "3D Measured Multi Polygon";
    2776          19 :             else if (b3D)
    2777           8 :                 return "3D Multi Polygon";
    2778          11 :             else if (bMeasured)
    2779           0 :                 return "Measured Multi Polygon";
    2780             :             else
    2781          11 :                 return "Multi Polygon";
    2782             : 
    2783          25 :         case wkbGeometryCollection:
    2784          25 :             if (b3D && bMeasured)
    2785           0 :                 return "3D Measured Geometry Collection";
    2786          25 :             else if (b3D)
    2787          10 :                 return "3D Geometry Collection";
    2788          15 :             else if (bMeasured)
    2789           0 :                 return "Measured Geometry Collection";
    2790             :             else
    2791          15 :                 return "Geometry Collection";
    2792             : 
    2793           0 :         case wkbCircularString:
    2794           0 :             if (b3D && bMeasured)
    2795           0 :                 return "3D Measured Circular String";
    2796           0 :             else if (b3D)
    2797           0 :                 return "3D Circular String";
    2798           0 :             else if (bMeasured)
    2799           0 :                 return "Measured Circular String";
    2800             :             else
    2801           0 :                 return "Circular String";
    2802             : 
    2803           1 :         case wkbCompoundCurve:
    2804           1 :             if (b3D && bMeasured)
    2805           0 :                 return "3D Measured Compound Curve";
    2806           1 :             else if (b3D)
    2807           0 :                 return "3D Compound Curve";
    2808           1 :             else if (bMeasured)
    2809           0 :                 return "Measured Compound Curve";
    2810             :             else
    2811           1 :                 return "Compound Curve";
    2812             : 
    2813           0 :         case wkbCurvePolygon:
    2814           0 :             if (b3D && bMeasured)
    2815           0 :                 return "3D Measured Curve Polygon";
    2816           0 :             else if (b3D)
    2817           0 :                 return "3D Curve Polygon";
    2818           0 :             else if (bMeasured)
    2819           0 :                 return "Measured Curve Polygon";
    2820             :             else
    2821           0 :                 return "Curve Polygon";
    2822             : 
    2823           0 :         case wkbMultiCurve:
    2824           0 :             if (b3D && bMeasured)
    2825           0 :                 return "3D Measured Multi Curve";
    2826           0 :             else if (b3D)
    2827           0 :                 return "3D Multi Curve";
    2828           0 :             else if (bMeasured)
    2829           0 :                 return "Measured Multi Curve";
    2830             :             else
    2831           0 :                 return "Multi Curve";
    2832             : 
    2833           0 :         case wkbMultiSurface:
    2834           0 :             if (b3D && bMeasured)
    2835           0 :                 return "3D Measured Multi Surface";
    2836           0 :             else if (b3D)
    2837           0 :                 return "3D Multi Surface";
    2838           0 :             else if (bMeasured)
    2839           0 :                 return "Measured Multi Surface";
    2840             :             else
    2841           0 :                 return "Multi Surface";
    2842             : 
    2843           4 :         case wkbCurve:
    2844           4 :             if (b3D && bMeasured)
    2845           1 :                 return "3D Measured Curve";
    2846           3 :             else if (b3D)
    2847           1 :                 return "3D Curve";
    2848           2 :             else if (bMeasured)
    2849           1 :                 return "Measured Curve";
    2850             :             else
    2851           1 :                 return "Curve";
    2852             : 
    2853           4 :         case wkbSurface:
    2854           4 :             if (b3D && bMeasured)
    2855           1 :                 return "3D Measured Surface";
    2856           3 :             else if (b3D)
    2857           1 :                 return "3D Surface";
    2858           2 :             else if (bMeasured)
    2859           1 :                 return "Measured Surface";
    2860             :             else
    2861           1 :                 return "Surface";
    2862             : 
    2863           0 :         case wkbTriangle:
    2864           0 :             if (b3D && bMeasured)
    2865           0 :                 return "3D Measured Triangle";
    2866           0 :             else if (b3D)
    2867           0 :                 return "3D Triangle";
    2868           0 :             else if (bMeasured)
    2869           0 :                 return "Measured Triangle";
    2870             :             else
    2871           0 :                 return "Triangle";
    2872             : 
    2873           0 :         case wkbPolyhedralSurface:
    2874           0 :             if (b3D && bMeasured)
    2875           0 :                 return "3D Measured PolyhedralSurface";
    2876           0 :             else if (b3D)
    2877           0 :                 return "3D PolyhedralSurface";
    2878           0 :             else if (bMeasured)
    2879           0 :                 return "Measured PolyhedralSurface";
    2880             :             else
    2881           0 :                 return "PolyhedralSurface";
    2882             : 
    2883           2 :         case wkbTIN:
    2884           2 :             if (b3D && bMeasured)
    2885           0 :                 return "3D Measured TIN";
    2886           2 :             else if (b3D)
    2887           0 :                 return "3D TIN";
    2888           2 :             else if (bMeasured)
    2889           0 :                 return "Measured TIN";
    2890             :             else
    2891           2 :                 return "TIN";
    2892             : 
    2893         112 :         case wkbNone:
    2894         112 :             return "None";
    2895             : 
    2896           1 :         default:
    2897             :         {
    2898           1 :             return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
    2899             :         }
    2900             :     }
    2901             : }
    2902             : 
    2903             : /************************************************************************/
    2904             : /*                       OGRMergeGeometryTypes()                        */
    2905             : /************************************************************************/
    2906             : 
    2907             : /**
    2908             :  * \brief Find common geometry type.
    2909             :  *
    2910             :  * Given two geometry types, find the most specific common
    2911             :  * type.  Normally used repeatedly with the geometries in a
    2912             :  * layer to try and establish the most specific geometry type
    2913             :  * that can be reported for the layer.
    2914             :  *
    2915             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    2916             :  * geometry types with nothing in common but the base geometry
    2917             :  * type.  wkbNone should be used to indicate that no geometries
    2918             :  * have been encountered yet, and means the first geometry
    2919             :  * encountered will establish the preliminary type.
    2920             :  *
    2921             :  * @param eMain the first input geometry type.
    2922             :  * @param eExtra the second input geometry type.
    2923             :  *
    2924             :  * @return the merged geometry type.
    2925             :  */
    2926             : 
    2927           0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
    2928             :                                          OGRwkbGeometryType eExtra)
    2929             : 
    2930             : {
    2931           0 :     return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
    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             :  * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
    2949             :  * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
    2950             :  * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
    2951             :  * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
    2952             :  *
    2953             :  * @param eMain the first input geometry type.
    2954             :  * @param eExtra the second input geometry type.
    2955             :  * @param bAllowPromotingToCurves determine if promotion to curve type
    2956             :  * must be done.
    2957             :  *
    2958             :  * @return the merged geometry type.
    2959             :  *
    2960             :  */
    2961             : 
    2962         575 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
    2963             :                                            OGRwkbGeometryType eExtra,
    2964             :                                            int bAllowPromotingToCurves)
    2965             : 
    2966             : {
    2967         575 :     OGRwkbGeometryType eFMain = wkbFlatten(eMain);
    2968         575 :     OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
    2969             : 
    2970         575 :     const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
    2971         575 :     const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
    2972             : 
    2973         575 :     if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
    2974          17 :         return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    2975             : 
    2976         558 :     if (eFMain == wkbNone)
    2977           2 :         return eExtra;
    2978             : 
    2979         556 :     if (eFExtra == wkbNone)
    2980           0 :         return eMain;
    2981             : 
    2982         556 :     if (eFMain == eFExtra)
    2983             :     {
    2984         539 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    2985             :     }
    2986             : 
    2987          17 :     if (bAllowPromotingToCurves)
    2988             :     {
    2989          17 :         if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
    2990           4 :             return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
    2991             : 
    2992          13 :         if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    2993           3 :             return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    2994             : 
    2995          10 :         if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    2996           6 :             return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    2997             :     }
    2998             : 
    2999             :     // One is subclass of the other one
    3000           4 :     if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3001             :     {
    3002           0 :         return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3003             :     }
    3004           4 :     else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3005             :     {
    3006           0 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3007             :     }
    3008             : 
    3009             :     // Nothing apparently in common.
    3010           4 :     return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3011             : }
    3012             : 
    3013             : /**
    3014             :  * \fn void OGRGeometry::flattenTo2D();
    3015             :  *
    3016             :  * \brief Convert geometry to strictly 2D.
    3017             :  * In a sense this converts all Z coordinates
    3018             :  * to 0.0.
    3019             :  *
    3020             :  * This method is the same as the C function OGR_G_FlattenTo2D().
    3021             :  */
    3022             : 
    3023             : /************************************************************************/
    3024             : /*                         OGR_G_FlattenTo2D()                          */
    3025             : /************************************************************************/
    3026             : /**
    3027             :  * \brief Convert geometry to strictly 2D.
    3028             :  * In a sense this converts all Z coordinates
    3029             :  * to 0.0.
    3030             :  *
    3031             :  * This function is the same as the CPP method OGRGeometry::flattenTo2D().
    3032             :  *
    3033             :  * @param hGeom handle on the geometry to convert.
    3034             :  */
    3035             : 
    3036          31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
    3037             : 
    3038             : {
    3039          31 :     OGRGeometry::FromHandle(hGeom)->flattenTo2D();
    3040          31 : }
    3041             : 
    3042             : /************************************************************************/
    3043             : /*                            exportToGML()                             */
    3044             : /************************************************************************/
    3045             : 
    3046             : /**
    3047             :  * \fn char *OGRGeometry::exportToGML( const char* const *
    3048             :  * papszOptions = NULL ) const;
    3049             :  *
    3050             :  * \brief Convert a geometry into GML format.
    3051             :  *
    3052             :  * The GML geometry is expressed directly in terms of GML basic data
    3053             :  * types assuming the this is available in the gml namespace.  The returned
    3054             :  * string should be freed with CPLFree() when no longer required.
    3055             :  *
    3056             :  * The supported options are :
    3057             :  * <ul>
    3058             :  * <li> FORMAT=GML2/GML3/GML32.
    3059             :  *      If not set, it will default to GML 2.1.2 output.
    3060             :  * </li>
    3061             :  * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
    3062             :  *      To use gml:Curve element for linestrings.
    3063             :  *      Otherwise gml:LineString will be used .
    3064             :  * </li>
    3065             :  * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
    3066             :  *      SRSNAME_FORMAT in GDAL &gt;=2.2). Defaults to YES.
    3067             :  *      If YES, SRS with EPSG authority will be written with the
    3068             :  *      "urn:ogc:def:crs:EPSG::" prefix.
    3069             :  *      In the case the SRS should be treated as lat/long or
    3070             :  *      northing/easting, then the function will take care of coordinate order
    3071             :  *      swapping if the data axis to CRS axis mapping indicates it.
    3072             :  *      If set to NO, SRS with EPSG authority will be written with the "EPSG:"
    3073             :  *      prefix, even if they are in lat/long order.
    3074             :  * </li>
    3075             :  * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
    3076             :  *      Defaults to OGC_URN.  If SHORT, then srsName will be in
    3077             :  *      the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
    3078             :  *      in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
    3079             :  *      then srsName will be in the form
    3080             :  *      http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
    3081             :  *      OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
    3082             :  *      or northing/easting, then the function will take care of coordinate
    3083             :  *      order swapping if the data axis to CRS axis mapping indicates it.
    3084             :  * </li>
    3085             :  * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
    3086             :  *      top-level geometry element with the provided value.
    3087             :  *      Required for GML 3.2 compatibility.
    3088             :  * </li>
    3089             :  * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
    3090             :  *      FORMAT=GML3/GML32) Default to POSLIST.
    3091             :  *      For 2.5D geometries, define the location where to attach the
    3092             :  *      srsDimension attribute.
    3093             :  *      There are diverging implementations. Some put in on the
    3094             :  *      &lt;gml:posList&gt; element, other on the top geometry element.
    3095             :  * </li>
    3096             :  * <li> NAMESPACE_DECL=YES/NO. If set to YES,
    3097             :  *      xmlns:gml="http://www.opengis.net/gml" will be added to the root node
    3098             :  *      for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
    3099             :  * </li>
    3100             :  * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
    3101             :  *      Resolution for the coordinate precision of the X and Y coordinates.
    3102             :  *      Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
    3103             :  *      to 5 decimal digits. 0 for the default behavior.
    3104             :  * </li>
    3105             :  * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
    3106             :  *      Resolution for the coordinate precision of the Z coordinates.
    3107             :  *      Expressed in the units of the Z axis of the SRS.
    3108             :  *      0 for the default behavior.
    3109             :  * </li>
    3110             :  * </ul>
    3111             :  *
    3112             :  * This method is the same as the C function OGR_G_ExportToGMLEx().
    3113             :  *
    3114             :  * @param papszOptions NULL-terminated list of options.
    3115             :  * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
    3116             :  */
    3117             : 
    3118         251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
    3119             : {
    3120         251 :     return OGR_G_ExportToGMLEx(
    3121             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
    3122         251 :         const_cast<char **>(papszOptions));
    3123             : }
    3124             : 
    3125             : /************************************************************************/
    3126             : /*                            exportToKML()                             */
    3127             : /************************************************************************/
    3128             : 
    3129             : /**
    3130             :  * \fn char *OGRGeometry::exportToKML() const;
    3131             :  *
    3132             :  * \brief Convert a geometry into KML format.
    3133             :  *
    3134             :  * The returned string should be freed with CPLFree() when no longer required.
    3135             :  *
    3136             :  * This method is the same as the C function OGR_G_ExportToKML().
    3137             :  *
    3138             :  * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
    3139             :  */
    3140             : 
    3141           0 : char *OGRGeometry::exportToKML() const
    3142             : {
    3143           0 :     return OGR_G_ExportToKML(
    3144           0 :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
    3145             : }
    3146             : 
    3147             : /************************************************************************/
    3148             : /*                            exportToJson()                            */
    3149             : /************************************************************************/
    3150             : 
    3151             : /**
    3152             :  * \fn char *OGRGeometry::exportToJson() const;
    3153             :  *
    3154             :  * \brief Convert a geometry into GeoJSON format.
    3155             :  *
    3156             :  * The returned string should be freed with CPLFree() when no longer required.
    3157             :  *
    3158             :  * The following options are supported :
    3159             :  * <ul>
    3160             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
    3161             :  * (added in GDAL 3.9)</li>
    3162             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
    3163             :  * (added in GDAL 3.9)</li>
    3164             :  * </ul>
    3165             :  *
    3166             :  * This method is the same as the C function OGR_G_ExportToJson().
    3167             :  *
    3168             :  * @param papszOptions Null terminated list of options, or null (added in 3.9)
    3169             :  * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
    3170             :  */
    3171             : 
    3172          43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
    3173             : {
    3174          43 :     OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
    3175          43 :     return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
    3176          43 :                                 const_cast<char **>(papszOptions));
    3177             : }
    3178             : 
    3179             : /************************************************************************/
    3180             : /*                 OGRSetGenerate_DB2_V72_BYTE_ORDER()                  */
    3181             : /************************************************************************/
    3182             : 
    3183             : /**
    3184             :  * \brief Special entry point to enable the hack for generating DB2 V7.2 style
    3185             :  * WKB.
    3186             :  *
    3187             :  * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
    3188             :  * order in WKB.  This entry point is used to turn on or off the generation of
    3189             :  * such WKB.
    3190             :  */
    3191           4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
    3192             : 
    3193             : {
    3194             : #if defined(HACK_FOR_IBM_DB2_V72)
    3195           4 :     OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
    3196           4 :     return OGRERR_NONE;
    3197             : #else
    3198             :     if (bGenerate_DB2_V72_BYTE_ORDER)
    3199             :         return OGRERR_FAILURE;
    3200             :     else
    3201             :         return OGRERR_NONE;
    3202             : #endif
    3203             : }
    3204             : 
    3205             : /************************************************************************/
    3206             : /*                 OGRGetGenerate_DB2_V72_BYTE_ORDER()                  */
    3207             : /*                                                                      */
    3208             : /*      This is a special entry point to get the value of static flag   */
    3209             : /*      OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER.                      */
    3210             : /************************************************************************/
    3211           0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
    3212             : {
    3213           0 :     return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
    3214             : }
    3215             : 
    3216             : /************************************************************************/
    3217             : /*                         createGEOSContext()                          */
    3218             : /************************************************************************/
    3219             : 
    3220             : /** Create a new GEOS context.
    3221             :  * @return a new GEOS context (to be freed with freeGEOSContext())
    3222             :  */
    3223       84969 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
    3224             : {
    3225             : #ifndef HAVE_GEOS
    3226             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3227             :     return nullptr;
    3228             : #else
    3229       84969 :     return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
    3230             : #endif
    3231             : }
    3232             : 
    3233             : /************************************************************************/
    3234             : /*                          freeGEOSContext()                           */
    3235             : /************************************************************************/
    3236             : 
    3237             : /** Destroy a GEOS context.
    3238             :  * @param hGEOSCtxt GEOS context
    3239             :  */
    3240       92242 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
    3241             : {
    3242             :     (void)hGEOSCtxt;
    3243             : #ifdef HAVE_GEOS
    3244       92242 :     if (hGEOSCtxt != nullptr)
    3245             :     {
    3246       92242 :         finishGEOS_r(hGEOSCtxt);
    3247             :     }
    3248             : #endif
    3249       92242 : }
    3250             : #ifdef HAVE_GEOS
    3251             : 
    3252             : /************************************************************************/
    3253             : /*                      canConvertToMultiPolygon()                      */
    3254             : /************************************************************************/
    3255             : 
    3256         148 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
    3257             : {
    3258         338 :     for (const auto *poSubGeom : *poGC)
    3259             :     {
    3260             :         const OGRwkbGeometryType eSubGeomType =
    3261         272 :             wkbFlatten(poSubGeom->getGeometryType());
    3262         272 :         if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
    3263         164 :             eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
    3264             :         {
    3265          82 :             return false;
    3266             :         }
    3267             :     }
    3268             : 
    3269          66 :     return true;
    3270             : }
    3271             : 
    3272             : /************************************************************************/
    3273             : /*                         GEOSWarningSilencer                          */
    3274             : /************************************************************************/
    3275             : 
    3276             : /** Class that can be used to silence GEOS messages while in-scope. */
    3277             : class GEOSWarningSilencer
    3278             : {
    3279             :   public:
    3280        5534 :     explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
    3281        5534 :         : m_poContext(poContext)
    3282             :     {
    3283        5534 :         GEOSContext_setErrorHandler_r(m_poContext, nullptr);
    3284        5534 :         GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
    3285        5534 :     }
    3286             : 
    3287        5534 :     ~GEOSWarningSilencer()
    3288        5534 :     {
    3289        5534 :         GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
    3290        5534 :         GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
    3291        5534 :     }
    3292             : 
    3293             :     CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
    3294             : 
    3295             :   private:
    3296             :     GEOSContextHandle_t m_poContext{nullptr};
    3297             : };
    3298             : 
    3299             : /************************************************************************/
    3300             : /*                           repairForGEOS()                            */
    3301             : /************************************************************************/
    3302             : 
    3303             : /** Modify an OGRGeometry so that it can be converted into GEOS.
    3304             :  *  Modifications include closing unclosed rings and adding redundant vertices
    3305             :  *  to reach minimum point limits in GEOS.
    3306             :  *
    3307             :  *  It is assumed that the input is a non-curved type that can be
    3308             :  *  represented in GEOS.
    3309             :  *
    3310             :  * @param poGeom the geometry to modify
    3311             :  * @return an OGRGeometry that can be converted to GEOS using WKB
    3312             :  */
    3313          25 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
    3314             : {
    3315             : #if GEOS_VERSION_MAJOR >= 3 ||                                                 \
    3316             :     (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
    3317             :     static constexpr int MIN_RING_POINTS = 3;
    3318             : #else
    3319             :     static constexpr int MIN_RING_POINTS = 4;
    3320             : #endif
    3321             : 
    3322          25 :     const auto eType = wkbFlatten(poGeom->getGeometryType());
    3323             : 
    3324          25 :     if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    3325             :     {
    3326           5 :         std::unique_ptr<OGRGeometryCollection> poRet;
    3327           5 :         if (eType == wkbGeometryCollection)
    3328             :         {
    3329           2 :             poRet = std::make_unique<OGRGeometryCollection>();
    3330             :         }
    3331           3 :         else if (eType == wkbMultiPolygon)
    3332             :         {
    3333           3 :             poRet = std::make_unique<OGRMultiPolygon>();
    3334             :         }
    3335           0 :         else if (eType == wkbMultiLineString)
    3336             :         {
    3337           0 :             poRet = std::make_unique<OGRMultiLineString>();
    3338             :         }
    3339           0 :         else if (eType == wkbMultiPoint)
    3340             :         {
    3341           0 :             poRet = std::make_unique<OGRMultiPoint>();
    3342             :         }
    3343             :         else
    3344             :         {
    3345           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3346             :                      "Unexpected geometry type: %s",
    3347             :                      OGRGeometryTypeToName(eType));
    3348           0 :             return nullptr;
    3349             :         }
    3350             : 
    3351           5 :         const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
    3352          15 :         for (const auto *poSubGeomIn : *poColl)
    3353             :         {
    3354          10 :             std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
    3355          10 :             poRet->addGeometry(std::move(poSubGeom));
    3356             :         }
    3357             : 
    3358           5 :         return poRet;
    3359             :     }
    3360             : 
    3361          20 :     if (eType == wkbPoint)
    3362             :     {
    3363           0 :         return std::unique_ptr<OGRGeometry>(poGeom->clone());
    3364             :     }
    3365          20 :     if (eType == wkbLineString)
    3366             :     {
    3367             :         std::unique_ptr<OGRLineString> poLineString(
    3368           4 :             poGeom->toLineString()->clone());
    3369           2 :         if (poLineString->getNumPoints() == 1)
    3370             :         {
    3371           4 :             OGRPoint oPoint;
    3372           2 :             poLineString->getPoint(0, &oPoint);
    3373           2 :             poLineString->addPoint(&oPoint);
    3374             :         }
    3375           2 :         return poLineString;
    3376             :     }
    3377          18 :     if (eType == wkbPolygon)
    3378             :     {
    3379          36 :         std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
    3380          18 :         poPolygon->closeRings();
    3381             : 
    3382             :         // make sure rings have enough points
    3383          38 :         for (auto *poRing : *poPolygon)
    3384             :         {
    3385          24 :             while (poRing->getNumPoints() < MIN_RING_POINTS)
    3386             :             {
    3387           8 :                 OGRPoint oPoint;
    3388           4 :                 poRing->getPoint(0, &oPoint);
    3389           4 :                 poRing->addPoint(&oPoint);
    3390             :             }
    3391             :         }
    3392             : 
    3393          18 :         return poPolygon;
    3394             :     }
    3395             : 
    3396           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
    3397             :              OGRGeometryTypeToName(eType));
    3398           0 :     return nullptr;
    3399             : }
    3400             : 
    3401             : /************************************************************************/
    3402             : /*                         convertToGEOSGeom()                          */
    3403             : /************************************************************************/
    3404             : 
    3405      245055 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
    3406             :                                   const OGRGeometry *poGeom)
    3407             : {
    3408      245055 :     GEOSGeom hGeom = nullptr;
    3409      245055 :     const size_t nDataSize = poGeom->WkbSize();
    3410             :     unsigned char *pabyData =
    3411      245055 :         static_cast<unsigned char *>(CPLMalloc(nDataSize));
    3412             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    3413             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
    3414      245055 :     OGRwkbVariant eWkbVariant = wkbVariantIso;
    3415             : #else
    3416             :     OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
    3417             : #endif
    3418      245055 :     if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
    3419             :     {
    3420      244035 :         hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
    3421             :     }
    3422      245055 :     CPLFree(pabyData);
    3423             : 
    3424      245055 :     return hGeom;
    3425             : }
    3426             : #endif
    3427             : 
    3428             : /************************************************************************/
    3429             : /*                            exportToGEOS()                            */
    3430             : /************************************************************************/
    3431             : 
    3432             : /** Returns a GEOSGeom object corresponding to the geometry.
    3433             :  *
    3434             :  * @param hGEOSCtxt GEOS context
    3435             :  * @param bRemoveEmptyParts Whether empty parts of the geometry should be
    3436             :  * removed before exporting to GEOS (GDAL >= 3.10)
    3437             :  * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
    3438             :  * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
    3439             :  * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
    3440             :  * @return a GEOSGeom object corresponding to the geometry (to be freed with
    3441             :  * GEOSGeom_destroy_r()), or NULL in case of error
    3442             :  */
    3443      245040 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
    3444             :                                    bool bRemoveEmptyParts,
    3445             :                                    bool bAddPointsIfNeeded) const
    3446             : {
    3447             :     (void)hGEOSCtxt;
    3448             :     (void)bRemoveEmptyParts;
    3449             :     (void)bAddPointsIfNeeded;
    3450             : 
    3451             : #ifndef HAVE_GEOS
    3452             : 
    3453             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3454             :     return nullptr;
    3455             : 
    3456             : #else
    3457             : 
    3458      245040 :     if (hGEOSCtxt == nullptr)
    3459           0 :         return nullptr;
    3460             : 
    3461      245040 :     const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
    3462             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3463             :     // POINT EMPTY is exported to WKB as if it were POINT(0 0),
    3464             :     // so that particular case is necessary.
    3465             :     if (eType == wkbPoint && IsEmpty())
    3466             :     {
    3467             :         return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
    3468             :     }
    3469             : #endif
    3470             : 
    3471      245040 :     GEOSGeom hGeom = nullptr;
    3472             : 
    3473      245040 :     std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
    3474      245040 :     const OGRGeometry *poGeosInput = this;
    3475             : 
    3476      245040 :     const bool bHasZ = poGeosInput->Is3D();
    3477      245040 :     bool bHasM = poGeosInput->IsMeasured();
    3478             : 
    3479      245040 :     if (poGeosInput->hasCurveGeometry())
    3480             :     {
    3481         865 :         poModifiedInput.reset(poGeosInput->getLinearGeometry());
    3482         865 :         poGeosInput = poModifiedInput.get();
    3483             :     }
    3484             : 
    3485      245040 :     if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
    3486             :     {
    3487           1 :         if (!poModifiedInput)
    3488             :         {
    3489           1 :             poModifiedInput.reset(poGeosInput->clone());
    3490           1 :             poGeosInput = poModifiedInput.get();
    3491             :         }
    3492           1 :         poModifiedInput->removeEmptyParts();
    3493             :     }
    3494             : 
    3495             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3496             :     // GEOS < 3.12 doesn't support M dimension
    3497             :     if (bHasM)
    3498             :     {
    3499             :         if (!poModifiedInput)
    3500             :         {
    3501             :             poModifiedInput.reset(poGeosInput->clone());
    3502             :             poGeosInput = poModifiedInput.get();
    3503             :         }
    3504             :         poModifiedInput->setMeasured(false);
    3505             :         bHasM = false;
    3506             :     }
    3507             : #endif
    3508             : 
    3509      245040 :     if (eType == wkbTriangle)
    3510             :     {
    3511             :         poModifiedInput =
    3512          97 :             std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
    3513          97 :         poGeosInput = poModifiedInput.get();
    3514             :     }
    3515      244943 :     else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
    3516             :     {
    3517         844 :         if (!poModifiedInput)
    3518             :         {
    3519         844 :             poModifiedInput.reset(poGeosInput->clone());
    3520             :         }
    3521             : 
    3522        2532 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3523         844 :             std::move(poModifiedInput),
    3524         844 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
    3525         844 :         poGeosInput = poModifiedInput.get();
    3526             :     }
    3527      244247 :     else if (eType == wkbGeometryCollection &&
    3528         148 :              CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
    3529             :     {
    3530          66 :         if (!poModifiedInput)
    3531             :         {
    3532          64 :             poModifiedInput.reset(poGeosInput->clone());
    3533             :         }
    3534             : 
    3535             :         // Force into a MultiPolygon, then back to a GeometryCollection.
    3536             :         // This gets rid of fancy types like TIN and PolyhedralSurface that
    3537             :         // GEOS doesn't understand and flattens nested collections.
    3538         198 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3539          66 :             std::move(poModifiedInput),
    3540          66 :             OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
    3541         198 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3542          66 :             std::move(poModifiedInput),
    3543          66 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
    3544             : 
    3545          66 :         poGeosInput = poModifiedInput.get();
    3546             :     }
    3547             : 
    3548             :     {
    3549             :         // Rather than check for conditions that would prevent conversion to
    3550             :         // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
    3551             :         // conversion as-is. If the conversion fails, we don't want any
    3552             :         // warnings emitted; we'll repair the input and try again.
    3553           0 :         std::optional<GEOSWarningSilencer> oSilencer;
    3554      245040 :         if (bAddPointsIfNeeded)
    3555             :         {
    3556        5534 :             oSilencer.emplace(hGEOSCtxt);
    3557             :         }
    3558             : 
    3559      245040 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3560             :     }
    3561             : 
    3562      245040 :     if (hGeom == nullptr && bAddPointsIfNeeded)
    3563             :     {
    3564          15 :         poModifiedInput = repairForGEOS(poGeosInput);
    3565          15 :         poGeosInput = poModifiedInput.get();
    3566             : 
    3567          15 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3568             :     }
    3569             : 
    3570      245040 :     return hGeom;
    3571             : 
    3572             : #endif  // HAVE_GEOS
    3573             : }
    3574             : 
    3575             : /************************************************************************/
    3576             : /*                          hasCurveGeometry()                          */
    3577             : /************************************************************************/
    3578             : 
    3579             : /**
    3580             :  * \brief Returns if this geometry is or has curve geometry.
    3581             :  *
    3582             :  * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
    3583             :  * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
    3584             :  *
    3585             :  * If bLookForNonLinear is set to TRUE, it will be actually looked if
    3586             :  * the geometry or its subgeometries are or contain a non-linear
    3587             :  * geometry in them. In which case, if the method returns TRUE, it
    3588             :  * means that getLinearGeometry() would return an approximate version
    3589             :  * of the geometry. Otherwise, getLinearGeometry() would do a
    3590             :  * conversion, but with just converting container type, like
    3591             :  * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
    3592             :  * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
    3593             :  * conversion.
    3594             :  *
    3595             :  * This method is the same as the C function OGR_G_HasCurveGeometry().
    3596             :  *
    3597             :  * @param bLookForNonLinear set it to TRUE to check if the geometry is
    3598             :  * or contains a CIRCULARSTRING.
    3599             :  *
    3600             :  * @return TRUE if this geometry is or has curve geometry.
    3601             :  *
    3602             :  */
    3603             : 
    3604      278952 : OGRBoolean OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
    3605             : {
    3606      278952 :     return FALSE;
    3607             : }
    3608             : 
    3609             : /************************************************************************/
    3610             : /*                         getLinearGeometry()                          */
    3611             : /************************************************************************/
    3612             : 
    3613             : /**
    3614             :  * \brief Return, possibly approximate, non-curve version of this geometry.
    3615             :  *
    3616             :  * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
    3617             :  * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
    3618             :  *
    3619             :  * The ownership of the returned geometry belongs to the caller.
    3620             :  *
    3621             :  * The reverse method is OGRGeometry::getCurveGeometry().
    3622             :  *
    3623             :  * This method is the same as the C function OGR_G_GetLinearGeometry().
    3624             :  *
    3625             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    3626             :  * arc, zero to use the default setting.
    3627             :  * @param papszOptions options as a null-terminated list of strings.
    3628             :  *                     See OGRGeometryFactory::curveToLineString() for
    3629             :  *                     valid options.
    3630             :  *
    3631             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3632             :  *
    3633             :  */
    3634             : 
    3635             : OGRGeometry *
    3636          87 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
    3637             :                                CPL_UNUSED const char *const *papszOptions) const
    3638             : {
    3639          87 :     return clone();
    3640             : }
    3641             : 
    3642             : /************************************************************************/
    3643             : /*                          getCurveGeometry()                          */
    3644             : /************************************************************************/
    3645             : 
    3646             : /**
    3647             :  * \brief Return curve version of this geometry.
    3648             :  *
    3649             :  * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
    3650             :  * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
    3651             :  * curve geometries.
    3652             :  *
    3653             :  * If the geometry has no curve portion, the returned geometry will be a clone
    3654             :  * of it.
    3655             :  *
    3656             :  * The ownership of the returned geometry belongs to the caller.
    3657             :  *
    3658             :  * The reverse method is OGRGeometry::getLinearGeometry().
    3659             :  *
    3660             :  * This function is the same as C function OGR_G_GetCurveGeometry().
    3661             :  *
    3662             :  * @param papszOptions options as a null-terminated list of strings.
    3663             :  *                     Unused for now. Must be set to NULL.
    3664             :  *
    3665             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3666             :  *
    3667             :  */
    3668             : 
    3669             : OGRGeometry *
    3670           5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
    3671             : {
    3672           5 :     return clone();
    3673             : }
    3674             : 
    3675             : /************************************************************************/
    3676             : /*                              Distance()                              */
    3677             : /************************************************************************/
    3678             : 
    3679             : /**
    3680             :  * \brief Compute distance between two geometries.
    3681             :  *
    3682             :  * Returns the shortest distance between the two geometries. The distance is
    3683             :  * expressed into the same unit as the coordinates of the geometries.
    3684             :  *
    3685             :  * This method is the same as the C function OGR_G_Distance().
    3686             :  *
    3687             :  * This method is built on the GEOS library, check it for the definition
    3688             :  * of the geometry operation.
    3689             :  * If OGR is built without the GEOS library, this method will always fail,
    3690             :  * issuing a CPLE_NotSupported error.
    3691             :  *
    3692             :  * @param poOtherGeom the other geometry to compare against.
    3693             :  *
    3694             :  * @return the distance between the geometries or -1 if an error occurs.
    3695             :  */
    3696             : 
    3697          25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
    3698             : 
    3699             : {
    3700          25 :     if (nullptr == poOtherGeom)
    3701             :     {
    3702           0 :         CPLDebug("OGR",
    3703             :                  "OGRGeometry::Distance called with NULL geometry pointer");
    3704           0 :         return -1.0;
    3705             :     }
    3706             : 
    3707          25 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    3708             :     {
    3709             : #ifndef HAVE_SFCGAL
    3710             : 
    3711           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3712           0 :         return -1.0;
    3713             : 
    3714             : #else
    3715             : 
    3716             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3717             :         if (poThis == nullptr)
    3718             :             return -1.0;
    3719             : 
    3720             :         sfcgal_geometry_t *poOther =
    3721             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3722             :         if (poOther == nullptr)
    3723             :         {
    3724             :             sfcgal_geometry_delete(poThis);
    3725             :             return -1.0;
    3726             :         }
    3727             : 
    3728             :         const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
    3729             : 
    3730             :         sfcgal_geometry_delete(poThis);
    3731             :         sfcgal_geometry_delete(poOther);
    3732             : 
    3733             :         return dfDistance > 0.0 ? dfDistance : -1.0;
    3734             : 
    3735             : #endif
    3736             :     }
    3737             : 
    3738             :     else
    3739             :     {
    3740             : #ifndef HAVE_GEOS
    3741             : 
    3742             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3743             :         return -1.0;
    3744             : 
    3745             : #else
    3746             : 
    3747          25 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    3748             :         // GEOSGeom is a pointer
    3749          25 :         GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3750          25 :         GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
    3751             : 
    3752          25 :         int bIsErr = 0;
    3753          25 :         double dfDistance = 0.0;
    3754             : 
    3755          25 :         if (hThis != nullptr && hOther != nullptr)
    3756             :         {
    3757          25 :             bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
    3758             :         }
    3759             : 
    3760          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hThis);
    3761          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hOther);
    3762          25 :         freeGEOSContext(hGEOSCtxt);
    3763             : 
    3764          25 :         if (bIsErr > 0)
    3765             :         {
    3766          25 :             return dfDistance;
    3767             :         }
    3768             : 
    3769             :         /* Calculations error */
    3770           0 :         return -1.0;
    3771             : 
    3772             : #endif /* HAVE_GEOS */
    3773             :     }
    3774             : }
    3775             : 
    3776             : /************************************************************************/
    3777             : /*                           OGR_G_Distance()                           */
    3778             : /************************************************************************/
    3779             : /**
    3780             :  * \brief Compute distance between two geometries.
    3781             :  *
    3782             :  * Returns the shortest distance between the two geometries. The distance is
    3783             :  * expressed into the same unit as the coordinates of the geometries.
    3784             :  *
    3785             :  * This function is the same as the C++ method OGRGeometry::Distance().
    3786             :  *
    3787             :  * This function is built on the GEOS library, check it for the definition
    3788             :  * of the geometry operation.
    3789             :  * If OGR is built without the GEOS library, this function will always fail,
    3790             :  * issuing a CPLE_NotSupported error.
    3791             :  *
    3792             :  * @param hFirst the first geometry to compare against.
    3793             :  * @param hOther the other geometry to compare against.
    3794             :  *
    3795             :  * @return the distance between the geometries or -1 if an error occurs.
    3796             :  */
    3797             : 
    3798           2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
    3799             : 
    3800             : {
    3801           2 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
    3802             : 
    3803           4 :     return OGRGeometry::FromHandle(hFirst)->Distance(
    3804           4 :         OGRGeometry::FromHandle(hOther));
    3805             : }
    3806             : 
    3807             : /************************************************************************/
    3808             : /*                             Distance3D()                             */
    3809             : /************************************************************************/
    3810             : 
    3811             : /**
    3812             :  * \brief Returns the 3D distance between two geometries
    3813             :  *
    3814             :  * The distance is expressed into the same unit as the coordinates of the
    3815             :  * geometries.
    3816             :  *
    3817             :  * This method is built on the SFCGAL library, check it for the definition
    3818             :  * of the geometry operation.
    3819             :  * If OGR is built without the SFCGAL library, this method will always return
    3820             :  * -1.0
    3821             :  *
    3822             :  * This function is the same as the C function OGR_G_Distance3D().
    3823             :  *
    3824             :  * @return distance between the two geometries
    3825             :  */
    3826             : 
    3827           1 : double OGRGeometry::Distance3D(
    3828             :     UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
    3829             : {
    3830           1 :     if (poOtherGeom == nullptr)
    3831             :     {
    3832           0 :         CPLDebug("OGR",
    3833             :                  "OGRTriangle::Distance3D called with NULL geometry pointer");
    3834           0 :         return -1.0;
    3835             :     }
    3836             : 
    3837           1 :     if (!(poOtherGeom->Is3D() && Is3D()))
    3838             :     {
    3839           0 :         CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
    3840             :                         "geometry(geometries)");
    3841           0 :         return -1.0;
    3842             :     }
    3843             : 
    3844             : #ifndef HAVE_SFCGAL
    3845             : 
    3846           1 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3847           1 :     return -1.0;
    3848             : 
    3849             : #else
    3850             : 
    3851             :     sfcgal_init();
    3852             :     sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3853             :     if (poThis == nullptr)
    3854             :         return -1.0;
    3855             : 
    3856             :     sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3857             :     if (poOther == nullptr)
    3858             :     {
    3859             :         sfcgal_geometry_delete(poThis);
    3860             :         return -1.0;
    3861             :     }
    3862             : 
    3863             :     const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
    3864             : 
    3865             :     sfcgal_geometry_delete(poThis);
    3866             :     sfcgal_geometry_delete(poOther);
    3867             : 
    3868             :     return dfDistance > 0 ? dfDistance : -1.0;
    3869             : 
    3870             : #endif
    3871             : }
    3872             : 
    3873             : /************************************************************************/
    3874             : /*                          OGR_G_Distance3D()                          */
    3875             : /************************************************************************/
    3876             : /**
    3877             :  * \brief Returns the 3D distance between two geometries
    3878             :  *
    3879             :  * The distance is expressed into the same unit as the coordinates of the
    3880             :  * geometries.
    3881             :  *
    3882             :  * This method is built on the SFCGAL library, check it for the definition
    3883             :  * of the geometry operation.
    3884             :  * If OGR is built without the SFCGAL library, this method will always return
    3885             :  * -1.0
    3886             :  *
    3887             :  * This function is the same as the C++ method OGRGeometry::Distance3D().
    3888             :  *
    3889             :  * @param hFirst the first geometry to compare against.
    3890             :  * @param hOther the other geometry to compare against.
    3891             :  * @return distance between the two geometries
    3892             :  *
    3893             :  * @return the distance between the geometries or -1 if an error occurs.
    3894             :  */
    3895             : 
    3896           1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
    3897             : 
    3898             : {
    3899           1 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
    3900             : 
    3901           2 :     return OGRGeometry::FromHandle(hFirst)->Distance3D(
    3902           2 :         OGRGeometry::FromHandle(hOther));
    3903             : }
    3904             : 
    3905             : /************************************************************************/
    3906             : /*                      OGRGeometryRebuildCurves()                      */
    3907             : /************************************************************************/
    3908             : 
    3909             : #ifdef HAVE_GEOS
    3910        9316 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
    3911             :                                              const OGRGeometry *poOtherGeom,
    3912             :                                              OGRGeometry *poOGRProduct)
    3913             : {
    3914       18632 :     if (poOGRProduct != nullptr &&
    3915       18548 :         wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
    3916        9232 :         (poGeom->hasCurveGeometry(true) ||
    3917        2705 :          (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
    3918             :     {
    3919           8 :         OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
    3920           8 :         delete poOGRProduct;
    3921           8 :         return poCurveGeom;
    3922             :     }
    3923        9308 :     return poOGRProduct;
    3924             : }
    3925             : 
    3926             : /************************************************************************/
    3927             : /*                       BuildGeometryFromGEOS()                        */
    3928             : /************************************************************************/
    3929             : 
    3930        3784 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
    3931             :                                           GEOSGeom hGeosProduct,
    3932             :                                           const OGRGeometry *poSelf,
    3933             :                                           const OGRGeometry *poOtherGeom)
    3934             : {
    3935        3784 :     OGRGeometry *poOGRProduct = nullptr;
    3936        3784 :     if (hGeosProduct != nullptr)
    3937             :     {
    3938             :         poOGRProduct =
    3939        3782 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
    3940        3782 :         if (poOGRProduct != nullptr &&
    3941        9232 :             poSelf->getSpatialReference() != nullptr &&
    3942        1668 :             (poOtherGeom == nullptr ||
    3943        1668 :              (poOtherGeom->getSpatialReference() != nullptr &&
    3944        1533 :               poOtherGeom->getSpatialReference()->IsSame(
    3945             :                   poSelf->getSpatialReference()))))
    3946             :         {
    3947        1582 :             poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
    3948             :         }
    3949             :         poOGRProduct =
    3950        3782 :             OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
    3951        3782 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
    3952             :     }
    3953        3784 :     return poOGRProduct;
    3954             : }
    3955             : 
    3956             : /************************************************************************/
    3957             : /*                     BuildGeometryFromTwoGeoms()                      */
    3958             : /************************************************************************/
    3959             : 
    3960        2777 : static OGRGeometry *BuildGeometryFromTwoGeoms(
    3961             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    3962             :     GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
    3963             :                                        const GEOSGeometry *,
    3964             :                                        const GEOSGeometry *))
    3965             : {
    3966        2777 :     OGRGeometry *poOGRProduct = nullptr;
    3967             : 
    3968        2777 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    3969        2777 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    3970        2777 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3971        2777 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    3972             :     {
    3973             :         GEOSGeom hGeosProduct =
    3974        2777 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
    3975             : 
    3976             :         poOGRProduct =
    3977        2777 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
    3978             :     }
    3979        2777 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    3980        2777 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    3981        2777 :     poSelf->freeGEOSContext(hGEOSCtxt);
    3982             : 
    3983        2777 :     return poOGRProduct;
    3984             : }
    3985             : 
    3986             : /************************************************************************/
    3987             : /*                      OGRGEOSBooleanPredicate()                       */
    3988             : /************************************************************************/
    3989             : 
    3990       22722 : static OGRBoolean OGRGEOSBooleanPredicate(
    3991             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    3992             :     char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
    3993             :                               const GEOSGeometry *))
    3994             : {
    3995       22722 :     OGRBoolean bResult = FALSE;
    3996             : 
    3997       22722 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    3998       22722 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    3999       22722 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    4000       22722 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    4001             :     {
    4002       22163 :         bResult =
    4003       22163 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
    4004             :     }
    4005       22722 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    4006       22722 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    4007       22722 :     poSelf->freeGEOSContext(hGEOSCtxt);
    4008             : 
    4009       22722 :     return bResult;
    4010             : }
    4011             : 
    4012             : #endif  // HAVE_GEOS
    4013             : 
    4014             : /************************************************************************/
    4015             : /*                             MakeValid()                              */
    4016             : /************************************************************************/
    4017             : 
    4018             : /**
    4019             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4020             :  *
    4021             :  * Already-valid geometries are cloned without further intervention
    4022             :  * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
    4023             :  * may be subject to non-significant transformations, such as duplicated point
    4024             :  * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
    4025             :  * geometry collections could be returned a single geometry. GDAL 3.10
    4026             :  * returns the same type of geometry).
    4027             :  *
    4028             :  * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
    4029             :  * post-processing step is often desired.
    4030             :  *
    4031             :  * This method is the same as the C function OGR_G_MakeValid().
    4032             :  *
    4033             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4034             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4035             :  * library, this function will return a clone of the input geometry if it is
    4036             :  * valid, or NULL if it is invalid.
    4037             :  *
    4038             :  * Certain geometries cannot be read using GEOS, for example if Polygon rings
    4039             :  * are not closed or do not contain enough vertices. If a geometry cannot be
    4040             :  * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
    4041             :  * attempt to modify these geometries such that they can be read and
    4042             :  * repaired by GEOS.
    4043             :  *
    4044             :  * @param papszOptions NULL terminated list of options, or NULL. The following
    4045             :  * options are available:
    4046             :  * <ul>
    4047             :  * <li>METHOD=LINEWORK/STRUCTURE.
    4048             :  *     LINEWORK is the default method, which combines all rings into a set of
    4049             :  *     noded lines and then extracts valid polygons from that linework.
    4050             :  *     The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
    4051             :  *     all rings valid, then merges shells and
    4052             :  *     subtracts holes from shells to generate valid result. Assumes that
    4053             :  *     holes and shells are correctly categorized.</li>
    4054             :  * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
    4055             :  *     NO (default): collapses are converted to empty geometries
    4056             :  *     YES: collapses are converted to a valid geometry of lower dimension.</li>
    4057             :  * </ul>
    4058             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4059             :  *
    4060             :  * @since GDAL 3.0
    4061             :  */
    4062        5535 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
    4063             : {
    4064             :     (void)papszOptions;
    4065             : #ifndef HAVE_GEOS
    4066             :     if (IsValid())
    4067             :         return clone();
    4068             : 
    4069             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4070             :     return nullptr;
    4071             : #else
    4072        5535 :     if (IsSFCGALCompatible())
    4073             :     {
    4074           0 :         if (IsValid())
    4075           0 :             return clone();
    4076             :     }
    4077        5535 :     else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
    4078             :     {
    4079           3 :         GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
    4080           3 :         OGRBoolean bIsValid = FALSE;
    4081           3 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4082           3 :         if (hGeosGeom)
    4083             :         {
    4084           3 :             bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
    4085           3 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4086             :         }
    4087           3 :         freeGEOSContext(hGEOSCtxt);
    4088           3 :         if (bIsValid)
    4089           1 :             return clone();
    4090             :     }
    4091             : 
    4092        5534 :     const bool bStructureMethod = EQUAL(
    4093             :         CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
    4094        5534 :     CPL_IGNORE_RET_VAL(bStructureMethod);
    4095             : #if !(GEOS_VERSION_MAJOR > 3 ||                                                \
    4096             :       (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    4097             :     if (bStructureMethod)
    4098             :     {
    4099             :         CPLError(CE_Failure, CPLE_NotSupported,
    4100             :                  "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
    4101             :         return nullptr;
    4102             :     }
    4103             : #endif
    4104             : 
    4105        5534 :     OGRGeometry *poOGRProduct = nullptr;
    4106             : 
    4107        5534 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4108        5534 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
    4109        5534 :     if (hGeosGeom != nullptr)
    4110             :     {
    4111             :         GEOSGeom hGEOSRet;
    4112             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4113             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4114        5534 :         if (bStructureMethod)
    4115             :         {
    4116             :             GEOSMakeValidParams *params =
    4117          15 :                 GEOSMakeValidParams_create_r(hGEOSCtxt);
    4118          15 :             CPLAssert(params);
    4119          15 :             GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
    4120             :                                             GEOS_MAKE_VALID_STRUCTURE);
    4121          15 :             GEOSMakeValidParams_setKeepCollapsed_r(
    4122             :                 hGEOSCtxt, params,
    4123          15 :                 CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
    4124          15 :             hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
    4125          15 :             GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
    4126             :         }
    4127             :         else
    4128             : #endif
    4129             :         {
    4130        5519 :             hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
    4131             :         }
    4132        5534 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4133             : 
    4134        5534 :         if (hGEOSRet != nullptr)
    4135             :         {
    4136             :             poOGRProduct =
    4137        5534 :                 OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
    4138        5534 :             if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
    4139           6 :                 poOGRProduct->assignSpatialReference(getSpatialReference());
    4140             :             poOGRProduct =
    4141        5534 :                 OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
    4142        5534 :             GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
    4143             : 
    4144             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4145             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4146             :             // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
    4147             :             // if the input is a multiple geometry
    4148        5534 :             if (poOGRProduct && bStructureMethod &&
    4149       11074 :                 OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
    4150           6 :                 !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
    4151             :                                      wkbGeometryCollection))
    4152             :             {
    4153           6 :                 poOGRProduct = OGRGeometryFactory::forceTo(
    4154           6 :                                    std::unique_ptr<OGRGeometry>(poOGRProduct),
    4155           3 :                                    getGeometryType())
    4156           3 :                                    .release();
    4157             :             }
    4158             : #endif
    4159             :         }
    4160             :     }
    4161        5534 :     freeGEOSContext(hGEOSCtxt);
    4162             : 
    4163        5534 :     return poOGRProduct;
    4164             : #endif
    4165             : }
    4166             : 
    4167             : /************************************************************************/
    4168             : /*                          OGR_G_MakeValid()                           */
    4169             : /************************************************************************/
    4170             : 
    4171             : /**
    4172             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4173             :  *
    4174             :  * Already-valid geometries are cloned without further intervention.
    4175             :  *
    4176             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4177             :  *
    4178             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4179             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4180             :  * library, this function will return a clone of the input geometry if it is
    4181             :  * valid, or NULL if it is invalid
    4182             :  *
    4183             :  * @param hGeom The Geometry to make valid.
    4184             :  *
    4185             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4186             :  * or NULL if an error occurs.
    4187             :  *
    4188             :  * @since GDAL 3.0
    4189             :  */
    4190             : 
    4191           0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
    4192             : 
    4193             : {
    4194           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
    4195             : 
    4196           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
    4197             : }
    4198             : 
    4199             : /************************************************************************/
    4200             : /*                         OGR_G_MakeValidEx()                          */
    4201             : /************************************************************************/
    4202             : 
    4203             : /**
    4204             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4205             :  *
    4206             :  * Already-valid geometries are cloned without further intervention.
    4207             :  *
    4208             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4209             :  *
    4210             :  * See documentation of that method for possible options.
    4211             :  *
    4212             :  * @param hGeom The Geometry to make valid.
    4213             :  * @param papszOptions Options.
    4214             :  *
    4215             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4216             :  * or NULL if an error occurs.
    4217             :  *
    4218             :  * @since GDAL 3.4
    4219             :  */
    4220             : 
    4221          25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
    4222             : 
    4223             : {
    4224          25 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
    4225             : 
    4226          25 :     return OGRGeometry::ToHandle(
    4227          50 :         OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
    4228             : }
    4229             : 
    4230             : /************************************************************************/
    4231             : /*                             Normalize()                              */
    4232             : /************************************************************************/
    4233             : 
    4234             : /**
    4235             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4236             :  *
    4237             :  * This method is the same as the C function OGR_G_Normalize().
    4238             :  *
    4239             :  * This function is built on the GEOS library; check it for the definition
    4240             :  * of the geometry operation.
    4241             :  * If OGR is built without the GEOS library, this function will always fail,
    4242             :  * issuing a CPLE_NotSupported error.
    4243             :  *
    4244             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4245             :  *
    4246             :  * @since GDAL 3.3
    4247             :  */
    4248          51 : OGRGeometry *OGRGeometry::Normalize() const
    4249             : {
    4250             : #ifndef HAVE_GEOS
    4251             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4252             :     return nullptr;
    4253             : #else
    4254          51 :     OGRGeometry *poOGRProduct = nullptr;
    4255             : 
    4256          51 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4257          51 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4258          51 :     if (hGeosGeom != nullptr)
    4259             :     {
    4260             : 
    4261          51 :         int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
    4262             : 
    4263          51 :         if (hGEOSRet == 0)
    4264             :         {
    4265             :             poOGRProduct =
    4266          51 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
    4267             :         }
    4268             :         else
    4269             :         {
    4270           0 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4271             :         }
    4272             :     }
    4273          51 :     freeGEOSContext(hGEOSCtxt);
    4274             : 
    4275          51 :     return poOGRProduct;
    4276             : #endif
    4277             : }
    4278             : 
    4279             : /************************************************************************/
    4280             : /*                          OGR_G_Normalize()                           */
    4281             : /************************************************************************/
    4282             : 
    4283             : /**
    4284             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4285             :  *
    4286             :  * This function is the same as the C++ method OGRGeometry::Normalize().
    4287             :  *
    4288             :  * This function is built on the GEOS library; check it for the definition
    4289             :  * of the geometry operation.
    4290             :  * If OGR is built without the GEOS library, this function will always fail,
    4291             :  * issuing a CPLE_NotSupported error.
    4292             :  * @param hGeom The Geometry to normalize.
    4293             :  *
    4294             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4295             :  * or NULL if an error occurs.
    4296             :  *
    4297             :  * @since GDAL 3.3
    4298             :  */
    4299             : 
    4300          21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
    4301             : 
    4302             : {
    4303          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
    4304             : 
    4305          21 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
    4306             : }
    4307             : 
    4308             : /************************************************************************/
    4309             : /*                             ConvexHull()                             */
    4310             : /************************************************************************/
    4311             : 
    4312             : /**
    4313             :  * \brief Compute convex hull.
    4314             :  *
    4315             :  * A new geometry object is created and returned containing the convex
    4316             :  * hull of the geometry on which the method is invoked.
    4317             :  *
    4318             :  * This method is the same as the C function OGR_G_ConvexHull().
    4319             :  *
    4320             :  * This method is built on the GEOS library, check it for the definition
    4321             :  * of the geometry operation.
    4322             :  * If OGR is built without the GEOS library, this method will always fail,
    4323             :  * issuing a CPLE_NotSupported error.
    4324             :  *
    4325             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4326             :  */
    4327             : 
    4328           6 : OGRGeometry *OGRGeometry::ConvexHull() const
    4329             : 
    4330             : {
    4331           6 :     if (IsSFCGALCompatible())
    4332             :     {
    4333             : #ifndef HAVE_SFCGAL
    4334             : 
    4335           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    4336           0 :         return nullptr;
    4337             : 
    4338             : #else
    4339             : 
    4340             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    4341             :         if (poThis == nullptr)
    4342             :             return nullptr;
    4343             : 
    4344             :         sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
    4345             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    4346             :         if (h_prodGeom)
    4347             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    4348             : 
    4349             :         sfcgal_geometry_delete(poThis);
    4350             :         sfcgal_geometry_delete(poRes);
    4351             : 
    4352             :         return h_prodGeom;
    4353             : 
    4354             : #endif
    4355             :     }
    4356             : 
    4357             :     else
    4358             :     {
    4359             : #ifndef HAVE_GEOS
    4360             : 
    4361             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4362             :         return nullptr;
    4363             : 
    4364             : #else
    4365             : 
    4366           6 :         OGRGeometry *poOGRProduct = nullptr;
    4367             : 
    4368           6 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4369           6 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4370           6 :         if (hGeosGeom != nullptr)
    4371             :         {
    4372           6 :             GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
    4373           6 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4374             : 
    4375             :             poOGRProduct =
    4376           6 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4377             :         }
    4378           6 :         freeGEOSContext(hGEOSCtxt);
    4379             : 
    4380           6 :         return poOGRProduct;
    4381             : 
    4382             : #endif /* HAVE_GEOS */
    4383             :     }
    4384             : }
    4385             : 
    4386             : /************************************************************************/
    4387             : /*                          OGR_G_ConvexHull()                          */
    4388             : /************************************************************************/
    4389             : /**
    4390             :  * \brief Compute convex hull.
    4391             :  *
    4392             :  * A new geometry object is created and returned containing the convex
    4393             :  * hull of the geometry on which the method is invoked.
    4394             :  *
    4395             :  * This function is the same as the C++ method OGRGeometry::ConvexHull().
    4396             :  *
    4397             :  * This function is built on the GEOS library, check it for the definition
    4398             :  * of the geometry operation.
    4399             :  * If OGR is built without the GEOS library, this function will always fail,
    4400             :  * issuing a CPLE_NotSupported error.
    4401             :  *
    4402             :  * @param hTarget The Geometry to calculate the convex hull of.
    4403             :  *
    4404             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4405             :  * or NULL if an error occurs.
    4406             :  */
    4407             : 
    4408           1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
    4409             : 
    4410             : {
    4411           1 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
    4412             : 
    4413           1 :     return OGRGeometry::ToHandle(
    4414           1 :         OGRGeometry::FromHandle(hTarget)->ConvexHull());
    4415             : }
    4416             : 
    4417             : /************************************************************************/
    4418             : /*                            ConcaveHull()                             */
    4419             : /************************************************************************/
    4420             : 
    4421             : /**
    4422             :  * \brief Compute the concave hull of a geometry.
    4423             :  *
    4424             :  * The concave hull is fully contained within the convex hull and also
    4425             :  * contains all the points of the input, but in a smaller area.
    4426             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4427             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4428             :  * that contains all the points in the input Geometry.
    4429             :  *
    4430             :  * A new geometry object is created and returned containing the concave
    4431             :  * hull of the geometry on which the method is invoked.
    4432             :  *
    4433             :  * This method is the same as the C function OGR_G_ConcaveHull().
    4434             :  *
    4435             :  * This method is built on the GEOS >= 3.11 library
    4436             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4437             :  * fail, issuing a CPLE_NotSupported error.
    4438             :  *
    4439             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4440             :  * @param bAllowHoles Whether holes are allowed.
    4441             :  *
    4442             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4443             :  *
    4444             :  * @since GDAL 3.6
    4445             :  * @see OGRGeometry::ConcaveHullOfPolygons()
    4446             :  */
    4447             : 
    4448           8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
    4449             : {
    4450             : #ifndef HAVE_GEOS
    4451             :     (void)dfRatio;
    4452             :     (void)bAllowHoles;
    4453             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4454             :     return nullptr;
    4455             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4456             :     (void)dfRatio;
    4457             :     (void)bAllowHoles;
    4458             :     CPLError(CE_Failure, CPLE_NotSupported,
    4459             :              "GEOS 3.11 or later needed for ConcaveHull.");
    4460             :     return nullptr;
    4461             : #else
    4462           8 :     OGRGeometry *poOGRProduct = nullptr;
    4463             : 
    4464           8 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4465           8 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4466           8 :     if (hGeosGeom != nullptr)
    4467             :     {
    4468             :         GEOSGeom hGeosHull =
    4469           8 :             GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
    4470           8 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4471             : 
    4472             :         poOGRProduct =
    4473           8 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4474             :     }
    4475           8 :     freeGEOSContext(hGEOSCtxt);
    4476             : 
    4477           8 :     return poOGRProduct;
    4478             : #endif /* HAVE_GEOS */
    4479             : }
    4480             : 
    4481             : /************************************************************************/
    4482             : /*                         OGR_G_ConcaveHull()                          */
    4483             : /************************************************************************/
    4484             : /**
    4485             :  * \brief Compute the concave hull of a geometry.
    4486             :  *
    4487             :  * The concave hull is fully contained within the convex hull and also
    4488             :  * contains all the points of the input, but in a smaller area.
    4489             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4490             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4491             :  * that contains all the points in the input Geometry.
    4492             :  *
    4493             :  * A new geometry object is created and returned containing the convex
    4494             :  * hull of the geometry on which the function is invoked.
    4495             :  *
    4496             :  * This function is the same as the C++ method OGRGeometry::ConcaveHull().
    4497             :  *
    4498             :  * This function is built on the GEOS >= 3.11 library
    4499             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4500             :  * fail, issuing a CPLE_NotSupported error.
    4501             :  *
    4502             :  * @param hTarget The Geometry to calculate the concave hull of.
    4503             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4504             :  * @param bAllowHoles Whether holes are allowed.
    4505             :  *
    4506             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4507             :  * or NULL if an error occurs.
    4508             :  *
    4509             :  * @since GDAL 3.6
    4510             :  * @see OGR_G_ConcaveHullOfPolygons()
    4511             :  */
    4512             : 
    4513           2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
    4514             :                                bool bAllowHoles)
    4515             : 
    4516             : {
    4517           2 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
    4518             : 
    4519           2 :     return OGRGeometry::ToHandle(
    4520           2 :         OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
    4521             : }
    4522             : 
    4523             : /************************************************************************/
    4524             : /*                       ConcaveHullOfPolygons()                        */
    4525             : /************************************************************************/
    4526             : 
    4527             : /**
    4528             :  * \brief Compute the concave hull of a set of polygons, respecting
    4529             :  * the polygons as constraints.
    4530             :  *
    4531             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4532             :  * polygons.
    4533             :  * The computed hull "fills the gap" between the polygons,
    4534             :  * and does not intersect their interior.
    4535             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4536             :  * determined by a numeric target parameter.
    4537             :  *
    4538             :  * The concave hull is constructed by removing the longest outer edges
    4539             :  * of the Delaunay Triangulation of the space between the polygons,
    4540             :  * until the target criterion parameter is reached.
    4541             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4542             :  * between polygons to be no larger than this value.
    4543             :  * This can be expressed as a ratio between the lengths of the longest and
    4544             :  * shortest edges.
    4545             :  *
    4546             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4547             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4548             :  * for more details.
    4549             :  *
    4550             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4551             :  * be non-overlapping).
    4552             :  *
    4553             :  * A new geometry object is created and returned containing the concave
    4554             :  * hull of the geometry on which the method is invoked.
    4555             :  *
    4556             :  * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
    4557             :  *
    4558             :  * This method is built on the GEOS >= 3.11 library
    4559             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4560             :  * fail, issuing a CPLE_NotSupported error.
    4561             :  *
    4562             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4563             :  *                      difference between the longest and shortest edge lengths
    4564             :  *                      between the polygons.
    4565             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4566             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4567             :  *                      the original polygons.
    4568             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4569             :  *                 polygons.
    4570             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4571             :  *
    4572             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4573             :  *
    4574             :  * @since GDAL 3.13
    4575             :  * @see OGRGeometry::ConcaveHull()
    4576             :  */
    4577             : 
    4578          13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
    4579             :                                                 bool bIsTight,
    4580             :                                                 bool bAllowHoles) const
    4581             : {
    4582             : #ifndef HAVE_GEOS
    4583             :     (void)dfLengthRatio;
    4584             :     (void)bIsTight;
    4585             :     (void)bAllowHoles;
    4586             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4587             :     return nullptr;
    4588             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4589             :     (void)dfLengthRatio;
    4590             :     (void)bIsTight;
    4591             :     (void)bAllowHoles;
    4592             :     CPLError(CE_Failure, CPLE_NotSupported,
    4593             :              "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
    4594             :     return nullptr;
    4595             : #else
    4596          13 :     OGRGeometry *poOGRProduct = nullptr;
    4597             : 
    4598          13 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4599          13 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4600          13 :     if (hGeosGeom != nullptr)
    4601             :     {
    4602          13 :         GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
    4603             :             hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
    4604          13 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4605             : 
    4606             :         poOGRProduct =
    4607          13 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4608             :     }
    4609          13 :     freeGEOSContext(hGEOSCtxt);
    4610             : 
    4611          13 :     return poOGRProduct;
    4612             : #endif /* HAVE_GEOS */
    4613             : }
    4614             : 
    4615             : /************************************************************************/
    4616             : /*                    OGR_G_ConcaveHullOfPolygons()                     */
    4617             : /************************************************************************/
    4618             : /**
    4619             :  * \brief Compute the concave hull of a set of polygons, respecting
    4620             :  * the polygons as constraints.
    4621             :  *
    4622             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4623             :  * polygons.
    4624             :  * The computed hull "fills the gap" between the polygons,
    4625             :  * and does not intersect their interior.
    4626             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4627             :  * determined by a numeric target parameter.
    4628             :  *
    4629             :  * The concave hull is constructed by removing the longest outer edges
    4630             :  * of the Delaunay Triangulation of the space between the polygons,
    4631             :  * until the target criterion parameter is reached.
    4632             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4633             :  * between polygons to be no larger than this value.
    4634             :  * This can be expressed as a ratio between the lengths of the longest and
    4635             :  * shortest edges.
    4636             :  *
    4637             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4638             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4639             :  * for more details.
    4640             :  *
    4641             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4642             :  * be non-overlapping).
    4643             :  *
    4644             :  * A new geometry object is created and returned containing the concave
    4645             :  * hull of the geometry on which the method is invoked.
    4646             :  *
    4647             :  * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
    4648             :  *
    4649             :  * This function is built on the GEOS >= 3.11 library
    4650             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4651             :  * fail, issuing a CPLE_NotSupported error.
    4652             :  *
    4653             :  * @param hTarget The Geometry to calculate the concave hull of.
    4654             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4655             :  *                      difference between the longest and shortest edge lengths
    4656             :  *                      between the polygons.
    4657             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4658             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4659             :  *                      the original polygons.
    4660             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4661             :  *                 polygons.
    4662             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4663             :  *
    4664             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4665             :  * or NULL if an error occurs.
    4666             :  *
    4667             :  * @since GDAL 3.13
    4668             :  * @see OGR_G_ConcaveHull()
    4669             :  */
    4670             : 
    4671           7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
    4672             :                                          double dfLengthRatio, bool bIsTight,
    4673             :                                          bool bAllowHoles)
    4674             : 
    4675             : {
    4676           7 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
    4677             : 
    4678           7 :     return OGRGeometry::ToHandle(
    4679             :         OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
    4680           7 :             dfLengthRatio, bIsTight, bAllowHoles));
    4681             : }
    4682             : 
    4683             : /************************************************************************/
    4684             : /*                              Boundary()                              */
    4685             : /************************************************************************/
    4686             : 
    4687             : /**
    4688             :  * \brief Compute boundary.
    4689             :  *
    4690             :  * A new geometry object is created and returned containing the boundary
    4691             :  * of the geometry on which the method is invoked.
    4692             :  *
    4693             :  * This method is the same as the C function OGR_G_Boundary().
    4694             :  *
    4695             :  * This method is built on the GEOS library, check it for the definition
    4696             :  * of the geometry operation.
    4697             :  * If OGR is built without the GEOS library, this method will always fail,
    4698             :  * issuing a CPLE_NotSupported error.
    4699             :  *
    4700             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4701             :  *
    4702             :  */
    4703             : 
    4704           6 : OGRGeometry *OGRGeometry::Boundary() const
    4705             : 
    4706             : {
    4707             : #ifndef HAVE_GEOS
    4708             : 
    4709             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4710             :     return nullptr;
    4711             : 
    4712             : #else
    4713             : 
    4714           6 :     OGRGeometry *poOGRProduct = nullptr;
    4715             : 
    4716           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4717           6 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4718           6 :     if (hGeosGeom != nullptr)
    4719             :     {
    4720           6 :         GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
    4721           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4722             : 
    4723             :         poOGRProduct =
    4724           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4725             :     }
    4726           6 :     freeGEOSContext(hGEOSCtxt);
    4727             : 
    4728           6 :     return poOGRProduct;
    4729             : 
    4730             : #endif  // HAVE_GEOS
    4731             : }
    4732             : 
    4733             : //! @cond Doxygen_Suppress
    4734             : /**
    4735             :  * \brief Compute boundary (deprecated)
    4736             :  *
    4737             :  * @deprecated
    4738             :  *
    4739             :  * @see Boundary()
    4740             :  */
    4741           0 : OGRGeometry *OGRGeometry::getBoundary() const
    4742             : 
    4743             : {
    4744           0 :     return Boundary();
    4745             : }
    4746             : 
    4747             : //! @endcond
    4748             : 
    4749             : /************************************************************************/
    4750             : /*                           OGR_G_Boundary()                           */
    4751             : /************************************************************************/
    4752             : /**
    4753             :  * \brief Compute boundary.
    4754             :  *
    4755             :  * A new geometry object is created and returned containing the boundary
    4756             :  * of the geometry on which the method is invoked.
    4757             :  *
    4758             :  * This function is the same as the C++ method OGR_G_Boundary().
    4759             :  *
    4760             :  * This function is built on the GEOS library, check it for the definition
    4761             :  * of the geometry operation.
    4762             :  * If OGR is built without the GEOS library, this function will always fail,
    4763             :  * issuing a CPLE_NotSupported error.
    4764             :  *
    4765             :  * @param hTarget The Geometry to calculate the boundary of.
    4766             :  *
    4767             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4768             :  * or NULL if an error occurs.
    4769             :  *
    4770             :  */
    4771           6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
    4772             : 
    4773             : {
    4774           6 :     VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
    4775             : 
    4776           6 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4777             : }
    4778             : 
    4779             : /**
    4780             :  * \brief Compute boundary (deprecated)
    4781             :  *
    4782             :  * @deprecated
    4783             :  *
    4784             :  * @see OGR_G_Boundary()
    4785             :  */
    4786           0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
    4787             : 
    4788             : {
    4789           0 :     VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
    4790             : 
    4791           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4792             : }
    4793             : 
    4794             : /************************************************************************/
    4795             : /*                               Buffer()                               */
    4796             : /************************************************************************/
    4797             : 
    4798             : /**
    4799             :  * \brief Compute buffer of geometry.
    4800             :  *
    4801             :  * Builds a new geometry containing the buffer region around the geometry
    4802             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4803             :  * the buffer distance of the original geometry.
    4804             :  *
    4805             :  * Some buffer sections are properly described as curves, but are converted to
    4806             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4807             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4808             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4809             :  * large numbers of vertices in the resulting buffer geometry while small
    4810             :  * numbers reduce the accuracy of the result.
    4811             :  *
    4812             :  * This method is the same as the C function OGR_G_Buffer().
    4813             :  *
    4814             :  * This method is built on the GEOS library, check it for the definition
    4815             :  * of the geometry operation.
    4816             :  * If OGR is built without the GEOS library, this method will always fail,
    4817             :  * issuing a CPLE_NotSupported error.
    4818             :  *
    4819             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4820             :  *               the same unit as the coordinates of the geometry.
    4821             :  *
    4822             :  * @param nQuadSegs the number of segments used to approximate a 90
    4823             :  * degree (quadrant) of curvature.
    4824             :  *
    4825             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4826             :  */
    4827             : 
    4828          39 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
    4829             : 
    4830             : {
    4831             :     (void)dfDist;
    4832             :     (void)nQuadSegs;
    4833             : #ifndef HAVE_GEOS
    4834             : 
    4835             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4836             :     return nullptr;
    4837             : 
    4838             : #else
    4839             : 
    4840          39 :     OGRGeometry *poOGRProduct = nullptr;
    4841             : 
    4842          39 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4843          39 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4844          39 :     if (hGeosGeom != nullptr)
    4845             :     {
    4846             :         GEOSGeom hGeosProduct =
    4847          39 :             GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
    4848          39 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4849             : 
    4850             :         poOGRProduct =
    4851          39 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4852             :     }
    4853          39 :     freeGEOSContext(hGEOSCtxt);
    4854             : 
    4855          39 :     return poOGRProduct;
    4856             : 
    4857             : #endif  // HAVE_GEOS
    4858             : }
    4859             : 
    4860             : /************************************************************************/
    4861             : /*                            OGR_G_Buffer()                            */
    4862             : /************************************************************************/
    4863             : 
    4864             : /**
    4865             :  * \brief Compute buffer of geometry.
    4866             :  *
    4867             :  * Builds a new geometry containing the buffer region around the geometry
    4868             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4869             :  * the buffer distance of the original geometry.
    4870             :  *
    4871             :  * Some buffer sections are properly described as curves, but are converted to
    4872             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4873             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4874             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4875             :  * large numbers of vertices in the resulting buffer geometry while small
    4876             :  * numbers reduce the accuracy of the result.
    4877             :  *
    4878             :  * This function is the same as the C++ method OGRGeometry::Buffer().
    4879             :  *
    4880             :  * This function is built on the GEOS library, check it for the definition
    4881             :  * of the geometry operation.
    4882             :  * If OGR is built without the GEOS library, this function will always fail,
    4883             :  * issuing a CPLE_NotSupported error.
    4884             :  *
    4885             :  * @param hTarget the geometry.
    4886             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4887             :  *               the same unit as the coordinates of the geometry.
    4888             :  *
    4889             :  * @param nQuadSegs the number of segments used to approximate a 90 degree
    4890             :  * (quadrant) of curvature.
    4891             :  *
    4892             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4893             :  * or NULL if an error occurs.
    4894             :  */
    4895             : 
    4896          39 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
    4897             : 
    4898             : {
    4899          39 :     VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
    4900             : 
    4901          39 :     return OGRGeometry::ToHandle(
    4902          39 :         OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
    4903             : }
    4904             : 
    4905             : /**
    4906             :  * \brief Compute buffer of geometry.
    4907             :  *
    4908             :  * Builds a new geometry containing the buffer region around the geometry
    4909             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4910             :  * the buffer distance of the original geometry.
    4911             :  *
    4912             :  * This function is built on the GEOS library, check it for the definition
    4913             :  * of the geometry operation.
    4914             :  * If OGR is built without the GEOS library, this function will always fail,
    4915             :  * issuing a CPLE_NotSupported error.
    4916             :  *
    4917             :  * The following options are supported. See the GEOS library for more detailed
    4918             :  * descriptions.
    4919             :  *
    4920             :  * <ul>
    4921             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    4922             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    4923             :  * <li>MITRE_LIMIT=double</li>
    4924             :  * <li>QUADRANT_SEGMENTS=int</li>
    4925             :  * <li>SINGLE_SIDED=YES/NO</li>
    4926             :  * </ul>
    4927             :  *
    4928             :  * This function is the same as the C function OGR_G_BufferEx().
    4929             :  *
    4930             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4931             :  *               the same unit as the coordinates of the geometry.
    4932             :  * @param papszOptions NULL terminated list of options (may be NULL)
    4933             :  *
    4934             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4935             :  *
    4936             :  * @since GDAL 3.10
    4937             :  */
    4938             : 
    4939          35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
    4940             :                                    CSLConstList papszOptions) const
    4941             : {
    4942             :     (void)dfDist;
    4943             :     (void)papszOptions;
    4944             : #ifndef HAVE_GEOS
    4945             : 
    4946             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4947             :     return nullptr;
    4948             : 
    4949             : #else
    4950          35 :     OGRGeometry *poOGRProduct = nullptr;
    4951          35 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4952             : 
    4953          35 :     auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
    4954          35 :     bool bParamsAreValid = true;
    4955             : 
    4956         166 :     for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
    4957             :     {
    4958         131 :         if (EQUAL(pszParam, "ENDCAP_STYLE"))
    4959             :         {
    4960             :             int nStyle;
    4961          25 :             if (EQUAL(pszValue, "ROUND"))
    4962             :             {
    4963          22 :                 nStyle = GEOSBUF_CAP_ROUND;
    4964             :             }
    4965           3 :             else if (EQUAL(pszValue, "FLAT"))
    4966             :             {
    4967           1 :                 nStyle = GEOSBUF_CAP_FLAT;
    4968             :             }
    4969           2 :             else if (EQUAL(pszValue, "SQUARE"))
    4970             :             {
    4971           1 :                 nStyle = GEOSBUF_CAP_SQUARE;
    4972             :             }
    4973             :             else
    4974             :             {
    4975           1 :                 bParamsAreValid = false;
    4976           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    4977             :                          "Invalid value for ENDCAP_STYLE: %s", pszValue);
    4978           2 :                 break;
    4979             :             }
    4980             : 
    4981          24 :             if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
    4982             :             {
    4983           0 :                 bParamsAreValid = false;
    4984             :             }
    4985             :         }
    4986         106 :         else if (EQUAL(pszParam, "JOIN_STYLE"))
    4987             :         {
    4988             :             int nStyle;
    4989          25 :             if (EQUAL(pszValue, "ROUND"))
    4990             :             {
    4991          21 :                 nStyle = GEOSBUF_JOIN_ROUND;
    4992             :             }
    4993           4 :             else if (EQUAL(pszValue, "MITRE"))
    4994             :             {
    4995           3 :                 nStyle = GEOSBUF_JOIN_MITRE;
    4996             :             }
    4997           1 :             else if (EQUAL(pszValue, "BEVEL"))
    4998             :             {
    4999           0 :                 nStyle = GEOSBUF_JOIN_BEVEL;
    5000             :             }
    5001             :             else
    5002             :             {
    5003           1 :                 bParamsAreValid = false;
    5004           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    5005             :                          "Invalid value for JOIN_STYLE: %s", pszValue);
    5006           1 :                 break;
    5007             :             }
    5008             : 
    5009          24 :             if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
    5010             :             {
    5011           0 :                 bParamsAreValid = false;
    5012           0 :                 break;
    5013             :             }
    5014             :         }
    5015          81 :         else if (EQUAL(pszParam, "MITRE_LIMIT"))
    5016             :         {
    5017             :             try
    5018             :             {
    5019             :                 std::size_t end;
    5020          30 :                 double dfLimit = std::stod(pszValue, &end);
    5021             : 
    5022          24 :                 if (end != strlen(pszValue))
    5023             :                 {
    5024           0 :                     throw std::invalid_argument("");
    5025             :                 }
    5026             : 
    5027          24 :                 if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
    5028             :                                                       dfLimit))
    5029             :                 {
    5030           0 :                     bParamsAreValid = false;
    5031           0 :                     break;
    5032             :                 }
    5033             :             }
    5034           4 :             catch (const std::invalid_argument &)
    5035             :             {
    5036           2 :                 bParamsAreValid = false;
    5037           2 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5038             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5039             :             }
    5040           0 :             catch (const std::out_of_range &)
    5041             :             {
    5042           0 :                 bParamsAreValid = false;
    5043           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5044             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5045             :             }
    5046             :         }
    5047          55 :         else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
    5048             :         {
    5049             :             try
    5050             :             {
    5051             :                 std::size_t end;
    5052          38 :                 int nQuadSegs = std::stoi(pszValue, &end, 10);
    5053             : 
    5054          26 :                 if (end != strlen(pszValue))
    5055             :                 {
    5056           0 :                     throw std::invalid_argument("");
    5057             :                 }
    5058             : 
    5059          26 :                 if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
    5060             :                                                             nQuadSegs))
    5061             :                 {
    5062           0 :                     bParamsAreValid = false;
    5063           0 :                     break;
    5064             :                 }
    5065             :             }
    5066           6 :             catch (const std::invalid_argument &)
    5067             :             {
    5068           3 :                 bParamsAreValid = false;
    5069           3 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5070             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5071             :             }
    5072           2 :             catch (const std::out_of_range &)
    5073             :             {
    5074           1 :                 bParamsAreValid = false;
    5075           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5076             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5077             :             }
    5078             :         }
    5079          25 :         else if (EQUAL(pszParam, "SINGLE_SIDED"))
    5080             :         {
    5081          24 :             bool bSingleSided = CPLTestBool(pszValue);
    5082             : 
    5083          24 :             if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
    5084             :                                                    bSingleSided))
    5085             :             {
    5086           0 :                 bParamsAreValid = false;
    5087           0 :                 break;
    5088             :             }
    5089             :         }
    5090             :         else
    5091             :         {
    5092           1 :             bParamsAreValid = false;
    5093           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    5094             :                      "Unsupported buffer option: %s", pszValue);
    5095             :         }
    5096             :     }
    5097             : 
    5098          35 :     if (bParamsAreValid)
    5099             :     {
    5100          26 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    5101          26 :         if (hGeosGeom != nullptr)
    5102             :         {
    5103             :             GEOSGeom hGeosProduct =
    5104          26 :                 GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
    5105          26 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    5106             : 
    5107          26 :             if (hGeosProduct != nullptr)
    5108             :             {
    5109          26 :                 poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
    5110             :                                                      this, nullptr);
    5111             :             }
    5112             :         }
    5113             :     }
    5114             : 
    5115          35 :     GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
    5116          35 :     freeGEOSContext(hGEOSCtxt);
    5117          35 :     return poOGRProduct;
    5118             : #endif
    5119             : }
    5120             : 
    5121             : /**
    5122             :  * \brief Compute buffer of geometry.
    5123             :  *
    5124             :  * Builds a new geometry containing the buffer region around the geometry
    5125             :  * on which it is invoked.  The buffer is a polygon containing the region within
    5126             :  * the buffer distance of the original geometry.
    5127             :  *
    5128             :  * This function is built on the GEOS library, check it for the definition
    5129             :  * of the geometry operation.
    5130             :  * If OGR is built without the GEOS library, this function will always fail,
    5131             :  * issuing a CPLE_NotSupported error.
    5132             :  *
    5133             :  * The following options are supported. See the GEOS library for more detailed
    5134             :  * descriptions.
    5135             :  *
    5136             :  * <ul>
    5137             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    5138             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    5139             :  * <li>MITRE_LIMIT=double</li>
    5140             :  * <li>QUADRANT_SEGMENTS=int</li>
    5141             :  * <li>SINGLE_SIDED=YES/NO</li>
    5142             :  * </ul>
    5143             :  *
    5144             :  * This function is the same as the C++ method OGRGeometry::BufferEx().
    5145             :  *
    5146             :  * @param hTarget the geometry.
    5147             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5148             :  *               the same unit as the coordinates of the geometry.
    5149             :  * @param papszOptions NULL terminated list of options (may be NULL)
    5150             :  *
    5151             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5152             :  * or NULL if an error occurs.
    5153             :  *
    5154             :  * @since GDAL 3.10
    5155             :  */
    5156             : 
    5157          12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
    5158             :                             CSLConstList papszOptions)
    5159             : 
    5160             : {
    5161          12 :     VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
    5162             : 
    5163          12 :     return OGRGeometry::ToHandle(
    5164          12 :         OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
    5165             : }
    5166             : 
    5167             : /************************************************************************/
    5168             : /*                            Intersection()                            */
    5169             : /************************************************************************/
    5170             : 
    5171             : /**
    5172             :  * \brief Compute intersection.
    5173             :  *
    5174             :  * Generates a new geometry which is the region of intersection of the
    5175             :  * two geometries operated on.  The Intersects() method can be used to test if
    5176             :  * two geometries intersect.
    5177             :  *
    5178             :  * Geometry validity is not checked. In case you are unsure of the validity
    5179             :  * of the input geometries, call IsValid() before, otherwise the result might
    5180             :  * be wrong.
    5181             :  *
    5182             :  * This method is the same as the C function OGR_G_Intersection().
    5183             :  *
    5184             :  * This method is built on the GEOS library, check it for the definition
    5185             :  * of the geometry operation.
    5186             :  * If OGR is built without the GEOS library, this method will always fail,
    5187             :  * issuing a CPLE_NotSupported error.
    5188             :  *
    5189             :  * @param poOtherGeom the other geometry intersected with "this" geometry.
    5190             :  *
    5191             :  * @return a new geometry to be freed by the caller, or NULL if there is no
    5192             :  * intersection or if an error occurs.
    5193             :  *
    5194             :  */
    5195             : 
    5196             : OGRGeometry *
    5197        1950 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5198             : 
    5199             : {
    5200        1950 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5201             :     {
    5202             : #ifndef HAVE_SFCGAL
    5203             : 
    5204           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5205           0 :         return nullptr;
    5206             : 
    5207             : #else
    5208             : 
    5209             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5210             :         if (poThis == nullptr)
    5211             :             return nullptr;
    5212             : 
    5213             :         sfcgal_geometry_t *poOther =
    5214             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5215             :         if (poOther == nullptr)
    5216             :         {
    5217             :             sfcgal_geometry_delete(poThis);
    5218             :             return nullptr;
    5219             :         }
    5220             : 
    5221             :         sfcgal_geometry_t *poRes =
    5222             :             sfcgal_geometry_intersection_3d(poThis, poOther);
    5223             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    5224             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5225             :             poOtherGeom->getSpatialReference() != nullptr &&
    5226             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5227             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5228             : 
    5229             :         sfcgal_geometry_delete(poThis);
    5230             :         sfcgal_geometry_delete(poOther);
    5231             :         sfcgal_geometry_delete(poRes);
    5232             : 
    5233             :         return h_prodGeom;
    5234             : 
    5235             : #endif
    5236             :     }
    5237             : 
    5238             :     else
    5239             :     {
    5240             : #ifndef HAVE_GEOS
    5241             : 
    5242             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5243             :         return nullptr;
    5244             : 
    5245             : #else
    5246        1950 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
    5247             : #endif /* HAVE_GEOS */
    5248             :     }
    5249             : }
    5250             : 
    5251             : /************************************************************************/
    5252             : /*                         OGR_G_Intersection()                         */
    5253             : /************************************************************************/
    5254             : 
    5255             : /**
    5256             :  * \brief Compute intersection.
    5257             :  *
    5258             :  * Generates a new geometry which is the region of intersection of the
    5259             :  * two geometries operated on.  The OGR_G_Intersects() function can be used to
    5260             :  * test if two geometries intersect.
    5261             :  *
    5262             :  * Geometry validity is not checked. In case you are unsure of the validity
    5263             :  * of the input geometries, call IsValid() before, otherwise the result might
    5264             :  * be wrong.
    5265             :  *
    5266             :  * This function is the same as the C++ method OGRGeometry::Intersection().
    5267             :  *
    5268             :  * This function is built on the GEOS library, check it for the definition
    5269             :  * of the geometry operation.
    5270             :  * If OGR is built without the GEOS library, this function will always fail,
    5271             :  * issuing a CPLE_NotSupported error.
    5272             :  *
    5273             :  * @param hThis the geometry.
    5274             :  * @param hOther the other geometry.
    5275             :  *
    5276             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5277             :  * or NULL if there is not intersection of if an error occurs.
    5278             :  */
    5279             : 
    5280          12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
    5281             : 
    5282             : {
    5283          12 :     VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
    5284             : 
    5285          24 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
    5286          24 :         OGRGeometry::FromHandle(hOther)));
    5287             : }
    5288             : 
    5289             : /************************************************************************/
    5290             : /*                               Union()                                */
    5291             : /************************************************************************/
    5292             : 
    5293             : /**
    5294             :  * \brief Compute union.
    5295             :  *
    5296             :  * Generates a new geometry which is the region of union of the
    5297             :  * two geometries operated on.
    5298             :  *
    5299             :  * Geometry validity is not checked. In case you are unsure of the validity
    5300             :  * of the input geometries, call IsValid() before, otherwise the result might
    5301             :  * be wrong.
    5302             :  *
    5303             :  * This method is the same as the C function OGR_G_Union().
    5304             :  *
    5305             :  * This method is built on the GEOS library, check it for the definition
    5306             :  * of the geometry operation.
    5307             :  * If OGR is built without the GEOS library, this method will always fail,
    5308             :  * issuing a CPLE_NotSupported error.
    5309             :  *
    5310             :  * @param poOtherGeom the other geometry unioned with "this" geometry.
    5311             :  *
    5312             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5313             :  */
    5314             : 
    5315             : OGRGeometry *
    5316          68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5317             : 
    5318             : {
    5319          68 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5320             :     {
    5321             : #ifndef HAVE_SFCGAL
    5322             : 
    5323           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5324           0 :         return nullptr;
    5325             : 
    5326             : #else
    5327             : 
    5328             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5329             :         if (poThis == nullptr)
    5330             :             return nullptr;
    5331             : 
    5332             :         sfcgal_geometry_t *poOther =
    5333             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5334             :         if (poOther == nullptr)
    5335             :         {
    5336             :             sfcgal_geometry_delete(poThis);
    5337             :             return nullptr;
    5338             :         }
    5339             : 
    5340             :         sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
    5341             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5342             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5343             :             poOtherGeom->getSpatialReference() != nullptr &&
    5344             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5345             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5346             : 
    5347             :         sfcgal_geometry_delete(poThis);
    5348             :         sfcgal_geometry_delete(poOther);
    5349             :         sfcgal_geometry_delete(poRes);
    5350             : 
    5351             :         return h_prodGeom;
    5352             : 
    5353             : #endif
    5354             :     }
    5355             : 
    5356             :     else
    5357             :     {
    5358             : #ifndef HAVE_GEOS
    5359             : 
    5360             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5361             :         return nullptr;
    5362             : 
    5363             : #else
    5364          68 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
    5365             : #endif /* HAVE_GEOS */
    5366             :     }
    5367             : }
    5368             : 
    5369             : /************************************************************************/
    5370             : /*                            OGR_G_Union()                             */
    5371             : /************************************************************************/
    5372             : 
    5373             : /**
    5374             :  * \brief Compute union.
    5375             :  *
    5376             :  * Generates a new geometry which is the region of union of the
    5377             :  * two geometries operated on.
    5378             :  *
    5379             :  * Geometry validity is not checked. In case you are unsure of the validity
    5380             :  * of the input geometries, call IsValid() before, otherwise the result might
    5381             :  * be wrong.
    5382             :  *
    5383             :  * This function is the same as the C++ method OGRGeometry::Union().
    5384             :  *
    5385             :  * This function is built on the GEOS library, check it for the definition
    5386             :  * of the geometry operation.
    5387             :  * If OGR is built without the GEOS library, this function will always fail,
    5388             :  * issuing a CPLE_NotSupported error.
    5389             :  *
    5390             :  * @param hThis the geometry.
    5391             :  * @param hOther the other geometry.
    5392             :  *
    5393             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5394             :  * or NULL if an error occurs.
    5395             :  */
    5396             : 
    5397          10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
    5398             : 
    5399             : {
    5400          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
    5401             : 
    5402          20 :     return OGRGeometry::ToHandle(
    5403          20 :         OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
    5404             : }
    5405             : 
    5406             : /************************************************************************/
    5407             : /*                           UnionCascaded()                            */
    5408             : /************************************************************************/
    5409             : 
    5410             : /**
    5411             :  * \brief Compute union using cascading.
    5412             :  *
    5413             :  * Geometry validity is not checked. In case you are unsure of the validity
    5414             :  * of the input geometries, call IsValid() before, otherwise the result might
    5415             :  * be wrong.
    5416             :  *
    5417             :  * The input geometry must be a MultiPolygon.
    5418             :  *
    5419             :  * This method is the same as the C function OGR_G_UnionCascaded().
    5420             :  *
    5421             :  * This method is built on the GEOS library, check it for the definition
    5422             :  * of the geometry operation.
    5423             :  * If OGR is built without the GEOS library, this method will always fail,
    5424             :  * issuing a CPLE_NotSupported error.
    5425             :  *
    5426             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5427             :  *
    5428             :  *
    5429             :  * @deprecated Use UnaryUnion() instead
    5430             :  */
    5431             : 
    5432           2 : OGRGeometry *OGRGeometry::UnionCascaded() const
    5433             : 
    5434             : {
    5435             : #ifndef HAVE_GEOS
    5436             : 
    5437             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5438             :     return nullptr;
    5439             : #else
    5440             : 
    5441             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5442             :     if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
    5443             :     {
    5444             :         // GEOS < 3.11 crashes on an empty multipolygon input
    5445             :         auto poRet = new OGRGeometryCollection();
    5446             :         poRet->assignSpatialReference(getSpatialReference());
    5447             :         return poRet;
    5448             :     }
    5449             : #endif
    5450           2 :     OGRGeometry *poOGRProduct = nullptr;
    5451             : 
    5452           2 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5453           2 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5454           2 :     if (hThisGeosGeom != nullptr)
    5455             :     {
    5456           2 :         GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
    5457           2 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5458             : 
    5459             :         poOGRProduct =
    5460           2 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5461             :     }
    5462           2 :     freeGEOSContext(hGEOSCtxt);
    5463             : 
    5464           2 :     return poOGRProduct;
    5465             : 
    5466             : #endif  // HAVE_GEOS
    5467             : }
    5468             : 
    5469             : /************************************************************************/
    5470             : /*                        OGR_G_UnionCascaded()                         */
    5471             : /************************************************************************/
    5472             : 
    5473             : /**
    5474             :  * \brief Compute union using cascading.
    5475             :  *
    5476             :  * Geometry validity is not checked. In case you are unsure of the validity
    5477             :  * of the input geometries, call IsValid() before, otherwise the result might
    5478             :  * be wrong.
    5479             :  *
    5480             :  * The input geometry must be a MultiPolygon.
    5481             :  *
    5482             :  * This function is the same as the C++ method OGRGeometry::UnionCascaded().
    5483             :  *
    5484             :  * This function is built on the GEOS library, check it for the definition
    5485             :  * of the geometry operation.
    5486             :  * If OGR is built without the GEOS library, this function will always fail,
    5487             :  * issuing a CPLE_NotSupported error.
    5488             :  *
    5489             :  * @param hThis the geometry.
    5490             :  *
    5491             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5492             :  * or NULL if an error occurs.
    5493             :  *
    5494             :  * @deprecated Use OGR_G_UnaryUnion() instead
    5495             :  */
    5496             : 
    5497           2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
    5498             : 
    5499             : {
    5500           2 :     VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
    5501             : 
    5502           2 :     return OGRGeometry::ToHandle(
    5503           2 :         OGRGeometry::FromHandle(hThis)->UnionCascaded());
    5504             : }
    5505             : 
    5506             : /************************************************************************/
    5507             : /*                             UnaryUnion()                             */
    5508             : /************************************************************************/
    5509             : 
    5510             : /**
    5511             :  * \brief Returns the union of all components of a single geometry.
    5512             :  *
    5513             :  * Usually used to convert a collection into the smallest set of polygons that
    5514             :  * cover the same area.
    5515             :  *
    5516             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5517             :  *
    5518             :  * This method is the same as the C function OGR_G_UnaryUnion().
    5519             :  *
    5520             :  * This method is built on the GEOS library, check it for the definition
    5521             :  * of the geometry operation.
    5522             :  * If OGR is built without the GEOS library, this method will always fail,
    5523             :  * issuing a CPLE_NotSupported error.
    5524             :  *
    5525             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5526             :  *
    5527             :  * @since GDAL 3.7
    5528             :  */
    5529             : 
    5530         629 : OGRGeometry *OGRGeometry::UnaryUnion() const
    5531             : 
    5532             : {
    5533             : #ifndef HAVE_GEOS
    5534             : 
    5535             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5536             :     return nullptr;
    5537             : #else
    5538             : 
    5539             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5540             :     if (IsEmpty())
    5541             :     {
    5542             :         // GEOS < 3.11 crashes on an empty geometry
    5543             :         auto poRet = new OGRGeometryCollection();
    5544             :         poRet->assignSpatialReference(getSpatialReference());
    5545             :         return poRet;
    5546             :     }
    5547             : #endif
    5548         629 :     OGRGeometry *poOGRProduct = nullptr;
    5549             : 
    5550         629 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5551         629 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5552         629 :     if (hThisGeosGeom != nullptr)
    5553             :     {
    5554         629 :         GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
    5555         629 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5556             : 
    5557             :         poOGRProduct =
    5558         629 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5559             :     }
    5560         629 :     freeGEOSContext(hGEOSCtxt);
    5561             : 
    5562         629 :     return poOGRProduct;
    5563             : 
    5564             : #endif  // HAVE_GEOS
    5565             : }
    5566             : 
    5567             : /************************************************************************/
    5568             : /*                          OGR_G_UnaryUnion()                          */
    5569             : /************************************************************************/
    5570             : 
    5571             : /**
    5572             :  * \brief Returns the union of all components of a single geometry.
    5573             :  *
    5574             :  * Usually used to convert a collection into the smallest set of polygons that
    5575             :  * cover the same area.
    5576             :  *
    5577             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    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 function is the same as the C++ method OGRGeometry::UnaryUnion().
    5584             :  *
    5585             :  * This function 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 function will always fail,
    5588             :  * issuing a CPLE_NotSupported error.
    5589             :  *
    5590             :  * @param hThis the geometry.
    5591             :  *
    5592             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5593             :  * or NULL if an error occurs.
    5594             :  *
    5595             :  * @since GDAL 3.7
    5596             :  */
    5597             : 
    5598           3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
    5599             : 
    5600             : {
    5601           3 :     VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
    5602             : 
    5603           3 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
    5604             : }
    5605             : 
    5606             : /************************************************************************/
    5607             : /*                             Difference()                             */
    5608             : /************************************************************************/
    5609             : 
    5610             : /**
    5611             :  * \brief Compute difference.
    5612             :  *
    5613             :  * Generates a new geometry which is the region of this geometry with the
    5614             :  * region of the second geometry removed.
    5615             :  *
    5616             :  * Geometry validity is not checked. In case you are unsure of the validity
    5617             :  * of the input geometries, call IsValid() before, otherwise the result might
    5618             :  * be wrong.
    5619             :  *
    5620             :  * This method is the same as the C function OGR_G_Difference().
    5621             :  *
    5622             :  * This method is built on the GEOS library, check it for the definition
    5623             :  * of the geometry operation.
    5624             :  * If OGR is built without the GEOS library, this method will always fail,
    5625             :  * issuing a CPLE_NotSupported error.
    5626             :  *
    5627             :  * @param poOtherGeom the other geometry removed from "this" geometry.
    5628             :  *
    5629             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5630             :  * is empty or if an error occurs.
    5631             :  */
    5632             : 
    5633             : OGRGeometry *
    5634         752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5635             : 
    5636             : {
    5637         752 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5638             :     {
    5639             : #ifndef HAVE_SFCGAL
    5640             : 
    5641           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5642           0 :         return nullptr;
    5643             : 
    5644             : #else
    5645             : 
    5646             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5647             :         if (poThis == nullptr)
    5648             :             return nullptr;
    5649             : 
    5650             :         sfcgal_geometry_t *poOther =
    5651             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5652             :         if (poOther == nullptr)
    5653             :         {
    5654             :             sfcgal_geometry_delete(poThis);
    5655             :             return nullptr;
    5656             :         }
    5657             : 
    5658             :         sfcgal_geometry_t *poRes =
    5659             :             sfcgal_geometry_difference_3d(poThis, poOther);
    5660             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5661             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5662             :             poOtherGeom->getSpatialReference() != nullptr &&
    5663             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5664             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5665             : 
    5666             :         sfcgal_geometry_delete(poThis);
    5667             :         sfcgal_geometry_delete(poOther);
    5668             :         sfcgal_geometry_delete(poRes);
    5669             : 
    5670             :         return h_prodGeom;
    5671             : 
    5672             : #endif
    5673             :     }
    5674             : 
    5675             :     else
    5676             :     {
    5677             : #ifndef HAVE_GEOS
    5678             : 
    5679             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5680             :         return nullptr;
    5681             : 
    5682             : #else
    5683         752 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
    5684             : #endif /* HAVE_GEOS */
    5685             :     }
    5686             : }
    5687             : 
    5688             : /************************************************************************/
    5689             : /*                          OGR_G_Difference()                          */
    5690             : /************************************************************************/
    5691             : 
    5692             : /**
    5693             :  * \brief Compute difference.
    5694             :  *
    5695             :  * Generates a new geometry which is the region of this geometry with the
    5696             :  * region of the other geometry removed.
    5697             :  *
    5698             :  * Geometry validity is not checked. In case you are unsure of the validity
    5699             :  * of the input geometries, call IsValid() before, otherwise the result might
    5700             :  * be wrong.
    5701             :  *
    5702             :  * This function is the same as the C++ method OGRGeometry::Difference().
    5703             :  *
    5704             :  * This function is built on the GEOS library, check it for the definition
    5705             :  * of the geometry operation.
    5706             :  * If OGR is built without the GEOS library, this function will always fail,
    5707             :  * issuing a CPLE_NotSupported error.
    5708             :  *
    5709             :  * @param hThis the geometry.
    5710             :  * @param hOther the other geometry.
    5711             :  *
    5712             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5713             :  * or NULL if the difference is empty or if an error occurs.
    5714             :  */
    5715             : 
    5716           6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
    5717             : 
    5718             : {
    5719           6 :     VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
    5720             : 
    5721          12 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
    5722          12 :         OGRGeometry::FromHandle(hOther)));
    5723             : }
    5724             : 
    5725             : /************************************************************************/
    5726             : /*                           SymDifference()                            */
    5727             : /************************************************************************/
    5728             : 
    5729             : /**
    5730             :  * \brief Compute symmetric difference.
    5731             :  *
    5732             :  * Generates a new geometry which is the symmetric difference of this
    5733             :  * geometry and the second geometry passed into the method.
    5734             :  *
    5735             :  * Geometry validity is not checked. In case you are unsure of the validity
    5736             :  * of the input geometries, call IsValid() before, otherwise the result might
    5737             :  * be wrong.
    5738             :  *
    5739             :  * This method is the same as the C function OGR_G_SymDifference().
    5740             :  *
    5741             :  * This method is built on the GEOS library, check it for the definition
    5742             :  * of the geometry operation.
    5743             :  * If OGR is built without the GEOS library, this method will always fail,
    5744             :  * issuing a CPLE_NotSupported error.
    5745             :  *
    5746             :  * @param poOtherGeom the other geometry.
    5747             :  *
    5748             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5749             :  * is empty or if an error occurs.
    5750             :  *
    5751             :  */
    5752             : 
    5753           7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
    5754             : 
    5755             : {
    5756             :     (void)poOtherGeom;
    5757           7 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5758             :     {
    5759             : #ifndef HAVE_SFCGAL
    5760           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5761           0 :         return nullptr;
    5762             : #else
    5763             :         OGRGeometry *poFirstDifference = Difference(poOtherGeom);
    5764             :         if (poFirstDifference == nullptr)
    5765             :             return nullptr;
    5766             : 
    5767             :         OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
    5768             :         if (poOtherDifference == nullptr)
    5769             :         {
    5770             :             delete poFirstDifference;
    5771             :             return nullptr;
    5772             :         }
    5773             : 
    5774             :         OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
    5775             :         delete poFirstDifference;
    5776             :         delete poOtherDifference;
    5777             :         return poSymDiff;
    5778             : #endif
    5779             :     }
    5780             : 
    5781             : #ifndef HAVE_GEOS
    5782             : 
    5783             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5784             :     return nullptr;
    5785             : 
    5786             : #else
    5787           7 :     return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
    5788             : #endif  // HAVE_GEOS
    5789             : }
    5790             : 
    5791             : //! @cond Doxygen_Suppress
    5792             : /**
    5793             :  * \brief Compute symmetric difference (deprecated)
    5794             :  *
    5795             :  * @deprecated
    5796             :  *
    5797             :  * @see OGRGeometry::SymDifference()
    5798             :  */
    5799             : OGRGeometry *
    5800           0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
    5801             : 
    5802             : {
    5803           0 :     return SymDifference(poOtherGeom);
    5804             : }
    5805             : 
    5806             : //! @endcond
    5807             : 
    5808             : /************************************************************************/
    5809             : /*                        OGR_G_SymDifference()                         */
    5810             : /************************************************************************/
    5811             : 
    5812             : /**
    5813             :  * \brief Compute symmetric difference.
    5814             :  *
    5815             :  * Generates a new geometry which is the symmetric difference of this
    5816             :  * geometry and the other geometry.
    5817             :  *
    5818             :  * Geometry validity is not checked. In case you are unsure of the validity
    5819             :  * of the input geometries, call IsValid() before, otherwise the result might
    5820             :  * be wrong.
    5821             :  *
    5822             :  * This function is the same as the C++ method
    5823             :  * OGRGeometry::SymmetricDifference().
    5824             :  *
    5825             :  * This function is built on the GEOS library, check it for the definition
    5826             :  * of the geometry operation.
    5827             :  * If OGR is built without the GEOS library, this function will always fail,
    5828             :  * issuing a CPLE_NotSupported error.
    5829             :  *
    5830             :  * @param hThis the geometry.
    5831             :  * @param hOther the other geometry.
    5832             :  *
    5833             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5834             :  * or NULL if the difference is empty or if an error occurs.
    5835             :  *
    5836             :  */
    5837             : 
    5838           7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5839             : 
    5840             : {
    5841           7 :     VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
    5842             : 
    5843          14 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5844          14 :         OGRGeometry::FromHandle(hOther)));
    5845             : }
    5846             : 
    5847             : /**
    5848             :  * \brief Compute symmetric difference (deprecated)
    5849             :  *
    5850             :  * @deprecated
    5851             :  *
    5852             :  * @see OGR_G_SymmetricDifference()
    5853             :  */
    5854           0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5855             : 
    5856             : {
    5857           0 :     VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
    5858             : 
    5859           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5860           0 :         OGRGeometry::FromHandle(hOther)));
    5861             : }
    5862             : 
    5863             : /************************************************************************/
    5864             : /*                              Disjoint()                              */
    5865             : /************************************************************************/
    5866             : 
    5867             : /**
    5868             :  * \brief Test for disjointness.
    5869             :  *
    5870             :  * Tests if this geometry and the other passed into the method are disjoint.
    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 method is the same as the C function OGR_G_Disjoint().
    5877             :  *
    5878             :  * This method 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 method will always fail,
    5881             :  * issuing a CPLE_NotSupported error.
    5882             :  *
    5883             :  * @param poOtherGeom the geometry to compare to this geometry.
    5884             :  *
    5885             :  * @return TRUE if they are disjoint, otherwise FALSE.
    5886             :  */
    5887             : 
    5888           8 : OGRBoolean OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
    5889             : 
    5890             : {
    5891             :     (void)poOtherGeom;
    5892             : #ifndef HAVE_GEOS
    5893             : 
    5894             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5895             :     return FALSE;
    5896             : 
    5897             : #else
    5898           8 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
    5899             : #endif  // HAVE_GEOS
    5900             : }
    5901             : 
    5902             : /************************************************************************/
    5903             : /*                           OGR_G_Disjoint()                           */
    5904             : /************************************************************************/
    5905             : 
    5906             : /**
    5907             :  * \brief Test for disjointness.
    5908             :  *
    5909             :  * Tests if this geometry and the other geometry are disjoint.
    5910             :  *
    5911             :  * Geometry validity is not checked. In case you are unsure of the validity
    5912             :  * of the input geometries, call IsValid() before, otherwise the result might
    5913             :  * be wrong.
    5914             :  *
    5915             :  * This function is the same as the C++ method OGRGeometry::Disjoint().
    5916             :  *
    5917             :  * This function is built on the GEOS library, check it for the definition
    5918             :  * of the geometry operation.
    5919             :  * If OGR is built without the GEOS library, this function will always fail,
    5920             :  * issuing a CPLE_NotSupported error.
    5921             :  *
    5922             :  * @param hThis the geometry to compare.
    5923             :  * @param hOther the other geometry to compare.
    5924             :  *
    5925             :  * @return TRUE if they are disjoint, otherwise FALSE.
    5926             :  */
    5927           8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
    5928             : 
    5929             : {
    5930           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
    5931             : 
    5932          16 :     return OGRGeometry::FromHandle(hThis)->Disjoint(
    5933          16 :         OGRGeometry::FromHandle(hOther));
    5934             : }
    5935             : 
    5936             : /************************************************************************/
    5937             : /*                              Touches()                               */
    5938             : /************************************************************************/
    5939             : 
    5940             : /**
    5941             :  * \brief Test for touching.
    5942             :  *
    5943             :  * Tests if this geometry and the other passed into the method are touching.
    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 method is the same as the C function OGR_G_Touches().
    5950             :  *
    5951             :  * This method 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 method will always fail,
    5954             :  * issuing a CPLE_NotSupported error.
    5955             :  *
    5956             :  * @param poOtherGeom the geometry to compare to this geometry.
    5957             :  *
    5958             :  * @return TRUE if they are touching, otherwise FALSE.
    5959             :  */
    5960             : 
    5961          11 : OGRBoolean OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
    5962             : 
    5963             : {
    5964             :     (void)poOtherGeom;
    5965             : #ifndef HAVE_GEOS
    5966             : 
    5967             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5968             :     return FALSE;
    5969             : 
    5970             : #else
    5971          11 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
    5972             : #endif  // HAVE_GEOS
    5973             : }
    5974             : 
    5975             : /************************************************************************/
    5976             : /*                           OGR_G_Touches()                            */
    5977             : /************************************************************************/
    5978             : /**
    5979             :  * \brief Test for touching.
    5980             :  *
    5981             :  * Tests if this geometry and the other geometry are touching.
    5982             :  *
    5983             :  * Geometry validity is not checked. In case you are unsure of the validity
    5984             :  * of the input geometries, call IsValid() before, otherwise the result might
    5985             :  * be wrong.
    5986             :  *
    5987             :  * This function is the same as the C++ method OGRGeometry::Touches().
    5988             :  *
    5989             :  * This function is built on the GEOS library, check it for the definition
    5990             :  * of the geometry operation.
    5991             :  * If OGR is built without the GEOS library, this function will always fail,
    5992             :  * issuing a CPLE_NotSupported error.
    5993             :  *
    5994             :  * @param hThis the geometry to compare.
    5995             :  * @param hOther the other geometry to compare.
    5996             :  *
    5997             :  * @return TRUE if they are touching, otherwise FALSE.
    5998             :  */
    5999             : 
    6000           8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
    6001             : 
    6002             : {
    6003           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
    6004             : 
    6005          16 :     return OGRGeometry::FromHandle(hThis)->Touches(
    6006          16 :         OGRGeometry::FromHandle(hOther));
    6007             : }
    6008             : 
    6009             : /************************************************************************/
    6010             : /*                              Crosses()                               */
    6011             : /************************************************************************/
    6012             : 
    6013             : /**
    6014             :  * \brief Test for crossing.
    6015             :  *
    6016             :  * Tests if this geometry and the other passed into the method are crossing.
    6017             :  *
    6018             :  * Geometry validity is not checked. In case you are unsure of the validity
    6019             :  * of the input geometries, call IsValid() before, otherwise the result might
    6020             :  * be wrong.
    6021             :  *
    6022             :  * This method is the same as the C function OGR_G_Crosses().
    6023             :  *
    6024             :  * This method is built on the GEOS library, check it for the definition
    6025             :  * of the geometry operation.
    6026             :  * If OGR is built without the GEOS library, this method will always fail,
    6027             :  * issuing a CPLE_NotSupported error.
    6028             :  *
    6029             :  * @param poOtherGeom the geometry to compare to this geometry.
    6030             :  *
    6031             :  * @return TRUE if they are crossing, otherwise FALSE.
    6032             :  */
    6033             : 
    6034             : OGRBoolean
    6035           8 : OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    6036             : 
    6037             : {
    6038           8 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    6039             :     {
    6040             : #ifndef HAVE_SFCGAL
    6041             : 
    6042           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    6043           0 :         return FALSE;
    6044             : 
    6045             : #else
    6046             : 
    6047             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    6048             :         if (poThis == nullptr)
    6049             :             return FALSE;
    6050             : 
    6051             :         sfcgal_geometry_t *poOther =
    6052             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    6053             :         if (poOther == nullptr)
    6054             :         {
    6055             :             sfcgal_geometry_delete(poThis);
    6056             :             return FALSE;
    6057             :         }
    6058             : 
    6059             :         int res = sfcgal_geometry_intersects_3d(poThis, poOther);
    6060             : 
    6061             :         sfcgal_geometry_delete(poThis);
    6062             :         sfcgal_geometry_delete(poOther);
    6063             : 
    6064             :         return (res == 1) ? TRUE : FALSE;
    6065             : 
    6066             : #endif
    6067             :     }
    6068             : 
    6069             :     else
    6070             :     {
    6071             : 
    6072             : #ifndef HAVE_GEOS
    6073             : 
    6074             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6075             :         return FALSE;
    6076             : 
    6077             : #else
    6078           8 :         return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
    6079             : #endif /* HAVE_GEOS */
    6080             :     }
    6081             : }
    6082             : 
    6083             : /************************************************************************/
    6084             : /*                           OGR_G_Crosses()                            */
    6085             : /************************************************************************/
    6086             : /**
    6087             :  * \brief Test for crossing.
    6088             :  *
    6089             :  * Tests if this geometry and the other geometry are crossing.
    6090             :  *
    6091             :  * Geometry validity is not checked. In case you are unsure of the validity
    6092             :  * of the input geometries, call IsValid() before, otherwise the result might
    6093             :  * be wrong.
    6094             :  *
    6095             :  * This function is the same as the C++ method OGRGeometry::Crosses().
    6096             :  *
    6097             :  * This function is built on the GEOS library, check it for the definition
    6098             :  * of the geometry operation.
    6099             :  * If OGR is built without the GEOS library, this function will always fail,
    6100             :  * issuing a CPLE_NotSupported error.
    6101             :  *
    6102             :  * @param hThis the geometry to compare.
    6103             :  * @param hOther the other geometry to compare.
    6104             :  *
    6105             :  * @return TRUE if they are crossing, otherwise FALSE.
    6106             :  */
    6107             : 
    6108           8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
    6109             : 
    6110             : {
    6111           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
    6112             : 
    6113          16 :     return OGRGeometry::FromHandle(hThis)->Crosses(
    6114          16 :         OGRGeometry::FromHandle(hOther));
    6115             : }
    6116             : 
    6117             : /************************************************************************/
    6118             : /*                               Within()                               */
    6119             : /************************************************************************/
    6120             : 
    6121             : /**
    6122             :  * \brief Test for containment.
    6123             :  *
    6124             :  * Tests if actual geometry object is within the passed geometry.
    6125             :  *
    6126             :  * Geometry validity is not checked. In case you are unsure of the validity
    6127             :  * of the input geometries, call IsValid() before, otherwise the result might
    6128             :  * be wrong.
    6129             :  *
    6130             :  * This method is the same as the C function OGR_G_Within().
    6131             :  *
    6132             :  * This method is built on the GEOS library, check it for the definition
    6133             :  * of the geometry operation.
    6134             :  * If OGR is built without the GEOS library, this method will always fail,
    6135             :  * issuing a CPLE_NotSupported error.
    6136             :  *
    6137             :  * @param poOtherGeom the geometry to compare to this geometry.
    6138             :  *
    6139             :  * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
    6140             :  */
    6141             : 
    6142       22377 : OGRBoolean OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
    6143             : 
    6144             : {
    6145             :     (void)poOtherGeom;
    6146             : #ifndef HAVE_GEOS
    6147             : 
    6148             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6149             :     return FALSE;
    6150             : 
    6151             : #else
    6152       22377 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
    6153             : #endif  // HAVE_GEOS
    6154             : }
    6155             : 
    6156             : /************************************************************************/
    6157             : /*                            OGR_G_Within()                            */
    6158             : /************************************************************************/
    6159             : 
    6160             : /**
    6161             :  * \brief Test for containment.
    6162             :  *
    6163             :  * Tests if this geometry is within the other geometry.
    6164             :  *
    6165             :  * Geometry validity is not checked. In case you are unsure of the validity
    6166             :  * of the input geometries, call IsValid() before, otherwise the result might
    6167             :  * be wrong.
    6168             :  *
    6169             :  * This function is the same as the C++ method OGRGeometry::Within().
    6170             :  *
    6171             :  * This function is built on the GEOS library, check it for the definition
    6172             :  * of the geometry operation.
    6173             :  * If OGR is built without the GEOS library, this function will always fail,
    6174             :  * issuing a CPLE_NotSupported error.
    6175             :  *
    6176             :  * @param hThis the geometry to compare.
    6177             :  * @param hOther the other geometry to compare.
    6178             :  *
    6179             :  * @return TRUE if hThis is within hOther, otherwise FALSE.
    6180             :  */
    6181        7334 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
    6182             : 
    6183             : {
    6184        7334 :     VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
    6185             : 
    6186       14668 :     return OGRGeometry::FromHandle(hThis)->Within(
    6187        7334 :         OGRGeometry::FromHandle(hOther));
    6188             : }
    6189             : 
    6190             : /************************************************************************/
    6191             : /*                              Contains()                              */
    6192             : /************************************************************************/
    6193             : 
    6194             : /**
    6195             :  * \brief Test for containment.
    6196             :  *
    6197             :  * Tests if actual geometry object contains the passed geometry.
    6198             :  *
    6199             :  * Geometry validity is not checked. In case you are unsure of the validity
    6200             :  * of the input geometries, call IsValid() before, otherwise the result might
    6201             :  * be wrong.
    6202             :  *
    6203             :  * This method is the same as the C function OGR_G_Contains().
    6204             :  *
    6205             :  * This method is built on the GEOS library, check it for the definition
    6206             :  * of the geometry operation.
    6207             :  * If OGR is built without the GEOS library, this method will always fail,
    6208             :  * issuing a CPLE_NotSupported error.
    6209             :  *
    6210             :  * @param poOtherGeom the geometry to compare to this geometry.
    6211             :  *
    6212             :  * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
    6213             :  */
    6214             : 
    6215         311 : OGRBoolean OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
    6216             : 
    6217             : {
    6218             :     (void)poOtherGeom;
    6219             : #ifndef HAVE_GEOS
    6220             : 
    6221             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6222             :     return FALSE;
    6223             : 
    6224             : #else
    6225         311 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
    6226             : #endif  // HAVE_GEOS
    6227             : }
    6228             : 
    6229             : /************************************************************************/
    6230             : /*                           OGR_G_Contains()                           */
    6231             : /************************************************************************/
    6232             : 
    6233             : /**
    6234             :  * \brief Test for containment.
    6235             :  *
    6236             :  * Tests if this geometry contains the other geometry.
    6237             :  *
    6238             :  * Geometry validity is not checked. In case you are unsure of the validity
    6239             :  * of the input geometries, call IsValid() before, otherwise the result might
    6240             :  * be wrong.
    6241             :  *
    6242             :  * This function is the same as the C++ method OGRGeometry::Contains().
    6243             :  *
    6244             :  * This function is built on the GEOS library, check it for the definition
    6245             :  * of the geometry operation.
    6246             :  * If OGR is built without the GEOS library, this function will always fail,
    6247             :  * issuing a CPLE_NotSupported error.
    6248             :  *
    6249             :  * @param hThis the geometry to compare.
    6250             :  * @param hOther the other geometry to compare.
    6251             :  *
    6252             :  * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
    6253             :  */
    6254          10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
    6255             : 
    6256             : {
    6257          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
    6258             : 
    6259          20 :     return OGRGeometry::FromHandle(hThis)->Contains(
    6260          10 :         OGRGeometry::FromHandle(hOther));
    6261             : }
    6262             : 
    6263             : /************************************************************************/
    6264             : /*                              Overlaps()                              */
    6265             : /************************************************************************/
    6266             : 
    6267             : /**
    6268             :  * \brief Test for overlap.
    6269             :  *
    6270             :  * Tests if this geometry and the other passed into the method overlap, that is
    6271             :  * their intersection has a non-zero area.
    6272             :  *
    6273             :  * Geometry validity is not checked. In case you are unsure of the validity
    6274             :  * of the input geometries, call IsValid() before, otherwise the result might
    6275             :  * be wrong.
    6276             :  *
    6277             :  * This method is the same as the C function OGR_G_Overlaps().
    6278             :  *
    6279             :  * This method is built on the GEOS library, check it for the definition
    6280             :  * of the geometry operation.
    6281             :  * If OGR is built without the GEOS library, this method will always fail,
    6282             :  * issuing a CPLE_NotSupported error.
    6283             :  *
    6284             :  * @param poOtherGeom the geometry to compare to this geometry.
    6285             :  *
    6286             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6287             :  */
    6288             : 
    6289           7 : OGRBoolean OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
    6290             : 
    6291             : {
    6292             :     (void)poOtherGeom;
    6293             : #ifndef HAVE_GEOS
    6294             : 
    6295             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6296             :     return FALSE;
    6297             : 
    6298             : #else
    6299           7 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
    6300             : #endif  // HAVE_GEOS
    6301             : }
    6302             : 
    6303             : /************************************************************************/
    6304             : /*                           OGR_G_Overlaps()                           */
    6305             : /************************************************************************/
    6306             : /**
    6307             :  * \brief Test for overlap.
    6308             :  *
    6309             :  * Tests if this geometry and the other geometry overlap, that is their
    6310             :  * intersection has a non-zero area.
    6311             :  *
    6312             :  * Geometry validity is not checked. In case you are unsure of the validity
    6313             :  * of the input geometries, call IsValid() before, otherwise the result might
    6314             :  * be wrong.
    6315             :  *
    6316             :  * This function is the same as the C++ method OGRGeometry::Overlaps().
    6317             :  *
    6318             :  * This function is built on the GEOS library, check it for the definition
    6319             :  * of the geometry operation.
    6320             :  * If OGR is built without the GEOS library, this function will always fail,
    6321             :  * issuing a CPLE_NotSupported error.
    6322             :  *
    6323             :  * @param hThis the geometry to compare.
    6324             :  * @param hOther the other geometry to compare.
    6325             :  *
    6326             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6327             :  */
    6328             : 
    6329           7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
    6330             : 
    6331             : {
    6332           7 :     VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
    6333             : 
    6334          14 :     return OGRGeometry::FromHandle(hThis)->Overlaps(
    6335          14 :         OGRGeometry::FromHandle(hOther));
    6336             : }
    6337             : 
    6338             : /************************************************************************/
    6339             : /*                             closeRings()                             */
    6340             : /************************************************************************/
    6341             : 
    6342             : /**
    6343             :  * \brief Force rings to be closed.
    6344             :  *
    6345             :  * If this geometry, or any contained geometries has polygon rings that
    6346             :  * are not closed, they will be closed by adding the starting point at
    6347             :  * the end.
    6348             :  */
    6349             : 
    6350        1264 : void OGRGeometry::closeRings()
    6351             : {
    6352        1264 : }
    6353             : 
    6354             : /************************************************************************/
    6355             : /*                          OGR_G_CloseRings()                          */
    6356             : /************************************************************************/
    6357             : 
    6358             : /**
    6359             :  * \brief Force rings to be closed.
    6360             :  *
    6361             :  * If this geometry, or any contained geometries has polygon rings that
    6362             :  * are not closed, they will be closed by adding the starting point at
    6363             :  * the end.
    6364             :  *
    6365             :  * @param hGeom handle to the geometry.
    6366             :  */
    6367             : 
    6368           6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
    6369             : 
    6370             : {
    6371           6 :     VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
    6372             : 
    6373           6 :     OGRGeometry::FromHandle(hGeom)->closeRings();
    6374             : }
    6375             : 
    6376             : /************************************************************************/
    6377             : /*                              Centroid()                              */
    6378             : /************************************************************************/
    6379             : 
    6380             : /**
    6381             :  * \brief Compute the geometry centroid.
    6382             :  *
    6383             :  * The centroid location is applied to the passed in OGRPoint object.
    6384             :  * The centroid is not necessarily within the geometry.
    6385             :  *
    6386             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6387             :  * however the current implementation based on GEOS can operate on other
    6388             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6389             :  * multipolygons.
    6390             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6391             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6392             :  * (multipolygons).
    6393             :  *
    6394             :  * This function is the same as the C function OGR_G_Centroid().
    6395             :  *
    6396             :  * This function is built on the GEOS library, check it for the definition
    6397             :  * of the geometry operation.
    6398             :  * If OGR is built without the GEOS library, this function will always fail,
    6399             :  * issuing a CPLE_NotSupported error.
    6400             :  *
    6401             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6402             :  *
    6403             :  * to OGRPolygon)
    6404             :  */
    6405             : 
    6406           5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
    6407             : 
    6408             : {
    6409           5 :     if (poPoint == nullptr)
    6410           0 :         return OGRERR_FAILURE;
    6411             : 
    6412             : #ifndef HAVE_GEOS
    6413             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6414             :     return OGRERR_FAILURE;
    6415             : 
    6416             : #else
    6417             : 
    6418           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6419             :     GEOSGeom hThisGeosGeom =
    6420           5 :         exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
    6421             : 
    6422           5 :     if (hThisGeosGeom != nullptr)
    6423             :     {
    6424           5 :         GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
    6425           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6426             : 
    6427           5 :         if (hOtherGeosGeom == nullptr)
    6428             :         {
    6429           0 :             freeGEOSContext(hGEOSCtxt);
    6430           0 :             return OGRERR_FAILURE;
    6431             :         }
    6432             : 
    6433             :         OGRGeometry *poCentroidGeom =
    6434           5 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6435             : 
    6436           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6437             : 
    6438           5 :         if (poCentroidGeom == nullptr)
    6439             :         {
    6440           0 :             freeGEOSContext(hGEOSCtxt);
    6441           0 :             return OGRERR_FAILURE;
    6442             :         }
    6443           5 :         if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6444             :         {
    6445           0 :             delete poCentroidGeom;
    6446           0 :             freeGEOSContext(hGEOSCtxt);
    6447           0 :             return OGRERR_FAILURE;
    6448             :         }
    6449             : 
    6450           5 :         if (getSpatialReference() != nullptr)
    6451           0 :             poCentroidGeom->assignSpatialReference(getSpatialReference());
    6452             : 
    6453           5 :         OGRPoint *poCentroid = poCentroidGeom->toPoint();
    6454             : 
    6455           5 :         if (!poCentroid->IsEmpty())
    6456             :         {
    6457           4 :             poPoint->setX(poCentroid->getX());
    6458           4 :             poPoint->setY(poCentroid->getY());
    6459             :         }
    6460             :         else
    6461             :         {
    6462           1 :             poPoint->empty();
    6463             :         }
    6464             : 
    6465           5 :         delete poCentroidGeom;
    6466             : 
    6467           5 :         freeGEOSContext(hGEOSCtxt);
    6468           5 :         return OGRERR_NONE;
    6469             :     }
    6470             :     else
    6471             :     {
    6472           0 :         freeGEOSContext(hGEOSCtxt);
    6473           0 :         return OGRERR_FAILURE;
    6474             :     }
    6475             : 
    6476             : #endif  // HAVE_GEOS
    6477             : }
    6478             : 
    6479             : /************************************************************************/
    6480             : /*                           OGR_G_Centroid()                           */
    6481             : /************************************************************************/
    6482             : 
    6483             : /**
    6484             :  * \brief Compute the geometry centroid.
    6485             :  *
    6486             :  * The centroid location is applied to the passed in OGRPoint object.
    6487             :  * The centroid is not necessarily within the geometry.
    6488             :  *
    6489             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6490             :  * however the current implementation based on GEOS can operate on other
    6491             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6492             :  * multipolygons.
    6493             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6494             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6495             :  * (multipolygons).
    6496             :  *
    6497             :  * This function is the same as the C++ method OGRGeometry::Centroid().
    6498             :  *
    6499             :  * This function is built on the GEOS library, check it for the definition
    6500             :  * of the geometry operation.
    6501             :  * If OGR is built without the GEOS library, this function will always fail,
    6502             :  * issuing a CPLE_NotSupported error.
    6503             :  *
    6504             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6505             :  */
    6506             : 
    6507           5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
    6508             : 
    6509             : {
    6510           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
    6511             : 
    6512           5 :     OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
    6513           5 :     if (poCentroidGeom == nullptr)
    6514           0 :         return OGRERR_FAILURE;
    6515           5 :     if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6516             :     {
    6517           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6518             :                  "Passed wrong geometry type as centroid argument.");
    6519           0 :         return OGRERR_FAILURE;
    6520             :     }
    6521             : 
    6522           5 :     return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
    6523             : }
    6524             : 
    6525             : /************************************************************************/
    6526             : /*                        OGR_G_PointOnSurface()                        */
    6527             : /************************************************************************/
    6528             : 
    6529             : /**
    6530             :  * \brief Returns a point guaranteed to lie on the surface.
    6531             :  *
    6532             :  * This method relates to the SFCOM ISurface::get_PointOnSurface() method
    6533             :  * however the current implementation based on GEOS can operate on other
    6534             :  * geometry types than the types that are supported by SQL/MM-Part 3 :
    6535             :  * surfaces (polygons) and multisurfaces (multipolygons).
    6536             :  *
    6537             :  * This method is built on the GEOS library, check it for the definition
    6538             :  * of the geometry operation.
    6539             :  * If OGR is built without the GEOS library, this method will always fail,
    6540             :  * issuing a CPLE_NotSupported error.
    6541             :  *
    6542             :  * @param hGeom the geometry to operate on.
    6543             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6544             :  * or NULL if an error occurs.
    6545             :  *
    6546             :  */
    6547             : 
    6548           4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
    6549             : 
    6550             : {
    6551           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
    6552             : 
    6553             : #ifndef HAVE_GEOS
    6554             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6555             :     return nullptr;
    6556             : #else
    6557             : 
    6558           4 :     OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
    6559             : 
    6560           4 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    6561           4 :     GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
    6562             : 
    6563           4 :     if (hThisGeosGeom != nullptr)
    6564             :     {
    6565             :         GEOSGeom hOtherGeosGeom =
    6566           4 :             GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
    6567           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6568             : 
    6569           4 :         if (hOtherGeosGeom == nullptr)
    6570             :         {
    6571           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6572           0 :             return nullptr;
    6573             :         }
    6574             : 
    6575             :         OGRGeometry *poInsidePointGeom =
    6576           4 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6577             : 
    6578           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6579             : 
    6580           4 :         if (poInsidePointGeom == nullptr)
    6581             :         {
    6582           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6583           0 :             return nullptr;
    6584             :         }
    6585           4 :         if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
    6586             :         {
    6587           0 :             delete poInsidePointGeom;
    6588           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6589           0 :             return nullptr;
    6590             :         }
    6591             : 
    6592           4 :         if (poThis->getSpatialReference() != nullptr)
    6593           0 :             poInsidePointGeom->assignSpatialReference(
    6594           0 :                 poThis->getSpatialReference());
    6595             : 
    6596           4 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6597           4 :         return OGRGeometry::ToHandle(poInsidePointGeom);
    6598             :     }
    6599             : 
    6600           0 :     OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6601           0 :     return nullptr;
    6602             : #endif
    6603             : }
    6604             : 
    6605             : /************************************************************************/
    6606             : /*                       PointOnSurfaceInternal()                       */
    6607             : /************************************************************************/
    6608             : 
    6609             : //! @cond Doxygen_Suppress
    6610           0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
    6611             : {
    6612           0 :     if (poPoint == nullptr || poPoint->IsEmpty())
    6613           0 :         return OGRERR_FAILURE;
    6614             : 
    6615           0 :     OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
    6616             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
    6617           0 :     if (hInsidePoint == nullptr)
    6618           0 :         return OGRERR_FAILURE;
    6619             : 
    6620           0 :     OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
    6621           0 :     if (poInsidePoint->IsEmpty())
    6622             :     {
    6623           0 :         poPoint->empty();
    6624             :     }
    6625             :     else
    6626             :     {
    6627           0 :         poPoint->setX(poInsidePoint->getX());
    6628           0 :         poPoint->setY(poInsidePoint->getY());
    6629             :     }
    6630             : 
    6631           0 :     OGR_G_DestroyGeometry(hInsidePoint);
    6632             : 
    6633           0 :     return OGRERR_NONE;
    6634             : }
    6635             : 
    6636             : //! @endcond
    6637             : 
    6638             : /************************************************************************/
    6639             : /*                              Simplify()                              */
    6640             : /************************************************************************/
    6641             : 
    6642             : /**
    6643             :  * \brief Simplify the geometry.
    6644             :  *
    6645             :  * This function is the same as the C function OGR_G_Simplify().
    6646             :  *
    6647             :  * This function is built on the GEOS library, check it for the definition
    6648             :  * of the geometry operation.
    6649             :  * If OGR is built without the GEOS library, this function will always fail,
    6650             :  * issuing a CPLE_NotSupported error.
    6651             :  *
    6652             :  * @param dTolerance the distance tolerance for the simplification.
    6653             :  *
    6654             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6655             :  *
    6656             :  */
    6657             : 
    6658          55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
    6659             : 
    6660             : {
    6661             :     (void)dTolerance;
    6662             : #ifndef HAVE_GEOS
    6663             : 
    6664             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6665             :     return nullptr;
    6666             : 
    6667             : #else
    6668          55 :     OGRGeometry *poOGRProduct = nullptr;
    6669             : 
    6670          55 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6671          55 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6672          55 :     if (hThisGeosGeom != nullptr)
    6673             :     {
    6674             :         GEOSGeom hGeosProduct =
    6675          55 :             GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
    6676          55 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6677             :         poOGRProduct =
    6678          55 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6679             :     }
    6680          55 :     freeGEOSContext(hGEOSCtxt);
    6681          55 :     return poOGRProduct;
    6682             : 
    6683             : #endif  // HAVE_GEOS
    6684             : }
    6685             : 
    6686             : /************************************************************************/
    6687             : /*                           OGR_G_Simplify()                           */
    6688             : /************************************************************************/
    6689             : 
    6690             : /**
    6691             :  * \brief Compute a simplified geometry.
    6692             :  *
    6693             :  * This function is the same as the C++ method OGRGeometry::Simplify().
    6694             :  *
    6695             :  * This function is built on the GEOS library, check it for the definition
    6696             :  * of the geometry operation.
    6697             :  * If OGR is built without the GEOS library, this function will always fail,
    6698             :  * issuing a CPLE_NotSupported error.
    6699             :  *
    6700             :  * @param hThis the geometry.
    6701             :  * @param dTolerance the distance tolerance for the simplification.
    6702             :  *
    6703             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6704             :  * or NULL if an error occurs.
    6705             :  *
    6706             :  */
    6707             : 
    6708           1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
    6709             : 
    6710             : {
    6711           1 :     VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
    6712           1 :     return OGRGeometry::ToHandle(
    6713           1 :         OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
    6714             : }
    6715             : 
    6716             : /************************************************************************/
    6717             : /*                      SimplifyPreserveTopology()                      */
    6718             : /************************************************************************/
    6719             : 
    6720             : /**
    6721             :  * \brief Simplify the geometry while preserving topology.
    6722             :  *
    6723             :  * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
    6724             :  *
    6725             :  * This function is built on the GEOS library, check it for the definition
    6726             :  * of the geometry operation.
    6727             :  * If OGR is built without the GEOS library, this function will always fail,
    6728             :  * issuing a CPLE_NotSupported error.
    6729             :  *
    6730             :  * @param dTolerance the distance tolerance for the simplification.
    6731             :  *
    6732             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6733             :  *
    6734             :  */
    6735             : 
    6736          17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
    6737             : 
    6738             : {
    6739             :     (void)dTolerance;
    6740             : #ifndef HAVE_GEOS
    6741             : 
    6742             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6743             :     return nullptr;
    6744             : 
    6745             : #else
    6746          17 :     OGRGeometry *poOGRProduct = nullptr;
    6747             : 
    6748          17 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6749          17 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6750          17 :     if (hThisGeosGeom != nullptr)
    6751             :     {
    6752          17 :         GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
    6753             :             hGEOSCtxt, hThisGeosGeom, dTolerance);
    6754          17 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6755             :         poOGRProduct =
    6756          17 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6757             :     }
    6758          17 :     freeGEOSContext(hGEOSCtxt);
    6759          17 :     return poOGRProduct;
    6760             : 
    6761             : #endif  // HAVE_GEOS
    6762             : }
    6763             : 
    6764             : /************************************************************************/
    6765             : /*                   OGR_G_SimplifyPreserveTopology()                   */
    6766             : /************************************************************************/
    6767             : 
    6768             : /**
    6769             :  * \brief Simplify the geometry while preserving topology.
    6770             :  *
    6771             :  * This function is the same as the C++ method
    6772             :  * OGRGeometry::SimplifyPreserveTopology().
    6773             :  *
    6774             :  * This function is built on the GEOS library, check it for the definition
    6775             :  * of the geometry operation.
    6776             :  * If OGR is built without the GEOS library, this function will always fail,
    6777             :  * issuing a CPLE_NotSupported error.
    6778             :  *
    6779             :  * @param hThis the geometry.
    6780             :  * @param dTolerance the distance tolerance for the simplification.
    6781             :  *
    6782             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6783             :  * or NULL if an error occurs.
    6784             :  *
    6785             :  */
    6786             : 
    6787           1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
    6788             :                                             double dTolerance)
    6789             : 
    6790             : {
    6791           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
    6792           1 :     return OGRGeometry::ToHandle(
    6793           1 :         OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
    6794             : }
    6795             : 
    6796             : /************************************************************************/
    6797             : /*                          roundCoordinates()                          */
    6798             : /************************************************************************/
    6799             : 
    6800             : /** Round coordinates of the geometry to the specified precision.
    6801             :  *
    6802             :  * Note that this is not the same as OGRGeometry::SetPrecision(). The later
    6803             :  * will return valid geometries, whereas roundCoordinates() does not make
    6804             :  * such guarantee and may return geometries with invalidities, if they are
    6805             :  * not compatible of the specified precision. roundCoordinates() supports
    6806             :  * curve geometries, whereas SetPrecision() does not currently.
    6807             :  *
    6808             :  * One use case for roundCoordinates() is to undo the effect of
    6809             :  * quantizeCoordinates().
    6810             :  *
    6811             :  * @param sPrecision Contains the precision requirements.
    6812             :  * @since GDAL 3.9
    6813             :  */
    6814          40 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
    6815             : {
    6816             :     struct Rounder : public OGRDefaultGeometryVisitor
    6817             :     {
    6818             :         const OGRGeomCoordinatePrecision &m_precision;
    6819             :         const double m_invXYResolution;
    6820             :         const double m_invZResolution;
    6821             :         const double m_invMResolution;
    6822             : 
    6823          40 :         explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
    6824          40 :             : m_precision(sPrecisionIn),
    6825          40 :               m_invXYResolution(m_precision.dfXYResolution !=
    6826             :                                         OGRGeomCoordinatePrecision::UNKNOWN
    6827          40 :                                     ? 1.0 / m_precision.dfXYResolution
    6828             :                                     : 0.0),
    6829          40 :               m_invZResolution(m_precision.dfZResolution !=
    6830             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6831          40 :                                    ? 1.0 / m_precision.dfZResolution
    6832             :                                    : 0.0),
    6833          40 :               m_invMResolution(m_precision.dfMResolution !=
    6834             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6835          40 :                                    ? 1.0 / m_precision.dfMResolution
    6836         120 :                                    : 0.0)
    6837             :         {
    6838          40 :         }
    6839             : 
    6840             :         using OGRDefaultGeometryVisitor::visit;
    6841             : 
    6842         386 :         void visit(OGRPoint *poPoint) override
    6843             :         {
    6844         386 :             if (m_precision.dfXYResolution !=
    6845             :                 OGRGeomCoordinatePrecision::UNKNOWN)
    6846             :             {
    6847         386 :                 poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
    6848         386 :                               m_precision.dfXYResolution);
    6849         386 :                 poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
    6850         386 :                               m_precision.dfXYResolution);
    6851             :             }
    6852         772 :             if (m_precision.dfZResolution !=
    6853         390 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6854           4 :                 poPoint->Is3D())
    6855             :             {
    6856           4 :                 poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
    6857           4 :                               m_precision.dfZResolution);
    6858             :             }
    6859         772 :             if (m_precision.dfMResolution !=
    6860         390 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6861           4 :                 poPoint->IsMeasured())
    6862             :             {
    6863           4 :                 poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
    6864           4 :                               m_precision.dfMResolution);
    6865             :             }
    6866         386 :         }
    6867             :     };
    6868             : 
    6869          80 :     Rounder rounder(sPrecision);
    6870          40 :     accept(&rounder);
    6871          40 : }
    6872             : 
    6873             : /************************************************************************/
    6874             : /*                            SetPrecision()                            */
    6875             : /************************************************************************/
    6876             : 
    6877             : /** Set the geometry's precision, rounding all its coordinates to the precision
    6878             :  * grid, and making sure the geometry is still valid.
    6879             :  *
    6880             :  * This is a stronger version of roundCoordinates().
    6881             :  *
    6882             :  * Note that at time of writing GEOS does no supported curve geometries. So
    6883             :  * currently if this function is called on such a geometry, OGR will first call
    6884             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    6885             :  * that it is unlikely to yield to the expected result.
    6886             :  *
    6887             :  * This function is the same as the C function OGR_G_SetPrecision().
    6888             :  *
    6889             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    6890             :  * GEOS library. Check it for the definition of the geometry operation.
    6891             :  * If OGR is built without the GEOS library, this function will always fail,
    6892             :  * issuing a CPLE_NotSupported error.
    6893             :  *
    6894             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    6895             :  *                 precision.
    6896             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    6897             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    6898             :  *
    6899             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6900             :  *
    6901             :  * @since GDAL 3.9
    6902             :  */
    6903             : 
    6904           6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
    6905             : {
    6906             :     (void)dfGridSize;
    6907             :     (void)nFlags;
    6908             : #ifndef HAVE_GEOS
    6909             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6910             :     return nullptr;
    6911             : 
    6912             : #else
    6913           6 :     OGRGeometry *poOGRProduct = nullptr;
    6914             : 
    6915           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6916           6 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6917           6 :     if (hThisGeosGeom != nullptr)
    6918             :     {
    6919           6 :         GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
    6920             :             hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
    6921           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6922             :         poOGRProduct =
    6923           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6924             :     }
    6925           6 :     freeGEOSContext(hGEOSCtxt);
    6926           6 :     return poOGRProduct;
    6927             : 
    6928             : #endif  // HAVE_GEOS
    6929             : }
    6930             : 
    6931             : /************************************************************************/
    6932             : /*                         OGR_G_SetPrecision()                         */
    6933             : /************************************************************************/
    6934             : 
    6935             : /** Set the geometry's precision, rounding all its coordinates to the precision
    6936             :  * grid, and making sure the geometry is still valid.
    6937             :  *
    6938             :  * This is a stronger version of roundCoordinates().
    6939             :  *
    6940             :  * Note that at time of writing GEOS does no supported curve geometries. So
    6941             :  * currently if this function is called on such a geometry, OGR will first call
    6942             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    6943             :  * that it is unlikely to yield to the expected result.
    6944             :  *
    6945             :  * This function is the same as the C++ method OGRGeometry::SetPrecision().
    6946             :  *
    6947             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    6948             :  * GEOS library. Check it for the definition of the geometry operation.
    6949             :  * If OGR is built without the GEOS library, this function will always fail,
    6950             :  * issuing a CPLE_NotSupported error.
    6951             :  *
    6952             :  * @param hThis the geometry.
    6953             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    6954             :  *                 precision.
    6955             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    6956             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    6957             :  *
    6958             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6959             :  * or NULL if an error occurs.
    6960             :  *
    6961             :  * @since GDAL 3.9
    6962             :  */
    6963           1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
    6964             :                                 int nFlags)
    6965             : {
    6966           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
    6967           1 :     return OGRGeometry::ToHandle(
    6968           1 :         OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
    6969             : }
    6970             : 
    6971             : /************************************************************************/
    6972             : /*                       DelaunayTriangulation()                        */
    6973             : /************************************************************************/
    6974             : 
    6975             : /**
    6976             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    6977             :  *
    6978             :  * This function is the same as the C function OGR_G_DelaunayTriangulation().
    6979             :  *
    6980             :  * This function is built on the GEOS library, v3.4 or above.
    6981             :  * If OGR is built without the GEOS library, this function will always fail,
    6982             :  * issuing a CPLE_NotSupported error.
    6983             :  *
    6984             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    6985             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    6986             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    6987             :  *
    6988             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6989             :  */
    6990             : 
    6991             : #ifndef HAVE_GEOS
    6992             : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
    6993             :                                                 int /*bOnlyEdges*/) const
    6994             : {
    6995             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6996             :     return nullptr;
    6997             : }
    6998             : #else
    6999           1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
    7000             :                                                 int bOnlyEdges) const
    7001             : {
    7002           1 :     OGRGeometry *poOGRProduct = nullptr;
    7003             : 
    7004           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7005           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7006           1 :     if (hThisGeosGeom != nullptr)
    7007             :     {
    7008           1 :         GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
    7009             :             hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
    7010           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7011             :         poOGRProduct =
    7012           1 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7013             :     }
    7014           1 :     freeGEOSContext(hGEOSCtxt);
    7015           1 :     return poOGRProduct;
    7016             : }
    7017             : #endif
    7018             : 
    7019             : /************************************************************************/
    7020             : /*                    OGR_G_DelaunayTriangulation()                     */
    7021             : /************************************************************************/
    7022             : 
    7023             : /**
    7024             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    7025             :  *
    7026             :  * This function is the same as the C++ method
    7027             :  * OGRGeometry::DelaunayTriangulation().
    7028             :  *
    7029             :  * This function is built on the GEOS library, v3.4 or above.
    7030             :  * If OGR is built without the GEOS library, this function will always fail,
    7031             :  * issuing a CPLE_NotSupported error.
    7032             :  *
    7033             :  * @param hThis the geometry.
    7034             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    7035             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    7036             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    7037             :  *
    7038             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7039             :  * or NULL if an error occurs.
    7040             :  */
    7041             : 
    7042           1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
    7043             :                                          int bOnlyEdges)
    7044             : 
    7045             : {
    7046           1 :     VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
    7047             : 
    7048           1 :     return OGRGeometry::ToHandle(
    7049             :         OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
    7050           1 :                                                               bOnlyEdges));
    7051             : }
    7052             : 
    7053             : /************************************************************************/
    7054             : /*                  ConstrainedDelaunayTriangulation()                  */
    7055             : /************************************************************************/
    7056             : 
    7057             : /**
    7058             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7059             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7060             :  * geometry collection.
    7061             :  *
    7062             :  * This function is the same as the C function
    7063             :  * OGR_G_ConstrainedDelaunayTriangulation().
    7064             :  *
    7065             :  * This function is built on the GEOS library, v3.10 or above.
    7066             :  * If OGR is built without the GEOS library, this function will always fail,
    7067             :  * issuing a CPLE_NotSupported error.
    7068             :  *
    7069             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7070             :  *
    7071             :  * @since OGR 3.12
    7072             :  */
    7073             : 
    7074           3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
    7075             : {
    7076             : #ifndef HAVE_GEOS
    7077             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7078             :     return nullptr;
    7079             : #elif !(GEOS_VERSION_MAJOR > 3 ||                                              \
    7080             :         (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    7081             :     CPLError(
    7082             :         CE_Failure, CPLE_NotSupported,
    7083             :         "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
    7084             :     return nullptr;
    7085             : #else
    7086             : 
    7087           3 :     OGRGeometry *poOGRProduct = nullptr;
    7088             : 
    7089           3 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7090           3 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7091           3 :     if (hThisGeosGeom != nullptr)
    7092             :     {
    7093             :         GEOSGeom hGeosProduct =
    7094           3 :             GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
    7095           3 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7096             :         poOGRProduct =
    7097           3 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7098             :     }
    7099           3 :     freeGEOSContext(hGEOSCtxt);
    7100           3 :     return poOGRProduct;
    7101             : #endif
    7102             : }
    7103             : 
    7104             : /************************************************************************/
    7105             : /*               OGR_G_ConstrainedDelaunayTriangulation()               */
    7106             : /************************************************************************/
    7107             : 
    7108             : /**
    7109             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7110             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7111             :  * geometry collection.
    7112             :  *
    7113             :  * This function is the same as the C++ method
    7114             :  * OGRGeometry::ConstrainedDelaunayTriangulation().
    7115             :  *
    7116             :  * This function is built on the GEOS library, v3.10 or above.
    7117             :  * If OGR is built without the GEOS library, this function will always fail,
    7118             :  * issuing a CPLE_NotSupported error.
    7119             :  *
    7120             :  * @param hThis the geometry.
    7121             :  *
    7122             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7123             :  * or NULL if an error occurs.
    7124             :  *
    7125             :  * @since OGR 3.12
    7126             :  */
    7127             : 
    7128           3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
    7129             : {
    7130           3 :     VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
    7131             : 
    7132           3 :     return OGRGeometry::ToHandle(
    7133           3 :         OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
    7134             : }
    7135             : 
    7136             : /************************************************************************/
    7137             : /*                             Polygonize()                             */
    7138             : /************************************************************************/
    7139             : /* Contributor: Alessandro Furieri, a.furieri@lqt.it                    */
    7140             : /* Developed for Faunalia (http://www.faunalia.it) with funding from    */
    7141             : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED        */
    7142             : /*                   AMBIENTALE                                         */
    7143             : /************************************************************************/
    7144             : 
    7145             : /**
    7146             :  * \brief Polygonizes a set of sparse edges.
    7147             :  *
    7148             :  * A new geometry object is created and returned containing a collection
    7149             :  * of reassembled Polygons: NULL will be returned if the input collection
    7150             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7151             :  * into Polygons is impossible due to topological inconsistencies.
    7152             :  *
    7153             :  * This method is the same as the C function OGR_G_Polygonize().
    7154             :  *
    7155             :  * This method is built on the GEOS library, check it for the definition
    7156             :  * of the geometry operation.
    7157             :  * If OGR is built without the GEOS library, this method will always fail,
    7158             :  * issuing a CPLE_NotSupported error.
    7159             :  *
    7160             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7161             :  *
    7162             :  */
    7163             : 
    7164         117 : OGRGeometry *OGRGeometry::Polygonize() const
    7165             : 
    7166             : {
    7167             : #ifndef HAVE_GEOS
    7168             : 
    7169             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7170             :     return nullptr;
    7171             : 
    7172             : #else
    7173             : 
    7174         117 :     const OGRGeometryCollection *poColl = nullptr;
    7175         233 :     if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
    7176         116 :         wkbFlatten(getGeometryType()) == wkbMultiLineString)
    7177         116 :         poColl = toGeometryCollection();
    7178             :     else
    7179           1 :         return nullptr;
    7180             : 
    7181         116 :     const int nCount = poColl->getNumGeometries();
    7182             : 
    7183         116 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7184         116 :     bool bError = false;
    7185             : 
    7186         116 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7187             : 
    7188         116 :     GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
    7189         747 :     for (int ig = 0; ig < nCount; ig++)
    7190             :     {
    7191         631 :         GEOSGeom hGeosGeom = nullptr;
    7192         631 :         const OGRGeometry *poChild = poColl->getGeometryRef(ig);
    7193        1262 :         if (poChild == nullptr ||
    7194         631 :             wkbFlatten(poChild->getGeometryType()) != wkbLineString)
    7195           1 :             bError = true;
    7196             :         else
    7197             :         {
    7198         630 :             hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
    7199         630 :             if (hGeosGeom == nullptr)
    7200           0 :                 bError = true;
    7201             :         }
    7202         631 :         pahGeosGeomList[ig] = hGeosGeom;
    7203             :     }
    7204             : 
    7205         116 :     if (!bError)
    7206             :     {
    7207             :         GEOSGeom hGeosPolygs =
    7208         115 :             GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
    7209             : 
    7210             :         poPolygsOGRGeom =
    7211         115 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7212             :     }
    7213             : 
    7214         747 :     for (int ig = 0; ig < nCount; ig++)
    7215             :     {
    7216         631 :         GEOSGeom hGeosGeom = pahGeosGeomList[ig];
    7217         631 :         if (hGeosGeom != nullptr)
    7218         630 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    7219             :     }
    7220         116 :     delete[] pahGeosGeomList;
    7221         116 :     freeGEOSContext(hGEOSCtxt);
    7222             : 
    7223         116 :     return poPolygsOGRGeom;
    7224             : 
    7225             : #endif  // HAVE_GEOS
    7226             : }
    7227             : 
    7228             : /************************************************************************/
    7229             : /*                          OGR_G_Polygonize()                          */
    7230             : /************************************************************************/
    7231             : /**
    7232             :  * \brief Polygonizes a set of sparse edges.
    7233             :  *
    7234             :  * A new geometry object is created and returned containing a collection
    7235             :  * of reassembled Polygons: NULL will be returned if the input collection
    7236             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7237             :  * into Polygons is impossible due to topological inconsistencies.
    7238             :  *
    7239             :  * This function is the same as the C++ method OGRGeometry::Polygonize().
    7240             :  *
    7241             :  * This function is built on the GEOS library, check it for the definition
    7242             :  * of the geometry operation.
    7243             :  * If OGR is built without the GEOS library, this function will always fail,
    7244             :  * issuing a CPLE_NotSupported error.
    7245             :  *
    7246             :  * @param hTarget The Geometry to be polygonized.
    7247             :  *
    7248             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7249             :  * or NULL if an error occurs.
    7250             :  *
    7251             :  */
    7252             : 
    7253           3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
    7254             : 
    7255             : {
    7256           3 :     VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
    7257             : 
    7258           3 :     return OGRGeometry::ToHandle(
    7259           3 :         OGRGeometry::FromHandle(hTarget)->Polygonize());
    7260             : }
    7261             : 
    7262             : /************************************************************************/
    7263             : /*                             BuildArea()                              */
    7264             : /************************************************************************/
    7265             : 
    7266             : /**
    7267             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7268             :  *
    7269             :  * This method is the same as the C function OGR_G_BuildArea().
    7270             :  *
    7271             :  * Polygonization is performed similarly to OGRGeometry::Polygonize().
    7272             :  * Additionally, holes are dropped and the result is unified producing
    7273             :  * a single Polygon or a MultiPolygon.
    7274             :  *
    7275             :  * A new geometry object is created and returned: NULL on failure,
    7276             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7277             :  * Polygon or MultiPolygon on success.
    7278             :  *
    7279             :  * This method is built on the GEOSBuildArea_r() function of the GEOS
    7280             :  * library, check it for the definition of the geometry operation.
    7281             :  * If OGR is built without the GEOS library, this method will always fail,
    7282             :  * issuing a CPLE_NotSupported error.
    7283             :  *
    7284             :  * @return a newly allocated geometry now owned by the caller,
    7285             :  *         or NULL on failure.
    7286             :  *
    7287             :  * @since OGR 3.11
    7288             :  */
    7289             : 
    7290          30 : OGRGeometry *OGRGeometry::BuildArea() const
    7291             : 
    7292             : {
    7293             : #ifndef HAVE_GEOS
    7294             : 
    7295             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7296             :     return nullptr;
    7297             : 
    7298             : #else
    7299             : 
    7300          30 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7301             : 
    7302          30 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7303          30 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7304          30 :     if (hThisGeosGeom != nullptr)
    7305             :     {
    7306          30 :         GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
    7307             :         poPolygsOGRGeom =
    7308          30 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7309          30 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7310             :     }
    7311          30 :     freeGEOSContext(hGEOSCtxt);
    7312             : 
    7313          30 :     return poPolygsOGRGeom;
    7314             : 
    7315             : #endif  // HAVE_GEOS
    7316             : }
    7317             : 
    7318             : /************************************************************************/
    7319             : /*                          OGR_G_BuildArea()                           */
    7320             : /************************************************************************/
    7321             : 
    7322             : /**
    7323             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7324             :  *
    7325             :  * This function is the same as the C++ method OGRGeometry::BuildArea().
    7326             :  *
    7327             :  * Polygonization is performed similarly to OGR_G_Polygonize().
    7328             :  * Additionally, holes are dropped and the result is unified producing
    7329             :  * a single Polygon or a MultiPolygon.
    7330             :  *
    7331             :  * A new geometry object is created and returned: NULL on failure,
    7332             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7333             :  * Polygon or MultiPolygon on success.
    7334             :  *
    7335             :  * This function is built on the GEOSBuildArea_r() function of the GEOS
    7336             :  * library, check it for the definition of the geometry operation.
    7337             :  * If OGR is built without the GEOS library, this function will always fail,
    7338             :  * issuing a CPLE_NotSupported error.
    7339             :  *
    7340             :  * @param hGeom handle on the geometry to polygonize.
    7341             :  *
    7342             :  * @return a handle on newly allocated geometry now owned by the caller,
    7343             :  *         or NULL on failure.
    7344             :  *
    7345             :  * @since OGR 3.11
    7346             :  */
    7347             : 
    7348           0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
    7349             : 
    7350             : {
    7351           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
    7352             : 
    7353           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
    7354             : }
    7355             : 
    7356             : /************************************************************************/
    7357             : /*                               swapXY()                               */
    7358             : /************************************************************************/
    7359             : 
    7360             : /**
    7361             :  * \brief Swap x and y coordinates.
    7362             :  *
    7363             :  */
    7364             : 
    7365           0 : void OGRGeometry::swapXY()
    7366             : 
    7367             : {
    7368           0 : }
    7369             : 
    7370             : /************************************************************************/
    7371             : /*                               swapXY()                               */
    7372             : /************************************************************************/
    7373             : 
    7374             : /**
    7375             :  * \brief Swap x and y coordinates.
    7376             :  *
    7377             :  * @param hGeom geometry.
    7378             :  */
    7379             : 
    7380          22 : void OGR_G_SwapXY(OGRGeometryH hGeom)
    7381             : {
    7382          22 :     VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
    7383             : 
    7384          22 :     OGRGeometry::FromHandle(hGeom)->swapXY();
    7385             : }
    7386             : 
    7387             : /************************************************************************/
    7388             : /*                        Prepared geometry API                         */
    7389             : /************************************************************************/
    7390             : 
    7391             : #if defined(HAVE_GEOS)
    7392             : struct _OGRPreparedGeometry
    7393             : {
    7394             :     GEOSContextHandle_t hGEOSCtxt;
    7395             :     GEOSGeom hGEOSGeom;
    7396             :     const GEOSPreparedGeometry *poPreparedGEOSGeom;
    7397             : };
    7398             : #endif
    7399             : 
    7400             : /************************************************************************/
    7401             : /*                   OGRHasPreparedGeometrySupport()                    */
    7402             : /************************************************************************/
    7403             : 
    7404             : /** Returns if GEOS has prepared geometry support.
    7405             :  * @return TRUE or FALSE
    7406             :  */
    7407           1 : int OGRHasPreparedGeometrySupport()
    7408             : {
    7409             : #if defined(HAVE_GEOS)
    7410           1 :     return TRUE;
    7411             : #else
    7412             :     return FALSE;
    7413             : #endif
    7414             : }
    7415             : 
    7416             : /************************************************************************/
    7417             : /*                     OGRCreatePreparedGeometry()                      */
    7418             : /************************************************************************/
    7419             : 
    7420             : /** Creates a prepared geometry.
    7421             :  *
    7422             :  * To free with OGRDestroyPreparedGeometry()
    7423             :  *
    7424             :  * @param hGeom input geometry to prepare.
    7425             :  * @return handle to a prepared geometry.
    7426             :  * @since GDAL 3.3
    7427             :  */
    7428       52569 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
    7429             : {
    7430             :     (void)hGeom;
    7431             : #if defined(HAVE_GEOS)
    7432       52569 :     OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
    7433       52569 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    7434       52569 :     GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
    7435       52569 :     if (hGEOSGeom == nullptr)
    7436             :     {
    7437           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7438           0 :         return nullptr;
    7439             :     }
    7440             :     const GEOSPreparedGeometry *poPreparedGEOSGeom =
    7441       52569 :         GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
    7442       52569 :     if (poPreparedGEOSGeom == nullptr)
    7443             :     {
    7444           0 :         GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
    7445           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7446           0 :         return nullptr;
    7447             :     }
    7448             : 
    7449       52569 :     OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
    7450       52569 :     poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
    7451       52569 :     poPreparedGeom->hGEOSGeom = hGEOSGeom;
    7452       52569 :     poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
    7453             : 
    7454       52569 :     return poPreparedGeom;
    7455             : #else
    7456             :     return nullptr;
    7457             : #endif
    7458             : }
    7459             : 
    7460             : /************************************************************************/
    7461             : /*                     OGRDestroyPreparedGeometry()                     */
    7462             : /************************************************************************/
    7463             : 
    7464             : /** Destroys a prepared geometry.
    7465             :  * @param hPreparedGeom prepared geometry.
    7466             :  * @since GDAL 3.3
    7467             :  */
    7468       52615 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
    7469             : {
    7470             :     (void)hPreparedGeom;
    7471             : #if defined(HAVE_GEOS)
    7472       52615 :     if (hPreparedGeom != nullptr)
    7473             :     {
    7474       52569 :         GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
    7475             :                                    hPreparedGeom->poPreparedGEOSGeom);
    7476       52569 :         GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
    7477       52569 :         OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
    7478       52569 :         delete hPreparedGeom;
    7479             :     }
    7480             : #endif
    7481       52615 : }
    7482             : 
    7483             : /************************************************************************/
    7484             : /*                   OGRPreparedGeometryIntersects()                    */
    7485             : /************************************************************************/
    7486             : 
    7487             : /** Returns whether a prepared geometry intersects with a geometry.
    7488             :  * @param hPreparedGeom prepared geometry.
    7489             :  * @param hOtherGeom other geometry.
    7490             :  * @return TRUE or FALSE.
    7491             :  * @since GDAL 3.3
    7492             :  */
    7493        5563 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
    7494             :                                   const OGRGeometryH hOtherGeom)
    7495             : {
    7496             :     (void)hPreparedGeom;
    7497             :     (void)hOtherGeom;
    7498             : #if defined(HAVE_GEOS)
    7499        5563 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7500        5563 :     if (hPreparedGeom == nullptr ||
    7501             :         poOtherGeom == nullptr
    7502             :         // The check for IsEmpty() is for buggy GEOS versions.
    7503             :         // See https://github.com/libgeos/geos/pull/423
    7504       11126 :         || poOtherGeom->IsEmpty())
    7505             :     {
    7506           1 :         return FALSE;
    7507             :     }
    7508             : 
    7509             :     GEOSGeom hGEOSOtherGeom =
    7510        5562 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7511        5562 :     if (hGEOSOtherGeom == nullptr)
    7512           0 :         return FALSE;
    7513             : 
    7514             :     const bool bRet =
    7515        5562 :         GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
    7516             :                                  hPreparedGeom->poPreparedGEOSGeom,
    7517        5562 :                                  hGEOSOtherGeom) == 1;
    7518        5562 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7519             : 
    7520        5562 :     return bRet;
    7521             : #else
    7522             :     return FALSE;
    7523             : #endif
    7524             : }
    7525             : 
    7526             : /** Returns whether a prepared geometry contains a geometry.
    7527             :  * @param hPreparedGeom prepared geometry.
    7528             :  * @param hOtherGeom other geometry.
    7529             :  * @return TRUE or FALSE.
    7530             :  */
    7531      120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
    7532             :                                 const OGRGeometryH hOtherGeom)
    7533             : {
    7534             :     (void)hPreparedGeom;
    7535             :     (void)hOtherGeom;
    7536             : #if defined(HAVE_GEOS)
    7537      120516 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7538      120516 :     if (hPreparedGeom == nullptr ||
    7539             :         poOtherGeom == nullptr
    7540             :         // The check for IsEmpty() is for buggy GEOS versions.
    7541             :         // See https://github.com/libgeos/geos/pull/423
    7542      241032 :         || poOtherGeom->IsEmpty())
    7543             :     {
    7544           1 :         return FALSE;
    7545             :     }
    7546             : 
    7547             :     GEOSGeom hGEOSOtherGeom =
    7548      120515 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7549      120515 :     if (hGEOSOtherGeom == nullptr)
    7550           0 :         return FALSE;
    7551             : 
    7552      120515 :     const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
    7553             :                                              hPreparedGeom->poPreparedGEOSGeom,
    7554      120515 :                                              hGEOSOtherGeom) == 1;
    7555      120515 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7556             : 
    7557      120515 :     return bRet;
    7558             : #else
    7559             :     return FALSE;
    7560             : #endif
    7561             : }
    7562             : 
    7563             : /************************************************************************/
    7564             : /*                        OGRGeometryFromEWKB()                         */
    7565             : /************************************************************************/
    7566             : 
    7567        1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
    7568             :                                  int bIsPostGIS1_EWKB)
    7569             : 
    7570             : {
    7571        1445 :     OGRGeometry *poGeometry = nullptr;
    7572             : 
    7573        1445 :     size_t nWKBSize = 0;
    7574        1445 :     const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
    7575        1445 :     if (pabyWKB == nullptr)
    7576           0 :         return nullptr;
    7577             : 
    7578             :     /* -------------------------------------------------------------------- */
    7579             :     /*      Try to ingest the geometry.                                     */
    7580             :     /* -------------------------------------------------------------------- */
    7581        1445 :     (void)OGRGeometryFactory::createFromWkb(
    7582             :         pabyWKB, nullptr, &poGeometry, nWKBSize,
    7583             :         (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
    7584             : 
    7585        1445 :     return poGeometry;
    7586             : }
    7587             : 
    7588             : /************************************************************************/
    7589             : /*                       OGRGeometryFromHexEWKB()                       */
    7590             : /************************************************************************/
    7591             : 
    7592        1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
    7593             :                                     int bIsPostGIS1_EWKB)
    7594             : 
    7595             : {
    7596        1443 :     if (pszBytea == nullptr)
    7597           0 :         return nullptr;
    7598             : 
    7599        1443 :     int nWKBLength = 0;
    7600        1443 :     GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
    7601             : 
    7602             :     OGRGeometry *poGeometry =
    7603        1443 :         OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
    7604             : 
    7605        1443 :     CPLFree(pabyWKB);
    7606             : 
    7607        1443 :     return poGeometry;
    7608             : }
    7609             : 
    7610             : /************************************************************************/
    7611             : /*                        OGRGeometryToHexEWKB()                        */
    7612             : /************************************************************************/
    7613             : 
    7614        1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
    7615             :                            int nPostGISMajor, int nPostGISMinor)
    7616             : {
    7617        1071 :     const size_t nWkbSize = poGeometry->WkbSize();
    7618        1071 :     GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
    7619        1071 :     if (pabyWKB == nullptr)
    7620           0 :         return CPLStrdup("");
    7621             : 
    7622         118 :     if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
    7623        1815 :         wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
    7624         626 :         poGeometry->IsEmpty())
    7625             :     {
    7626           2 :         if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
    7627             :             OGRERR_NONE)
    7628             :         {
    7629           0 :             CPLFree(pabyWKB);
    7630           0 :             return CPLStrdup("");
    7631             :         }
    7632             :     }
    7633        1069 :     else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
    7634             :                                      (nPostGISMajor < 2)
    7635             :                                          ? wkbVariantPostGIS1
    7636        1069 :                                          : wkbVariantOldOgc) != OGRERR_NONE)
    7637             :     {
    7638           0 :         CPLFree(pabyWKB);
    7639           0 :         return CPLStrdup("");
    7640             :     }
    7641             : 
    7642             :     // When converting to hex, each byte takes 2 hex characters.  In addition
    7643             :     // we add in 8 characters to represent the SRID integer in hex, and
    7644             :     // one for a null terminator.
    7645             :     // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
    7646             :     // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
    7647             :     // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
    7648        1071 :     if (nWkbSize >
    7649        1071 :         static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
    7650             :     {
    7651           0 :         CPLFree(pabyWKB);
    7652           0 :         return CPLStrdup("");
    7653             :     }
    7654        1071 :     const size_t nTextSize = nWkbSize * 2 + 8 + 1;
    7655        1071 :     char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
    7656        1071 :     if (pszTextBuf == nullptr)
    7657             :     {
    7658           0 :         CPLFree(pabyWKB);
    7659           0 :         return CPLStrdup("");
    7660             :     }
    7661        1071 :     char *pszTextBufCurrent = pszTextBuf;
    7662             : 
    7663             :     // Convert the 1st byte, which is the endianness flag, to hex.
    7664        1071 :     char *pszHex = CPLBinaryToHex(1, pabyWKB);
    7665        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7666        1071 :     CPLFree(pszHex);
    7667        1071 :     pszTextBufCurrent += 2;
    7668             : 
    7669             :     // Next, get the geom type which is bytes 2 through 5.
    7670             :     GUInt32 geomType;
    7671        1071 :     memcpy(&geomType, pabyWKB + 1, 4);
    7672             : 
    7673             :     // Now add the SRID flag if an SRID is provided.
    7674        1071 :     if (nSRSId > 0)
    7675             :     {
    7676             :         // Change the flag to wkbNDR (little) endianness.
    7677         541 :         constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
    7678         541 :         GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
    7679             :         // Apply the flag.
    7680         541 :         geomType = geomType | nGSrsFlag;
    7681             :     }
    7682             : 
    7683             :     // Now write the geom type which is 4 bytes.
    7684        1071 :     pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
    7685        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7686        1071 :     CPLFree(pszHex);
    7687        1071 :     pszTextBufCurrent += 8;
    7688             : 
    7689             :     // Now include SRID if provided.
    7690        1071 :     if (nSRSId > 0)
    7691             :     {
    7692             :         // Force the srsid to wkbNDR (little) endianness.
    7693         541 :         const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
    7694         541 :         pszHex = CPLBinaryToHex(sizeof(nGSRSId),
    7695             :                                 reinterpret_cast<const GByte *>(&nGSRSId));
    7696         541 :         strcpy(pszTextBufCurrent, pszHex);
    7697         541 :         CPLFree(pszHex);
    7698         541 :         pszTextBufCurrent += 8;
    7699             :     }
    7700             : 
    7701             :     // Copy the rest of the data over - subtract
    7702             :     // 5 since we already copied 5 bytes above.
    7703        1071 :     pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
    7704        1071 :     CPLFree(pabyWKB);
    7705        1071 :     if (!pszHex || pszHex[0] == 0)
    7706             :     {
    7707           0 :         CPLFree(pszTextBuf);
    7708           0 :         return pszHex;
    7709             :     }
    7710        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7711        1071 :     CPLFree(pszHex);
    7712             : 
    7713        1071 :     return pszTextBuf;
    7714             : }
    7715             : 
    7716             : /************************************************************************/
    7717             : /*                       importPreambleFromWkb()                        */
    7718             : /************************************************************************/
    7719             : 
    7720             : //! @cond Doxygen_Suppress
    7721      180798 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
    7722             :                                           size_t nSize,
    7723             :                                           OGRwkbByteOrder &eByteOrder,
    7724             :                                           OGRwkbVariant eWkbVariant)
    7725             : {
    7726      180798 :     if (nSize < 9 && nSize != static_cast<size_t>(-1))
    7727           0 :         return OGRERR_NOT_ENOUGH_DATA;
    7728             : 
    7729             :     /* -------------------------------------------------------------------- */
    7730             :     /*      Get the byte order byte.                                        */
    7731             :     /* -------------------------------------------------------------------- */
    7732      180798 :     int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
    7733      180798 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    7734           0 :         return OGRERR_CORRUPT_DATA;
    7735      180798 :     eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    7736             : 
    7737             :     /* -------------------------------------------------------------------- */
    7738             :     /*      Get the geometry feature type.                                  */
    7739             :     /* -------------------------------------------------------------------- */
    7740             :     OGRwkbGeometryType eGeometryType;
    7741             :     const OGRErr err =
    7742      180798 :         OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
    7743      180798 :     if (wkbHasZ(eGeometryType))
    7744       62309 :         flags |= OGR_G_3D;
    7745      180798 :     if (wkbHasM(eGeometryType))
    7746       59692 :         flags |= OGR_G_MEASURED;
    7747             : 
    7748      180798 :     if (err != OGRERR_NONE || eGeometryType != getGeometryType())
    7749           0 :         return OGRERR_CORRUPT_DATA;
    7750             : 
    7751      180798 :     return OGRERR_NONE;
    7752             : }
    7753             : 
    7754             : /************************************************************************/
    7755             : /*                    importPreambleOfCollectionFromWkb()              */
    7756             : /*                                                                      */
    7757             : /*      Utility method for OGRSimpleCurve, OGRCompoundCurve,            */
    7758             : /*      OGRCurvePolygon and OGRGeometryCollection.                      */
    7759             : /************************************************************************/
    7760             : 
    7761       97429 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
    7762             :     const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
    7763             :     OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
    7764             :     OGRwkbVariant eWkbVariant)
    7765             : {
    7766       97429 :     nGeomCount = 0;
    7767             : 
    7768             :     OGRErr eErr =
    7769       97429 :         importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
    7770       97429 :     if (eErr != OGRERR_NONE)
    7771           0 :         return eErr;
    7772             : 
    7773             :     /* -------------------------------------------------------------------- */
    7774             :     /*      Clear existing Geoms.                                           */
    7775             :     /* -------------------------------------------------------------------- */
    7776       97429 :     int _flags = flags;  // flags set in importPreambleFromWkb
    7777       97429 :     empty();             // may reset flags etc.
    7778             : 
    7779             :     // restore
    7780       97429 :     if (_flags & OGR_G_3D)
    7781       59265 :         set3D(TRUE);
    7782       97429 :     if (_flags & OGR_G_MEASURED)
    7783       56768 :         setMeasured(TRUE);
    7784             : 
    7785             :     /* -------------------------------------------------------------------- */
    7786             :     /*      Get the sub-geometry count.                                     */
    7787             :     /* -------------------------------------------------------------------- */
    7788       97429 :     memcpy(&nGeomCount, pabyData + 5, 4);
    7789             : 
    7790       97429 :     if (OGR_SWAP(eByteOrder))
    7791         386 :         nGeomCount = CPL_SWAP32(nGeomCount);
    7792             : 
    7793      194724 :     if (nGeomCount < 0 ||
    7794       97295 :         static_cast<size_t>(nGeomCount) >
    7795       97295 :             std::numeric_limits<size_t>::max() / nMinSubGeomSize)
    7796             :     {
    7797         134 :         nGeomCount = 0;
    7798         134 :         return OGRERR_CORRUPT_DATA;
    7799             :     }
    7800       97295 :     const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
    7801             : 
    7802             :     // Each ring has a minimum of nMinSubGeomSize bytes.
    7803       97295 :     if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
    7804             :     {
    7805         910 :         CPLError(CE_Failure, CPLE_AppDefined,
    7806             :                  "Length of input WKB is too small");
    7807         910 :         nGeomCount = 0;
    7808         910 :         return OGRERR_NOT_ENOUGH_DATA;
    7809             :     }
    7810             : 
    7811       96385 :     nDataOffset = 9;
    7812       96385 :     if (nSize != static_cast<size_t>(-1))
    7813             :     {
    7814       96365 :         CPLAssert(nSize >= nDataOffset);
    7815       96365 :         nSize -= nDataOffset;
    7816             :     }
    7817             : 
    7818       96385 :     return OGRERR_NONE;
    7819             : }
    7820             : 
    7821             : /************************************************************************/
    7822             : /*                      importCurveCollectionFromWkt()                  */
    7823             : /*                                                                      */
    7824             : /*      Utility method for OGRCompoundCurve, OGRCurvePolygon and        */
    7825             : /*      OGRMultiCurve.                                                  */
    7826             : /************************************************************************/
    7827             : 
    7828        1445 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
    7829             :     const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
    7830             :     int bAllowCurve, int bAllowCompoundCurve,
    7831             :     OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
    7832             : 
    7833             : {
    7834        1445 :     int bHasZ = FALSE;
    7835        1445 :     int bHasM = FALSE;
    7836        1445 :     bool bIsEmpty = false;
    7837        1445 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
    7838        1445 :     flags = 0;
    7839        1445 :     if (eErr != OGRERR_NONE)
    7840          14 :         return eErr;
    7841        1431 :     if (bHasZ)
    7842         206 :         flags |= OGR_G_3D;
    7843        1431 :     if (bHasM)
    7844         132 :         flags |= OGR_G_MEASURED;
    7845        1431 :     if (bIsEmpty)
    7846         111 :         return OGRERR_NONE;
    7847             : 
    7848             :     char szToken[OGR_WKT_TOKEN_MAX];
    7849        1320 :     const char *pszInput = *ppszInput;
    7850        1320 :     eErr = OGRERR_NONE;
    7851             : 
    7852             :     // Skip first '('.
    7853        1320 :     pszInput = OGRWktReadToken(pszInput, szToken);
    7854             : 
    7855             :     /* ==================================================================== */
    7856             :     /*      Read each curve in turn.  Note that we try to reuse the same    */
    7857             :     /*      point list buffer from curve to curve to cut down on            */
    7858             :     /*      allocate/deallocate overhead.                                   */
    7859             :     /* ==================================================================== */
    7860        1320 :     OGRRawPoint *paoPoints = nullptr;
    7861        1320 :     int nMaxPoints = 0;
    7862        1320 :     double *padfZ = nullptr;
    7863             : 
    7864         653 :     do
    7865             :     {
    7866             : 
    7867             :         /* --------------------------------------------------------------------
    7868             :          */
    7869             :         /*      Get the first token, which should be the geometry type. */
    7870             :         /* --------------------------------------------------------------------
    7871             :          */
    7872        1973 :         const char *pszInputBefore = pszInput;
    7873        1973 :         pszInput = OGRWktReadToken(pszInput, szToken);
    7874             : 
    7875             :         /* --------------------------------------------------------------------
    7876             :          */
    7877             :         /*      Do the import. */
    7878             :         /* --------------------------------------------------------------------
    7879             :          */
    7880        1973 :         OGRCurve *poCurve = nullptr;
    7881        1973 :         if (EQUAL(szToken, "("))
    7882             :         {
    7883        1425 :             OGRLineString *poLine = new OGRLineString();
    7884        1425 :             poCurve = poLine;
    7885        1425 :             pszInput = pszInputBefore;
    7886        1425 :             eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
    7887             :                                                  paoPoints, nMaxPoints, padfZ);
    7888             :         }
    7889         548 :         else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
    7890             :         {
    7891          16 :             poCurve = new OGRLineString();
    7892             :         }
    7893             :         // Accept LINESTRING(), but this is an extension to the BNF, also
    7894             :         // accepted by PostGIS.
    7895         532 :         else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
    7896         517 :                  (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
    7897         517 :                   !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
    7898        1222 :                   OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
    7899         158 :                  (bAllowCompoundCurve &&
    7900         158 :                   STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
    7901             :         {
    7902         494 :             OGRGeometry *poGeom = nullptr;
    7903         494 :             pszInput = pszInputBefore;
    7904             :             eErr =
    7905         494 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
    7906         494 :             if (poGeom == nullptr)
    7907             :             {
    7908           1 :                 eErr = OGRERR_CORRUPT_DATA;
    7909             :             }
    7910             :             else
    7911             :             {
    7912         493 :                 poCurve = poGeom->toCurve();
    7913             :             }
    7914             :         }
    7915             :         else
    7916             :         {
    7917          38 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
    7918             :                      szToken);
    7919          38 :             eErr = OGRERR_CORRUPT_DATA;
    7920             :         }
    7921             : 
    7922             :         // If this has M it is an error if poGeom does not have M.
    7923        1973 :         if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
    7924           0 :             eErr = OGRERR_CORRUPT_DATA;
    7925             : 
    7926        1973 :         if (eErr == OGRERR_NONE)
    7927        1928 :             eErr = pfnAddCurveDirectly(this, poCurve);
    7928        1973 :         if (eErr != OGRERR_NONE)
    7929             :         {
    7930          55 :             delete poCurve;
    7931          55 :             break;
    7932             :         }
    7933             : 
    7934             :         /* --------------------------------------------------------------------
    7935             :          */
    7936             :         /*      Read the delimiter following the surface. */
    7937             :         /* --------------------------------------------------------------------
    7938             :          */
    7939        1918 :         pszInput = OGRWktReadToken(pszInput, szToken);
    7940        1918 :     } while (szToken[0] == ',' && eErr == OGRERR_NONE);
    7941             : 
    7942        1320 :     CPLFree(paoPoints);
    7943        1320 :     CPLFree(padfZ);
    7944             : 
    7945             :     /* -------------------------------------------------------------------- */
    7946             :     /*      freak if we don't get a closing bracket.                        */
    7947             :     /* -------------------------------------------------------------------- */
    7948             : 
    7949        1320 :     if (eErr != OGRERR_NONE)
    7950          55 :         return eErr;
    7951             : 
    7952        1265 :     if (szToken[0] != ')')
    7953           9 :         return OGRERR_CORRUPT_DATA;
    7954             : 
    7955        1256 :     *ppszInput = pszInput;
    7956        1256 :     return OGRERR_NONE;
    7957             : }
    7958             : 
    7959             : //! @endcond
    7960             : 
    7961             : /************************************************************************/
    7962             : /*                           OGR_GT_Flatten()                           */
    7963             : /************************************************************************/
    7964             : /**
    7965             :  * \brief Returns the 2D geometry type corresponding to the passed geometry
    7966             :  * type.
    7967             :  *
    7968             :  * This function is intended to work with geometry types as old-style 99-402
    7969             :  * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
    7970             :  * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
    7971             :  *
    7972             :  * @param eType Input geometry type
    7973             :  *
    7974             :  * @return 2D geometry type corresponding to the passed geometry type.
    7975             :  *
    7976             :  */
    7977             : 
    7978     8053280 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
    7979             : {
    7980     8053280 :     eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
    7981     8053280 :     if (eType >= 1000 && eType < 2000)  // ISO Z.
    7982     2776760 :         return static_cast<OGRwkbGeometryType>(eType - 1000);
    7983     5276510 :     if (eType >= 2000 && eType < 3000)  // ISO M.
    7984        6059 :         return static_cast<OGRwkbGeometryType>(eType - 2000);
    7985     5270450 :     if (eType >= 3000 && eType < 4000)  // ISO ZM.
    7986      136249 :         return static_cast<OGRwkbGeometryType>(eType - 3000);
    7987     5134200 :     return eType;
    7988             : }
    7989             : 
    7990             : /************************************************************************/
    7991             : /*                            OGR_GT_HasZ()                             */
    7992             : /************************************************************************/
    7993             : /**
    7994             :  * \brief Return if the geometry type is a 3D geometry type.
    7995             :  *
    7996             :  * @param eType Input geometry type
    7997             :  *
    7998             :  * @return TRUE if the geometry type is a 3D geometry type.
    7999             :  *
    8000             :  */
    8001             : 
    8002     2118020 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
    8003             : {
    8004     2118020 :     if (eType & wkb25DBitInternalUse)
    8005      157100 :         return TRUE;
    8006     1960920 :     if (eType >= 1000 && eType < 2000)  // Accept 1000 for wkbUnknownZ.
    8007         264 :         return TRUE;
    8008     1960660 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8009      121539 :         return TRUE;
    8010     1839120 :     return FALSE;
    8011             : }
    8012             : 
    8013             : /************************************************************************/
    8014             : /*                            OGR_GT_HasM()                             */
    8015             : /************************************************************************/
    8016             : /**
    8017             :  * \brief Return if the geometry type is a measured type.
    8018             :  *
    8019             :  * @param eType Input geometry type
    8020             :  *
    8021             :  * @return TRUE if the geometry type is a measured type.
    8022             :  *
    8023             :  */
    8024             : 
    8025     2177030 : int OGR_GT_HasM(OGRwkbGeometryType eType)
    8026             : {
    8027     2177030 :     if (eType >= 2000 && eType < 3000)  // Accept 2000 for wkbUnknownM.
    8028        2593 :         return TRUE;
    8029     2174440 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8030      121195 :         return TRUE;
    8031     2053240 :     return FALSE;
    8032             : }
    8033             : 
    8034             : /************************************************************************/
    8035             : /*                            OGR_GT_SetZ()                             */
    8036             : /************************************************************************/
    8037             : /**
    8038             :  * \brief Returns the 3D geometry type corresponding to the passed geometry
    8039             :  * type.
    8040             :  *
    8041             :  * @param eType Input geometry type
    8042             :  *
    8043             :  * @return 3D geometry type corresponding to the passed geometry type.
    8044             :  *
    8045             :  */
    8046             : 
    8047        5748 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
    8048             : {
    8049        5748 :     if (OGR_GT_HasZ(eType) || eType == wkbNone)
    8050         498 :         return eType;
    8051        5250 :     if (eType <= wkbGeometryCollection)
    8052        5148 :         return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
    8053             :     else
    8054         102 :         return static_cast<OGRwkbGeometryType>(eType + 1000);
    8055             : }
    8056             : 
    8057             : /************************************************************************/
    8058             : /*                            OGR_GT_SetM()                             */
    8059             : /************************************************************************/
    8060             : /**
    8061             :  * \brief Returns the measured geometry type corresponding to the passed
    8062             :  * geometry type.
    8063             :  *
    8064             :  * @param eType Input geometry type
    8065             :  *
    8066             :  * @return measured geometry type corresponding to the passed geometry type.
    8067             :  *
    8068             :  */
    8069             : 
    8070        2013 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
    8071             : {
    8072        2013 :     if (OGR_GT_HasM(eType) || eType == wkbNone)
    8073         262 :         return eType;
    8074        1751 :     if (eType & wkb25DBitInternalUse)
    8075             :     {
    8076         717 :         eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
    8077         717 :         eType = static_cast<OGRwkbGeometryType>(eType + 1000);
    8078             :     }
    8079        1751 :     return static_cast<OGRwkbGeometryType>(eType + 2000);
    8080             : }
    8081             : 
    8082             : /************************************************************************/
    8083             : /*                         OGR_GT_SetModifier()                         */
    8084             : /************************************************************************/
    8085             : /**
    8086             :  * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
    8087             :  *
    8088             :  * @param eType Input geometry type
    8089             :  * @param bHasZ TRUE if the output geometry type must be 3D.
    8090             :  * @param bHasM TRUE if the output geometry type must be measured.
    8091             :  *
    8092             :  * @return Output geometry type.
    8093             :  *
    8094             :  */
    8095             : 
    8096        5561 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
    8097             :                                       int bHasM)
    8098             : {
    8099        5561 :     if (bHasZ && bHasM)
    8100         342 :         return OGR_GT_SetM(OGR_GT_SetZ(eType));
    8101        5219 :     else if (bHasM)
    8102         333 :         return OGR_GT_SetM(wkbFlatten(eType));
    8103        4886 :     else if (bHasZ)
    8104        2110 :         return OGR_GT_SetZ(wkbFlatten(eType));
    8105             :     else
    8106        2776 :         return wkbFlatten(eType);
    8107             : }
    8108             : 
    8109             : /************************************************************************/
    8110             : /*                         OGR_GT_IsSubClassOf)                         */
    8111             : /************************************************************************/
    8112             : /**
    8113             :  * \brief Returns if a type is a subclass of another one
    8114             :  *
    8115             :  * @param eType Type.
    8116             :  * @param eSuperType Super type
    8117             :  *
    8118             :  * @return TRUE if eType is a subclass of eSuperType.
    8119             :  *
    8120             :  */
    8121             : 
    8122      152600 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
    8123             : {
    8124      152600 :     eSuperType = wkbFlatten(eSuperType);
    8125      152600 :     eType = wkbFlatten(eType);
    8126             : 
    8127      152600 :     if (eSuperType == eType || eSuperType == wkbUnknown)
    8128       19437 :         return TRUE;
    8129             : 
    8130      133163 :     if (eSuperType == wkbGeometryCollection)
    8131       52340 :         return eType == wkbMultiPoint || eType == wkbMultiLineString ||
    8132      106442 :                eType == wkbMultiPolygon || eType == wkbMultiCurve ||
    8133       54102 :                eType == wkbMultiSurface;
    8134             : 
    8135       79061 :     if (eSuperType == wkbCurvePolygon)
    8136       21915 :         return eType == wkbPolygon || eType == wkbTriangle;
    8137             : 
    8138       57146 :     if (eSuperType == wkbMultiCurve)
    8139         249 :         return eType == wkbMultiLineString;
    8140             : 
    8141       56897 :     if (eSuperType == wkbMultiSurface)
    8142         288 :         return eType == wkbMultiPolygon;
    8143             : 
    8144       56609 :     if (eSuperType == wkbCurve)
    8145       23335 :         return eType == wkbLineString || eType == wkbCircularString ||
    8146       23335 :                eType == wkbCompoundCurve;
    8147             : 
    8148       33274 :     if (eSuperType == wkbSurface)
    8149        3519 :         return eType == wkbCurvePolygon || eType == wkbPolygon ||
    8150        7186 :                eType == wkbTriangle || eType == wkbPolyhedralSurface ||
    8151        3667 :                eType == wkbTIN;
    8152             : 
    8153       29607 :     if (eSuperType == wkbPolygon)
    8154         221 :         return eType == wkbTriangle;
    8155             : 
    8156       29386 :     if (eSuperType == wkbPolyhedralSurface)
    8157       14640 :         return eType == wkbTIN;
    8158             : 
    8159       14746 :     return FALSE;
    8160             : }
    8161             : 
    8162             : /************************************************************************/
    8163             : /*                        OGR_GT_GetCollection()                        */
    8164             : /************************************************************************/
    8165             : /**
    8166             :  * \brief Returns the collection type that can contain the passed geometry type
    8167             :  *
    8168             :  * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
    8169             :  * wkbLineString->wkbMultiLineString,
    8170             :  * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
    8171             :  * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
    8172             :  * wkbCurvePolygon->wkbMultiSurface.
    8173             :  * In other cases, wkbUnknown is returned
    8174             :  *
    8175             :  * Passed Z, M, ZM flag is preserved.
    8176             :  *
    8177             :  *
    8178             :  * @param eType Input geometry type
    8179             :  *
    8180             :  * @return the collection type that can contain the passed geometry type or
    8181             :  * wkbUnknown
    8182             :  *
    8183             :  */
    8184             : 
    8185        2707 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
    8186             : {
    8187        2707 :     const bool bHasZ = wkbHasZ(eType);
    8188        2707 :     const bool bHasM = wkbHasM(eType);
    8189        2707 :     if (eType == wkbNone)
    8190           1 :         return wkbNone;
    8191        2706 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8192        2706 :     if (eFGType == wkbPoint)
    8193          67 :         eType = wkbMultiPoint;
    8194             : 
    8195        2639 :     else if (eFGType == wkbLineString)
    8196         187 :         eType = wkbMultiLineString;
    8197             : 
    8198        2452 :     else if (eFGType == wkbPolygon)
    8199        1002 :         eType = wkbMultiPolygon;
    8200             : 
    8201        1450 :     else if (eFGType == wkbTriangle)
    8202           7 :         eType = wkbTIN;
    8203             : 
    8204        1443 :     else if (OGR_GT_IsCurve(eFGType))
    8205         189 :         eType = wkbMultiCurve;
    8206             : 
    8207        1254 :     else if (OGR_GT_IsSurface(eFGType))
    8208         952 :         eType = wkbMultiSurface;
    8209             : 
    8210             :     else
    8211         302 :         return wkbUnknown;
    8212             : 
    8213        2404 :     if (bHasZ)
    8214           3 :         eType = wkbSetZ(eType);
    8215        2404 :     if (bHasM)
    8216           3 :         eType = wkbSetM(eType);
    8217             : 
    8218        2404 :     return eType;
    8219             : }
    8220             : 
    8221             : /************************************************************************/
    8222             : /*                          OGR_GT_GetSingle()                          */
    8223             : /************************************************************************/
    8224             : /**
    8225             :  * \brief Returns the non-collection type that be contained in the passed
    8226             :  * geometry type.
    8227             :  *
    8228             :  * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
    8229             :  * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
    8230             :  * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
    8231             :  * wkbGeometryCollection -> wkbUnknown
    8232             :  * In other cases, the original geometry is returned.
    8233             :  *
    8234             :  * Passed Z, M, ZM flag is preserved.
    8235             :  *
    8236             :  *
    8237             :  * @param eType Input geometry type
    8238             :  *
    8239             :  * @return the the non-collection type that be contained in the passed geometry
    8240             :  * type or wkbUnknown
    8241             :  *
    8242             :  * @since GDAL 3.11
    8243             :  */
    8244             : 
    8245          43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
    8246             : {
    8247          43 :     const bool bHasZ = wkbHasZ(eType);
    8248          43 :     const bool bHasM = wkbHasM(eType);
    8249          43 :     if (eType == wkbNone)
    8250           1 :         return wkbNone;
    8251          42 :     const OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8252          42 :     if (eFGType == wkbMultiPoint)
    8253           8 :         eType = wkbPoint;
    8254             : 
    8255          34 :     else if (eFGType == wkbMultiLineString)
    8256           4 :         eType = wkbLineString;
    8257             : 
    8258          30 :     else if (eFGType == wkbMultiPolygon)
    8259           2 :         eType = wkbPolygon;
    8260             : 
    8261          28 :     else if (eFGType == wkbMultiCurve)
    8262           2 :         eType = wkbCompoundCurve;
    8263             : 
    8264          26 :     else if (eFGType == wkbMultiSurface)
    8265           2 :         eType = wkbCurvePolygon;
    8266             : 
    8267          24 :     else if (eFGType == wkbGeometryCollection)
    8268           1 :         return wkbUnknown;
    8269             : 
    8270          41 :     if (bHasZ)
    8271           3 :         eType = wkbSetZ(eType);
    8272          41 :     if (bHasM)
    8273           2 :         eType = wkbSetM(eType);
    8274             : 
    8275          41 :     return eType;
    8276             : }
    8277             : 
    8278             : /************************************************************************/
    8279             : /*                          OGR_GT_GetCurve()                           */
    8280             : /************************************************************************/
    8281             : /**
    8282             :  * \brief Returns the curve geometry type that can contain the passed geometry
    8283             :  * type
    8284             :  *
    8285             :  * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
    8286             :  * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
    8287             :  * and wkbMultiLineString->wkbMultiCurve.
    8288             :  * In other cases, the passed geometry is returned.
    8289             :  *
    8290             :  * Passed Z, M, ZM flag is preserved.
    8291             :  *
    8292             :  * @param eType Input geometry type
    8293             :  *
    8294             :  * @return the curve type that can contain the passed geometry type
    8295             :  *
    8296             :  */
    8297             : 
    8298          35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
    8299             : {
    8300          35 :     const bool bHasZ = wkbHasZ(eType);
    8301          35 :     const bool bHasM = wkbHasM(eType);
    8302          35 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8303             : 
    8304          35 :     if (eFGType == wkbLineString)
    8305           3 :         eType = wkbCompoundCurve;
    8306             : 
    8307          32 :     else if (eFGType == wkbPolygon)
    8308           1 :         eType = wkbCurvePolygon;
    8309             : 
    8310          31 :     else if (eFGType == wkbTriangle)
    8311           0 :         eType = wkbCurvePolygon;
    8312             : 
    8313          31 :     else if (eFGType == wkbMultiLineString)
    8314           6 :         eType = wkbMultiCurve;
    8315             : 
    8316          25 :     else if (eFGType == wkbMultiPolygon)
    8317           4 :         eType = wkbMultiSurface;
    8318             : 
    8319          35 :     if (bHasZ)
    8320           4 :         eType = wkbSetZ(eType);
    8321          35 :     if (bHasM)
    8322           4 :         eType = wkbSetM(eType);
    8323             : 
    8324          35 :     return eType;
    8325             : }
    8326             : 
    8327             : /************************************************************************/
    8328             : /*                          OGR_GT_GetLinear()                          */
    8329             : /************************************************************************/
    8330             : /**
    8331             :  * \brief Returns the non-curve geometry type that can contain the passed
    8332             :  * geometry type
    8333             :  *
    8334             :  * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
    8335             :  * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
    8336             :  * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
    8337             :  * In other cases, the passed geometry is returned.
    8338             :  *
    8339             :  * Passed Z, M, ZM flag is preserved.
    8340             :  *
    8341             :  * @param eType Input geometry type
    8342             :  *
    8343             :  * @return the non-curve type that can contain the passed geometry type
    8344             :  *
    8345             :  */
    8346             : 
    8347         783 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
    8348             : {
    8349         783 :     const bool bHasZ = wkbHasZ(eType);
    8350         783 :     const bool bHasM = wkbHasM(eType);
    8351         783 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8352             : 
    8353         783 :     if (OGR_GT_IsCurve(eFGType))
    8354          56 :         eType = wkbLineString;
    8355             : 
    8356         727 :     else if (OGR_GT_IsSurface(eFGType))
    8357          43 :         eType = wkbPolygon;
    8358             : 
    8359         684 :     else if (eFGType == wkbMultiCurve)
    8360         185 :         eType = wkbMultiLineString;
    8361             : 
    8362         499 :     else if (eFGType == wkbMultiSurface)
    8363         165 :         eType = wkbMultiPolygon;
    8364             : 
    8365         783 :     if (bHasZ)
    8366         154 :         eType = wkbSetZ(eType);
    8367         783 :     if (bHasM)
    8368         101 :         eType = wkbSetM(eType);
    8369             : 
    8370         783 :     return eType;
    8371             : }
    8372             : 
    8373             : /************************************************************************/
    8374             : /*                           OGR_GT_IsCurve()                           */
    8375             : /************************************************************************/
    8376             : 
    8377             : /**
    8378             :  * \brief Return if a geometry type is an instance of Curve
    8379             :  *
    8380             :  * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
    8381             :  * and their Z/M/ZM variant.
    8382             :  *
    8383             :  * @param eGeomType the geometry type
    8384             :  * @return TRUE if the geometry type is an instance of Curve
    8385             :  *
    8386             :  */
    8387             : 
    8388       23326 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
    8389             : {
    8390       23326 :     return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
    8391             : }
    8392             : 
    8393             : /************************************************************************/
    8394             : /*                          OGR_GT_IsSurface()                          */
    8395             : /************************************************************************/
    8396             : 
    8397             : /**
    8398             :  * \brief Return if a geometry type is an instance of Surface
    8399             :  *
    8400             :  * Such geometry type are wkbCurvePolygon and wkbPolygon
    8401             :  * and their Z/M/ZM variant.
    8402             :  *
    8403             :  * @param eGeomType the geometry type
    8404             :  * @return TRUE if the geometry type is an instance of Surface
    8405             :  *
    8406             :  */
    8407             : 
    8408        3661 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
    8409             : {
    8410        3661 :     return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
    8411             : }
    8412             : 
    8413             : /************************************************************************/
    8414             : /*                         OGR_GT_IsNonLinear()                         */
    8415             : /************************************************************************/
    8416             : 
    8417             : /**
    8418             :  * \brief Return if a geometry type is a non-linear geometry type.
    8419             :  *
    8420             :  * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
    8421             :  * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
    8422             :  * Z/M variants.
    8423             :  *
    8424             :  * @param eGeomType the geometry type
    8425             :  * @return TRUE if the geometry type is a non-linear geometry type.
    8426             :  *
    8427             :  */
    8428             : 
    8429      114674 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
    8430             : {
    8431      114674 :     OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
    8432      114666 :     return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
    8433      114593 :            eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
    8434      229340 :            eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
    8435      114674 :            eFGeomType == wkbMultiSurface;
    8436             : }
    8437             : 
    8438             : /************************************************************************/
    8439             : /*                            CastToError()                             */
    8440             : /************************************************************************/
    8441             : 
    8442             : //! @cond Doxygen_Suppress
    8443           0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
    8444             : {
    8445           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
    8446           0 :              poGeom->getGeometryName());
    8447           0 :     delete poGeom;
    8448           0 :     return nullptr;
    8449             : }
    8450             : 
    8451             : //! @endcond
    8452             : 
    8453             : /************************************************************************/
    8454             : /*                         OGRexportToSFCGAL()                          */
    8455             : /************************************************************************/
    8456             : 
    8457             : //! @cond Doxygen_Suppress
    8458             : sfcgal_geometry_t *
    8459           0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
    8460             : {
    8461             : #ifdef HAVE_SFCGAL
    8462             : 
    8463             :     sfcgal_init();
    8464             : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8465             : 
    8466             :     const auto exportToSFCGALViaWKB =
    8467             :         [](const OGRGeometry *geom) -> sfcgal_geometry_t *
    8468             :     {
    8469             :         if (!geom)
    8470             :             return nullptr;
    8471             : 
    8472             :         // Get WKB size and allocate buffer
    8473             :         size_t nSize = geom->WkbSize();
    8474             :         unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
    8475             : 
    8476             :         // Set export options with NDR byte order
    8477             :         OGRwkbExportOptions oOptions;
    8478             :         oOptions.eByteOrder = wkbNDR;
    8479             :         // and ISO to avoid wkb25DBit for Z geometries
    8480             :         oOptions.eWkbVariant = wkbVariantIso;
    8481             : 
    8482             :         // Export to WKB
    8483             :         sfcgal_geometry_t *sfcgalGeom = nullptr;
    8484             :         if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
    8485             :         {
    8486             :             sfcgalGeom = sfcgal_io_read_wkb(
    8487             :                 reinterpret_cast<const char *>(pabyWkb), nSize);
    8488             :         }
    8489             : 
    8490             :         CPLFree(pabyWkb);
    8491             :         return sfcgalGeom;
    8492             :     };
    8493             : 
    8494             :     // Handle special cases
    8495             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8496             :     {
    8497             :         std::unique_ptr<OGRLineString> poLS(
    8498             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8499             :         return exportToSFCGALViaWKB(poLS.get());
    8500             :     }
    8501             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8502             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8503             :     {
    8504             :         std::unique_ptr<OGRLineString> poLS(
    8505             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8506             :                 ->toLineString());
    8507             :         return exportToSFCGALViaWKB(poLS.get());
    8508             :     }
    8509             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8510             :     {
    8511             :         std::unique_ptr<OGRPolygon> poPolygon(
    8512             :             OGRGeometryFactory::forceToPolygon(
    8513             :                 poGeom->clone()->toCurvePolygon())
    8514             :                 ->toPolygon());
    8515             :         return exportToSFCGALViaWKB(poPolygon.get());
    8516             :     }
    8517             :     else
    8518             :     {
    8519             :         // Default case - direct export
    8520             :         return exportToSFCGALViaWKB(poGeom);
    8521             :     }
    8522             : #else
    8523             :     char *buffer = nullptr;
    8524             : 
    8525             :     // special cases - LinearRing, Circular String, Compound Curve, Curve
    8526             :     // Polygon
    8527             : 
    8528             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8529             :     {
    8530             :         // cast it to LineString and get the WKT
    8531             :         std::unique_ptr<OGRLineString> poLS(
    8532             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8533             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8534             :         {
    8535             :             sfcgal_geometry_t *_geometry =
    8536             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8537             :             CPLFree(buffer);
    8538             :             return _geometry;
    8539             :         }
    8540             :         else
    8541             :         {
    8542             :             CPLFree(buffer);
    8543             :             return nullptr;
    8544             :         }
    8545             :     }
    8546             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8547             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8548             :     {
    8549             :         // convert it to LineString and get the WKT
    8550             :         std::unique_ptr<OGRLineString> poLS(
    8551             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8552             :                 ->toLineString());
    8553             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8554             :         {
    8555             :             sfcgal_geometry_t *_geometry =
    8556             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8557             :             CPLFree(buffer);
    8558             :             return _geometry;
    8559             :         }
    8560             :         else
    8561             :         {
    8562             :             CPLFree(buffer);
    8563             :             return nullptr;
    8564             :         }
    8565             :     }
    8566             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8567             :     {
    8568             :         // convert it to Polygon and get the WKT
    8569             :         std::unique_ptr<OGRPolygon> poPolygon(
    8570             :             OGRGeometryFactory::forceToPolygon(
    8571             :                 poGeom->clone()->toCurvePolygon())
    8572             :                 ->toPolygon());
    8573             :         if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
    8574             :         {
    8575             :             sfcgal_geometry_t *_geometry =
    8576             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8577             :             CPLFree(buffer);
    8578             :             return _geometry;
    8579             :         }
    8580             :         else
    8581             :         {
    8582             :             CPLFree(buffer);
    8583             :             return nullptr;
    8584             :         }
    8585             :     }
    8586             :     else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
    8587             :     {
    8588             :         sfcgal_geometry_t *_geometry =
    8589             :             sfcgal_io_read_wkt(buffer, strlen(buffer));
    8590             :         CPLFree(buffer);
    8591             :         return _geometry;
    8592             :     }
    8593             :     else
    8594             :     {
    8595             :         CPLFree(buffer);
    8596             :         return nullptr;
    8597             :     }
    8598             : #endif
    8599             : #else
    8600           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8601           0 :     return nullptr;
    8602             : #endif
    8603             : }
    8604             : 
    8605             : //! @endcond
    8606             : 
    8607             : /************************************************************************/
    8608             : /*                         SFCGALexportToOGR()                          */
    8609             : /************************************************************************/
    8610             : 
    8611             : //! @cond Doxygen_Suppress
    8612           0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
    8613             :     UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
    8614             : {
    8615             : #ifdef HAVE_SFCGAL
    8616             :     if (geometry == nullptr)
    8617             :         return nullptr;
    8618             : 
    8619             :     sfcgal_init();
    8620             :     char *pabySFCGAL = nullptr;
    8621             :     size_t nLength = 0;
    8622             : #if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8623             : 
    8624             :     sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
    8625             : 
    8626             :     if (pabySFCGAL == nullptr || nLength == 0)
    8627             :         return nullptr;
    8628             : 
    8629             :     OGRGeometry *poGeom = nullptr;
    8630             :     OGRErr eErr = OGRGeometryFactory::createFromWkb(
    8631             :         reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
    8632             :         nLength);
    8633             : 
    8634             :     free(pabySFCGAL);
    8635             : 
    8636             :     if (eErr == OGRERR_NONE)
    8637             :     {
    8638             :         return poGeom;
    8639             :     }
    8640             :     else
    8641             :     {
    8642             :         return nullptr;
    8643             :     }
    8644             : #else
    8645             :     sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
    8646             :     char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
    8647             :     memcpy(pszWKT, pabySFCGAL, nLength);
    8648             :     pszWKT[nLength] = 0;
    8649             :     free(pabySFCGAL);
    8650             : 
    8651             :     sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
    8652             : 
    8653             :     OGRGeometry *poGeom = nullptr;
    8654             :     if (geom_type == SFCGAL_TYPE_POINT)
    8655             :     {
    8656             :         poGeom = new OGRPoint();
    8657             :     }
    8658             :     else if (geom_type == SFCGAL_TYPE_LINESTRING)
    8659             :     {
    8660             :         poGeom = new OGRLineString();
    8661             :     }
    8662             :     else if (geom_type == SFCGAL_TYPE_POLYGON)
    8663             :     {
    8664             :         poGeom = new OGRPolygon();
    8665             :     }
    8666             :     else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
    8667             :     {
    8668             :         poGeom = new OGRMultiPoint();
    8669             :     }
    8670             :     else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
    8671             :     {
    8672             :         poGeom = new OGRMultiLineString();
    8673             :     }
    8674             :     else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
    8675             :     {
    8676             :         poGeom = new OGRMultiPolygon();
    8677             :     }
    8678             :     else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
    8679             :     {
    8680             :         poGeom = new OGRGeometryCollection();
    8681             :     }
    8682             :     else if (geom_type == SFCGAL_TYPE_TRIANGLE)
    8683             :     {
    8684             :         poGeom = new OGRTriangle();
    8685             :     }
    8686             :     else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
    8687             :     {
    8688             :         poGeom = new OGRPolyhedralSurface();
    8689             :     }
    8690             :     else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
    8691             :     {
    8692             :         poGeom = new OGRTriangulatedSurface();
    8693             :     }
    8694             :     else
    8695             :     {
    8696             :         CPLFree(pszWKT);
    8697             :         return nullptr;
    8698             :     }
    8699             : 
    8700             :     const char *pszWKTTmp = pszWKT;
    8701             :     if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
    8702             :     {
    8703             :         CPLFree(pszWKT);
    8704             :         return poGeom;
    8705             :     }
    8706             :     else
    8707             :     {
    8708             :         delete poGeom;
    8709             :         CPLFree(pszWKT);
    8710             :         return nullptr;
    8711             :     }
    8712             : #endif
    8713             : #else
    8714           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8715           0 :     return nullptr;
    8716             : #endif
    8717             : }
    8718             : 
    8719             : //! @endcond
    8720             : 
    8721             : //! @cond Doxygen_Suppress
    8722       18646 : OGRBoolean OGRGeometry::IsSFCGALCompatible() const
    8723             : {
    8724       18646 :     const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
    8725       18646 :     if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
    8726             :         eGType == wkbTIN)
    8727             :     {
    8728           2 :         return TRUE;
    8729             :     }
    8730       18644 :     if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
    8731             :     {
    8732          13 :         const OGRGeometryCollection *poGC = toGeometryCollection();
    8733          13 :         bool bIsSFCGALCompatible = false;
    8734          13 :         for (auto &&poSubGeom : *poGC)
    8735             :         {
    8736             :             OGRwkbGeometryType eSubGeomType =
    8737          13 :                 wkbFlatten(poSubGeom->getGeometryType());
    8738          13 :             if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
    8739             :             {
    8740           0 :                 bIsSFCGALCompatible = true;
    8741             :             }
    8742          13 :             else if (eSubGeomType != wkbMultiPolygon)
    8743             :             {
    8744          13 :                 bIsSFCGALCompatible = false;
    8745          13 :                 break;
    8746             :             }
    8747             :         }
    8748          13 :         return bIsSFCGALCompatible;
    8749             :     }
    8750       18631 :     return FALSE;
    8751             : }
    8752             : 
    8753             : //! @endcond
    8754             : 
    8755             : /************************************************************************/
    8756             : /*                      roundCoordinatesIEEE754()                       */
    8757             : /************************************************************************/
    8758             : 
    8759             : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
    8760             :  * double-precision binary representation.
    8761             :  *
    8762             :  * Determines the number of bits (N) required to represent a coordinate value
    8763             :  * with a specified number of digits after the decimal point, and then sets all
    8764             :  * but the N most significant bits to zero. The resulting coordinate value will
    8765             :  * still round to the original value (e.g. after roundCoordinates()), but will
    8766             :  * have improved compressiblity.
    8767             :  *
    8768             :  * @param options Contains the precision requirements.
    8769             :  * @since GDAL 3.9
    8770             :  */
    8771           1 : void OGRGeometry::roundCoordinatesIEEE754(
    8772             :     const OGRGeomCoordinateBinaryPrecision &options)
    8773             : {
    8774             :     struct Quantizer : public OGRDefaultGeometryVisitor
    8775             :     {
    8776             :         const OGRGeomCoordinateBinaryPrecision &m_options;
    8777             : 
    8778           1 :         explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
    8779           1 :             : m_options(optionsIn)
    8780             :         {
    8781           1 :         }
    8782             : 
    8783             :         using OGRDefaultGeometryVisitor::visit;
    8784             : 
    8785           3 :         void visit(OGRPoint *poPoint) override
    8786             :         {
    8787           3 :             if (m_options.nXYBitPrecision != INT_MIN)
    8788             :             {
    8789             :                 uint64_t i;
    8790             :                 double d;
    8791           3 :                 d = poPoint->getX();
    8792           3 :                 memcpy(&i, &d, sizeof(i));
    8793           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8794           3 :                 memcpy(&d, &i, sizeof(i));
    8795           3 :                 poPoint->setX(d);
    8796           3 :                 d = poPoint->getY();
    8797           3 :                 memcpy(&i, &d, sizeof(i));
    8798           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8799           3 :                 memcpy(&d, &i, sizeof(i));
    8800           3 :                 poPoint->setY(d);
    8801             :             }
    8802           3 :             if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
    8803             :             {
    8804             :                 uint64_t i;
    8805             :                 double d;
    8806           3 :                 d = poPoint->getZ();
    8807           3 :                 memcpy(&i, &d, sizeof(i));
    8808           3 :                 i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
    8809           3 :                 memcpy(&d, &i, sizeof(i));
    8810           3 :                 poPoint->setZ(d);
    8811             :             }
    8812           3 :             if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
    8813             :             {
    8814             :                 uint64_t i;
    8815             :                 double d;
    8816           3 :                 d = poPoint->getM();
    8817           3 :                 memcpy(&i, &d, sizeof(i));
    8818           3 :                 i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
    8819           3 :                 memcpy(&d, &i, sizeof(i));
    8820           3 :                 poPoint->setM(d);
    8821             :             }
    8822           3 :         }
    8823             :     };
    8824             : 
    8825           2 :     Quantizer quantizer(options);
    8826           1 :     accept(&quantizer);
    8827           1 : }
    8828             : 
    8829             : /************************************************************************/
    8830             : /*                               visit()                                */
    8831             : /************************************************************************/
    8832             : 
    8833         107 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
    8834             : {
    8835        1257 :     for (auto &&oPoint : *poGeom)
    8836             :     {
    8837        1150 :         oPoint.accept(this);
    8838             :     }
    8839         107 : }
    8840             : 
    8841         106 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
    8842             : {
    8843         106 :     _visit(poGeom);
    8844         106 : }
    8845             : 
    8846          82 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
    8847             : {
    8848          82 :     visit(poGeom->toUpperClass());
    8849          82 : }
    8850             : 
    8851           1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
    8852             : {
    8853           1 :     _visit(poGeom);
    8854           1 : }
    8855             : 
    8856          80 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
    8857             : {
    8858         163 :     for (auto &&poSubGeom : *poGeom)
    8859          83 :         poSubGeom->accept(this);
    8860          80 : }
    8861             : 
    8862          79 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
    8863             : {
    8864          79 :     visit(poGeom->toUpperClass());
    8865          79 : }
    8866             : 
    8867           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
    8868             : {
    8869           1 :     visit(poGeom->toUpperClass());
    8870           1 : }
    8871             : 
    8872           8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
    8873             : {
    8874           8 :     visit(poGeom->toUpperClass());
    8875           8 : }
    8876             : 
    8877          15 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
    8878             : {
    8879          15 :     visit(poGeom->toUpperClass());
    8880          15 : }
    8881             : 
    8882          27 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
    8883             : {
    8884          78 :     for (auto &&poSubGeom : *poGeom)
    8885          51 :         poSubGeom->accept(this);
    8886          27 : }
    8887             : 
    8888           1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
    8889             : {
    8890           2 :     for (auto &&poSubGeom : *poGeom)
    8891           1 :         poSubGeom->accept(this);
    8892           1 : }
    8893             : 
    8894           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
    8895             : {
    8896           1 :     visit(poGeom->toUpperClass());
    8897           1 : }
    8898             : 
    8899           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
    8900             : {
    8901           1 :     visit(poGeom->toUpperClass());
    8902           1 : }
    8903             : 
    8904           2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
    8905             : {
    8906           2 :     visit(poGeom->toUpperClass());
    8907           2 : }
    8908             : 
    8909           2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
    8910             : {
    8911           4 :     for (auto &&poSubGeom : *poGeom)
    8912           2 :         poSubGeom->accept(this);
    8913           2 : }
    8914             : 
    8915           1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
    8916             : {
    8917           1 :     visit(poGeom->toUpperClass());
    8918           1 : }
    8919             : 
    8920         127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
    8921             : {
    8922        2988 :     for (auto &&oPoint : *poGeom)
    8923             :     {
    8924        2861 :         oPoint.accept(this);
    8925             :     }
    8926         127 : }
    8927             : 
    8928         121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
    8929             : {
    8930         121 :     _visit(poGeom);
    8931         121 : }
    8932             : 
    8933         110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
    8934             : {
    8935         110 :     visit(poGeom->toUpperClass());
    8936         110 : }
    8937             : 
    8938           6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
    8939             : {
    8940           6 :     _visit(poGeom);
    8941           6 : }
    8942             : 
    8943         112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
    8944             : {
    8945         225 :     for (auto &&poSubGeom : *poGeom)
    8946         113 :         poSubGeom->accept(this);
    8947         112 : }
    8948             : 
    8949         109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
    8950             : {
    8951         109 :     visit(poGeom->toUpperClass());
    8952         109 : }
    8953             : 
    8954          64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
    8955             : {
    8956          64 :     visit(poGeom->toUpperClass());
    8957          64 : }
    8958             : 
    8959           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
    8960             : {
    8961           1 :     visit(poGeom->toUpperClass());
    8962           1 : }
    8963             : 
    8964           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
    8965             : {
    8966           1 :     visit(poGeom->toUpperClass());
    8967           1 : }
    8968             : 
    8969          69 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
    8970             : {
    8971         325 :     for (auto &&poSubGeom : *poGeom)
    8972         256 :         poSubGeom->accept(this);
    8973          69 : }
    8974             : 
    8975           3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
    8976             : {
    8977          14 :     for (auto &&poSubGeom : *poGeom)
    8978          11 :         poSubGeom->accept(this);
    8979           3 : }
    8980             : 
    8981           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
    8982             : {
    8983           1 :     visit(poGeom->toUpperClass());
    8984           1 : }
    8985             : 
    8986           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
    8987             : {
    8988           1 :     visit(poGeom->toUpperClass());
    8989           1 : }
    8990             : 
    8991           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
    8992             : {
    8993           2 :     visit(poGeom->toUpperClass());
    8994           2 : }
    8995             : 
    8996           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
    8997             : {
    8998           4 :     for (auto &&poSubGeom : *poGeom)
    8999           2 :         poSubGeom->accept(this);
    9000           2 : }
    9001             : 
    9002           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
    9003             : {
    9004           1 :     visit(poGeom->toUpperClass());
    9005           1 : }
    9006             : 
    9007             : /************************************************************************/
    9008             : /*                     OGRGeometryUniquePtrDeleter                      */
    9009             : /************************************************************************/
    9010             : 
    9011             : //! @cond Doxygen_Suppress
    9012        1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
    9013             : {
    9014        1333 :     delete poGeom;
    9015        1333 : }
    9016             : 
    9017             : //! @endcond
    9018             : 
    9019             : /************************************************************************/
    9020             : /*                 OGRPreparedGeometryUniquePtrDeleter                  */
    9021             : /************************************************************************/
    9022             : 
    9023             : //! @cond Doxygen_Suppress
    9024         145 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
    9025             :     OGRPreparedGeometry *poPreparedGeom) const
    9026             : {
    9027         145 :     OGRDestroyPreparedGeometry(poPreparedGeom);
    9028         145 : }
    9029             : 
    9030             : //! @endcond
    9031             : 
    9032             : /************************************************************************/
    9033             : /*                    HomogenizeDimensionalityWith()                    */
    9034             : /************************************************************************/
    9035             : 
    9036             : //! @cond Doxygen_Suppress
    9037     3358650 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
    9038             : {
    9039     3358650 :     if (poOtherGeom->Is3D() && !Is3D())
    9040     1328850 :         set3D(TRUE);
    9041             : 
    9042     3358650 :     if (poOtherGeom->IsMeasured() && !IsMeasured())
    9043         851 :         setMeasured(TRUE);
    9044             : 
    9045     3358650 :     if (!poOtherGeom->Is3D() && Is3D())
    9046         298 :         poOtherGeom->set3D(TRUE);
    9047             : 
    9048     3358650 :     if (!poOtherGeom->IsMeasured() && IsMeasured())
    9049          41 :         poOtherGeom->setMeasured(TRUE);
    9050     3358650 : }
    9051             : 
    9052             : //! @endcond
    9053             : 
    9054             : /************************************************************************/
    9055             : /*             OGRGeomCoordinateBinaryPrecision::SetFrom()              */
    9056             : /************************************************************************/
    9057             : 
    9058             : /** Set binary precision options from resolution.
    9059             :  *
    9060             :  * @since GDAL 3.9
    9061             :  */
    9062          16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
    9063             :     const OGRGeomCoordinatePrecision &prec)
    9064             : {
    9065          16 :     if (prec.dfXYResolution != 0)
    9066             :     {
    9067          16 :         nXYBitPrecision =
    9068          16 :             static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
    9069             :     }
    9070          16 :     if (prec.dfZResolution != 0)
    9071             :     {
    9072          12 :         nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
    9073             :     }
    9074          16 :     if (prec.dfMResolution != 0)
    9075             :     {
    9076          12 :         nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
    9077             :     }
    9078          16 : }
    9079             : 
    9080             : /************************************************************************/
    9081             : /*                     OGRwkbExportOptionsCreate()                      */
    9082             : /************************************************************************/
    9083             : 
    9084             : /**
    9085             :  * \brief Create geometry WKB export options.
    9086             :  *
    9087             :  * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
    9088             :  *
    9089             :  * @return object to be freed with OGRwkbExportOptionsDestroy().
    9090             :  * @since GDAL 3.9
    9091             :  */
    9092           2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
    9093             : {
    9094           2 :     return new OGRwkbExportOptions;
    9095             : }
    9096             : 
    9097             : /************************************************************************/
    9098             : /*                     OGRwkbExportOptionsDestroy()                     */
    9099             : /************************************************************************/
    9100             : 
    9101             : /**
    9102             :  * \brief Destroy object returned by OGRwkbExportOptionsCreate()
    9103             :  *
    9104             :  * @param psOptions WKB export options
    9105             :  * @since GDAL 3.9
    9106             :  */
    9107             : 
    9108           2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
    9109             : {
    9110           2 :     delete psOptions;
    9111           2 : }
    9112             : 
    9113             : /************************************************************************/
    9114             : /*                  OGRwkbExportOptionsSetByteOrder()                   */
    9115             : /************************************************************************/
    9116             : 
    9117             : /**
    9118             :  * \brief Set the WKB byte order.
    9119             :  *
    9120             :  * @param psOptions WKB export options
    9121             :  * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
    9122             :  * Intel)
    9123             :  * @since GDAL 3.9
    9124             :  */
    9125             : 
    9126           1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
    9127             :                                      OGRwkbByteOrder eByteOrder)
    9128             : {
    9129           1 :     psOptions->eByteOrder = eByteOrder;
    9130           1 : }
    9131             : 
    9132             : /************************************************************************/
    9133             : /*                   OGRwkbExportOptionsSetVariant()                    */
    9134             : /************************************************************************/
    9135             : 
    9136             : /**
    9137             :  * \brief Set the WKB variant
    9138             :  *
    9139             :  * @param psOptions WKB export options
    9140             :  * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
    9141             :  * wkbVariantPostGIS1
    9142             :  * @since GDAL 3.9
    9143             :  */
    9144             : 
    9145           1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
    9146             :                                    OGRwkbVariant eWkbVariant)
    9147             : {
    9148           1 :     psOptions->eWkbVariant = eWkbVariant;
    9149           1 : }
    9150             : 
    9151             : /************************************************************************/
    9152             : /*                  OGRwkbExportOptionsSetPrecision()                   */
    9153             : /************************************************************************/
    9154             : 
    9155             : /**
    9156             :  * \brief Set precision options
    9157             :  *
    9158             :  * @param psOptions WKB export options
    9159             :  * @param hPrecisionOptions Precision options (might be null to reset them)
    9160             :  * @since GDAL 3.9
    9161             :  */
    9162             : 
    9163           1 : void OGRwkbExportOptionsSetPrecision(
    9164             :     OGRwkbExportOptions *psOptions,
    9165             :     OGRGeomCoordinatePrecisionH hPrecisionOptions)
    9166             : {
    9167           1 :     psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
    9168           1 :     if (hPrecisionOptions)
    9169           1 :         psOptions->sPrecision.SetFrom(*hPrecisionOptions);
    9170           1 : }
    9171             : 
    9172             : /************************************************************************/
    9173             : /*                            IsRectangle()                             */
    9174             : /************************************************************************/
    9175             : 
    9176             : /**
    9177             :  * \brief Returns whether the geometry is a polygon with 4 corners forming
    9178             :  * a rectangle.
    9179             :  *
    9180             :  * @since GDAL 3.10
    9181             :  */
    9182       52571 : bool OGRGeometry::IsRectangle() const
    9183             : {
    9184       52571 :     if (wkbFlatten(getGeometryType()) != wkbPolygon)
    9185         353 :         return false;
    9186             : 
    9187       52218 :     const OGRPolygon *poPoly = toPolygon();
    9188             : 
    9189       52218 :     if (poPoly->getNumInteriorRings() != 0)
    9190          27 :         return false;
    9191             : 
    9192       52191 :     const OGRLinearRing *poRing = poPoly->getExteriorRing();
    9193       52191 :     if (!poRing)
    9194           4 :         return false;
    9195             : 
    9196       52187 :     if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
    9197         206 :         return false;
    9198             : 
    9199             :     // If the ring has 5 points, the last should be the first.
    9200      103905 :     if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
    9201       51924 :                                         poRing->getY(0) != poRing->getY(4)))
    9202           1 :         return false;
    9203             : 
    9204             :     // Polygon with first segment in "y" direction.
    9205      103294 :     if (poRing->getX(0) == poRing->getX(1) &&
    9206      102627 :         poRing->getY(1) == poRing->getY(2) &&
    9207      154607 :         poRing->getX(2) == poRing->getX(3) &&
    9208       51313 :         poRing->getY(3) == poRing->getY(0))
    9209       51313 :         return true;
    9210             : 
    9211             :     // Polygon with first segment in "x" direction.
    9212        1253 :     if (poRing->getY(0) == poRing->getY(1) &&
    9213        1172 :         poRing->getX(1) == poRing->getX(2) &&
    9214        1839 :         poRing->getY(2) == poRing->getY(3) &&
    9215         586 :         poRing->getX(3) == poRing->getX(0))
    9216         586 :         return true;
    9217             : 
    9218          81 :     return false;
    9219             : }
    9220             : 
    9221             : /************************************************************************/
    9222             : /*                           hasEmptyParts()                            */
    9223             : /************************************************************************/
    9224             : 
    9225             : /**
    9226             :  * \brief Returns whether a geometry has empty parts/rings.
    9227             :  *
    9228             :  * Returns true if removeEmptyParts() will modify the geometry.
    9229             :  *
    9230             :  * This is different from IsEmpty().
    9231             :  *
    9232             :  * @since GDAL 3.10
    9233             :  */
    9234         103 : bool OGRGeometry::hasEmptyParts() const
    9235             : {
    9236         103 :     return false;
    9237             : }
    9238             : 
    9239             : /************************************************************************/
    9240             : /*                          removeEmptyParts()                          */
    9241             : /************************************************************************/
    9242             : 
    9243             : /**
    9244             :  * \brief Remove empty parts/rings from this geometry.
    9245             :  *
    9246             :  * @since GDAL 3.10
    9247             :  */
    9248          17 : void OGRGeometry::removeEmptyParts()
    9249             : {
    9250          17 : }
    9251             : 
    9252             : /************************************************************************/
    9253             : /*                        ~IOGRGeometryVisitor()                        */
    9254             : /************************************************************************/
    9255             : 
    9256             : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
    9257             : 
    9258             : /************************************************************************/
    9259             : /*                     ~IOGRConstGeometryVisitor()                      */
    9260             : /************************************************************************/
    9261             : 
    9262             : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;

Generated by: LCOV version 1.14