LCOV - code coverage report
Current view: top level - ogr - ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1802 2116 85.2 %
Date: 2025-10-21 22:35:35 Functions: 216 240 90.0 %

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

Generated by: LCOV version 1.14