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

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements a few base methods on OGRGeometry.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_geometry.h"
      16             : 
      17             : #include <climits>
      18             : #include <cstdarg>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <limits>
      24             : #include <memory>
      25             : #include <optional>
      26             : #include <stdexcept>
      27             : #include <string>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_error_internal.h"
      32             : #include "cpl_multiproc.h"
      33             : #include "cpl_string.h"
      34             : #include "ogr_api.h"
      35             : #include "ogr_core.h"
      36             : #include "ogr_geos.h"
      37             : #include "ogr_sfcgal.h"
      38             : #include "ogr_libs.h"
      39             : #include "ogr_p.h"
      40             : #include "ogr_spatialref.h"
      41             : #include "ogr_srs_api.h"
      42             : #include "ogr_wkb.h"
      43             : 
      44             : #ifndef SFCGAL_MAKE_VERSION
      45             : #define SFCGAL_MAKE_VERSION(major, minor, patch)                               \
      46             :     ((major) * 10000 + (minor) * 100 + (patch))
      47             : #endif
      48             : #ifndef SFCGAL_VERSION_NUM
      49             : #define SFCGAL_VERSION_NUM                                                     \
      50             :     SFCGAL_MAKE_VERSION(SFCGAL_VERSION_MAJOR, SFCGAL_VERSION_MINOR,            \
      51             :                         SFCGAL_VERSION_PATCH)
      52             : #endif
      53             : 
      54             : //! @cond Doxygen_Suppress
      55             : int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE;
      56             : //! @endcond
      57             : 
      58             : #ifdef HAVE_GEOS
      59         103 : static void OGRGEOSErrorHandler(const char *fmt, ...)
      60             : {
      61             :     va_list args;
      62             : 
      63         103 :     va_start(args, fmt);
      64         103 :     CPLErrorV(CE_Failure, CPLE_AppDefined, fmt, args);
      65         103 :     va_end(args);
      66         103 : }
      67             : 
      68         111 : static void OGRGEOSWarningHandler(const char *fmt, ...)
      69             : {
      70             :     va_list args;
      71             : 
      72         111 :     va_start(args, fmt);
      73         111 :     CPLErrorV(CE_Warning, CPLE_AppDefined, fmt, args);
      74         111 :     va_end(args);
      75         111 : }
      76             : #endif
      77             : 
      78             : /************************************************************************/
      79             : /*                           OGRWktOptions()                            */
      80             : /************************************************************************/
      81             : 
      82       11230 : int OGRWktOptions::getDefaultPrecision()
      83             : {
      84       11230 :     return atoi(CPLGetConfigOption("OGR_WKT_PRECISION", "15"));
      85             : }
      86             : 
      87       11326 : bool OGRWktOptions::getDefaultRound()
      88             : {
      89       11326 :     return CPLTestBool(CPLGetConfigOption("OGR_WKT_ROUND", "TRUE"));
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                            OGRGeometry()                             */
      94             : /************************************************************************/
      95             : 
      96             : OGRGeometry::OGRGeometry() = default;
      97             : 
      98             : /************************************************************************/
      99             : /*                  OGRGeometry( const OGRGeometry& )                   */
     100             : /************************************************************************/
     101             : 
     102             : /**
     103             :  * \brief Copy constructor.
     104             :  */
     105             : 
     106     1785140 : OGRGeometry::OGRGeometry(const OGRGeometry &other)
     107     1785140 :     : poSRS(other.poSRS), flags(other.flags)
     108             : {
     109     1785140 :     if (poSRS != nullptr)
     110       75752 :         const_cast<OGRSpatialReference *>(poSRS)->Reference();
     111     1785140 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                     OGRGeometry( OGRGeometry&& )                     */
     115             : /************************************************************************/
     116             : 
     117             : /**
     118             :  * \brief Move constructor.
     119             :  *
     120             :  * @since GDAL 3.11
     121             :  */
     122             : 
     123      156286 : OGRGeometry::OGRGeometry(OGRGeometry &&other)
     124      156286 :     : poSRS(other.poSRS), flags(other.flags)
     125             : {
     126      156286 :     other.poSRS = nullptr;
     127      156286 : }
     128             : 
     129             : /************************************************************************/
     130             : /*                            ~OGRGeometry()                            */
     131             : /************************************************************************/
     132             : 
     133    25604900 : OGRGeometry::~OGRGeometry()
     134             : 
     135             : {
     136    12802400 :     if (poSRS != nullptr)
     137     3632470 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     138    12802400 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                    operator=( const OGRGeometry&)                    */
     142             : /************************************************************************/
     143             : 
     144             : /**
     145             :  * \brief Assignment operator.
     146             :  */
     147             : 
     148        1211 : OGRGeometry &OGRGeometry::operator=(const OGRGeometry &other)
     149             : {
     150        1211 :     if (this != &other)
     151             :     {
     152        1211 :         empty();
     153        1211 :         assignSpatialReference(other.getSpatialReference());
     154        1211 :         flags = other.flags;
     155             :     }
     156        1211 :     return *this;
     157             : }
     158             : 
     159             : /************************************************************************/
     160             : /*                      operator=( OGRGeometry&&)                       */
     161             : /************************************************************************/
     162             : 
     163             : /**
     164             :  * \brief Move assignment operator.
     165             :  *
     166             :  * @since GDAL 3.11
     167             :  */
     168             : 
     169      103914 : OGRGeometry &OGRGeometry::operator=(OGRGeometry &&other)
     170             : {
     171      103914 :     if (this != &other)
     172             :     {
     173      103914 :         poSRS = other.poSRS;
     174      103914 :         other.poSRS = nullptr;
     175      103914 :         flags = other.flags;
     176             :     }
     177      103914 :     return *this;
     178             : }
     179             : 
     180             : /************************************************************************/
     181             : /*                            dumpReadable()                            */
     182             : /************************************************************************/
     183             : 
     184             : /**
     185             :  * \brief Dump geometry in well known text format to indicated output file.
     186             :  *
     187             :  * A few options can be defined to change the default dump :
     188             :  * <ul>
     189             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     190             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     191             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     192             :  * </ul>
     193             :  *
     194             :  * This method is the same as the C function OGR_G_DumpReadable().
     195             :  *
     196             :  * @param fp the text file to write the geometry to.
     197             :  * @param pszPrefix the prefix to put on each line of output.
     198             :  * @param papszOptions NULL terminated list of options (may be NULL)
     199             :  */
     200             : 
     201           0 : void OGRGeometry::dumpReadable(FILE *fp, const char *pszPrefix,
     202             :                                CSLConstList papszOptions) const
     203             : 
     204             : {
     205           0 :     if (fp == nullptr)
     206           0 :         fp = stdout;
     207             : 
     208           0 :     const auto osStr = dumpReadable(pszPrefix, papszOptions);
     209           0 :     fprintf(fp, "%s", osStr.c_str());
     210           0 : }
     211             : 
     212             : /************************************************************************/
     213             : /*                            dumpReadable()                            */
     214             : /************************************************************************/
     215             : 
     216             : /**
     217             :  * \brief Dump geometry in well known text format to indicated output file.
     218             :  *
     219             :  * A few options can be defined to change the default dump :
     220             :  * <ul>
     221             :  * <li>DISPLAY_GEOMETRY=NO : to hide the dump of the geometry</li>
     222             :  * <li>DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT</li>
     223             :  * <li>DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry</li>
     224             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
     225             :  * in WKT (added in GDAL 3.9)</li>
     226             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates in
     227             :  * WKT (added in GDAL 3.9)</li>
     228             :  * </ul>
     229             :  *
     230             :  * @param pszPrefix the prefix to put on each line of output.
     231             :  * @param papszOptions NULL terminated list of options (may be NULL)
     232             :  * @return a string with the geometry representation.
     233             :  * @since GDAL 3.7
     234             :  */
     235             : 
     236         307 : std::string OGRGeometry::dumpReadable(const char *pszPrefix,
     237             :                                       CSLConstList papszOptions) const
     238             : 
     239             : {
     240         307 :     if (pszPrefix == nullptr)
     241         306 :         pszPrefix = "";
     242             : 
     243         307 :     std::string osRet;
     244             : 
     245             :     const auto exportToWktWithOpts =
     246        2044 :         [this, pszPrefix, papszOptions, &osRet](bool bIso)
     247             :     {
     248         292 :         OGRErr err(OGRERR_NONE);
     249         292 :         OGRWktOptions opts;
     250         292 :         if (const char *pszXYPrecision =
     251         292 :                 CSLFetchNameValue(papszOptions, "XY_COORD_PRECISION"))
     252             :         {
     253           1 :             opts.format = OGRWktFormat::F;
     254           1 :             opts.xyPrecision = atoi(pszXYPrecision);
     255             :         }
     256         292 :         if (const char *pszZPrecision =
     257         292 :                 CSLFetchNameValue(papszOptions, "Z_COORD_PRECISION"))
     258             :         {
     259           1 :             opts.format = OGRWktFormat::F;
     260           1 :             opts.zPrecision = atoi(pszZPrecision);
     261             :         }
     262         292 :         if (bIso)
     263         292 :             opts.variant = wkbVariantIso;
     264         584 :         std::string wkt = exportToWkt(opts, &err);
     265         292 :         if (err == OGRERR_NONE)
     266             :         {
     267         292 :             osRet = pszPrefix;
     268         292 :             osRet += wkt.data();
     269         292 :             osRet += '\n';
     270             :         }
     271         292 :     };
     272             : 
     273             :     const char *pszDisplayGeometry =
     274         307 :         CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY");
     275         307 :     if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "SUMMARY"))
     276             :     {
     277          15 :         osRet += CPLOPrintf("%s%s : ", pszPrefix, getGeometryName());
     278          15 :         switch (getGeometryType())
     279             :         {
     280           1 :             case wkbUnknown:
     281             :             case wkbNone:
     282             :             case wkbPoint:
     283             :             case wkbPoint25D:
     284             :             case wkbPointM:
     285             :             case wkbPointZM:
     286           1 :                 break;
     287           0 :             case wkbPolyhedralSurface:
     288             :             case wkbTIN:
     289             :             case wkbPolyhedralSurfaceZ:
     290             :             case wkbTINZ:
     291             :             case wkbPolyhedralSurfaceM:
     292             :             case wkbTINM:
     293             :             case wkbPolyhedralSurfaceZM:
     294             :             case wkbTINZM:
     295             :             {
     296           0 :                 const OGRPolyhedralSurface *poPS = toPolyhedralSurface();
     297             :                 osRet +=
     298           0 :                     CPLOPrintf("%d geometries:\n", poPS->getNumGeometries());
     299           0 :                 for (auto &&poSubGeom : *poPS)
     300             :                 {
     301           0 :                     osRet += pszPrefix;
     302           0 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     303             :                 }
     304           0 :                 break;
     305             :             }
     306           0 :             case wkbLineString:
     307             :             case wkbLineString25D:
     308             :             case wkbLineStringM:
     309             :             case wkbLineStringZM:
     310             :             case wkbCircularString:
     311             :             case wkbCircularStringZ:
     312             :             case wkbCircularStringM:
     313             :             case wkbCircularStringZM:
     314             :             {
     315           0 :                 const OGRSimpleCurve *poSC = toSimpleCurve();
     316           0 :                 osRet += CPLOPrintf("%d points\n", poSC->getNumPoints());
     317           0 :                 break;
     318             :             }
     319          11 :             case wkbPolygon:
     320             :             case wkbTriangle:
     321             :             case wkbTriangleZ:
     322             :             case wkbTriangleM:
     323             :             case wkbTriangleZM:
     324             :             case wkbPolygon25D:
     325             :             case wkbPolygonM:
     326             :             case wkbPolygonZM:
     327             :             case wkbCurvePolygon:
     328             :             case wkbCurvePolygonZ:
     329             :             case wkbCurvePolygonM:
     330             :             case wkbCurvePolygonZM:
     331             :             {
     332          11 :                 const OGRCurvePolygon *poPoly = toCurvePolygon();
     333          11 :                 const OGRCurve *poRing = poPoly->getExteriorRingCurve();
     334          11 :                 const int nRings = poPoly->getNumInteriorRings();
     335          11 :                 if (poRing == nullptr)
     336             :                 {
     337           0 :                     osRet += "empty";
     338             :                 }
     339             :                 else
     340             :                 {
     341          11 :                     osRet += CPLOPrintf("%d points", poRing->getNumPoints());
     342          11 :                     if (wkbFlatten(poRing->getGeometryType()) ==
     343             :                         wkbCompoundCurve)
     344             :                     {
     345           0 :                         osRet += " (";
     346           0 :                         osRet += poRing->dumpReadable(nullptr, papszOptions);
     347           0 :                         osRet += ")";
     348             :                     }
     349          11 :                     if (nRings)
     350             :                     {
     351           1 :                         osRet += CPLOPrintf(", %d inner rings (", nRings);
     352           8 :                         for (int ir = 0; ir < nRings; ir++)
     353             :                         {
     354           7 :                             poRing = poPoly->getInteriorRingCurve(ir);
     355           7 :                             if (ir)
     356           6 :                                 osRet += ", ";
     357             :                             osRet +=
     358           7 :                                 CPLOPrintf("%d points", poRing->getNumPoints());
     359           7 :                             if (wkbFlatten(poRing->getGeometryType()) ==
     360             :                                 wkbCompoundCurve)
     361             :                             {
     362           2 :                                 osRet += " (";
     363             :                                 osRet +=
     364           2 :                                     poRing->dumpReadable(nullptr, papszOptions);
     365           2 :                                 osRet += ")";
     366             :                             }
     367             :                         }
     368           1 :                         osRet += ")";
     369             :                     }
     370             :                 }
     371          11 :                 osRet += "\n";
     372          11 :                 break;
     373             :             }
     374           2 :             case wkbCompoundCurve:
     375             :             case wkbCompoundCurveZ:
     376             :             case wkbCompoundCurveM:
     377             :             case wkbCompoundCurveZM:
     378             :             {
     379           2 :                 const OGRCompoundCurve *poCC = toCompoundCurve();
     380           2 :                 if (poCC->getNumCurves() == 0)
     381             :                 {
     382           0 :                     osRet += "empty";
     383             :                 }
     384             :                 else
     385             :                 {
     386           6 :                     for (int i = 0; i < poCC->getNumCurves(); i++)
     387             :                     {
     388           4 :                         if (i)
     389           2 :                             osRet += ", ";
     390             :                         osRet +=
     391           8 :                             CPLOPrintf("%s (%d points)",
     392           4 :                                        poCC->getCurve(i)->getGeometryName(),
     393           8 :                                        poCC->getCurve(i)->getNumPoints());
     394             :                     }
     395             :                 }
     396           2 :                 break;
     397             :             }
     398             : 
     399           1 :             case wkbMultiPoint:
     400             :             case wkbMultiLineString:
     401             :             case wkbMultiPolygon:
     402             :             case wkbMultiCurve:
     403             :             case wkbMultiSurface:
     404             :             case wkbGeometryCollection:
     405             :             case wkbMultiPoint25D:
     406             :             case wkbMultiLineString25D:
     407             :             case wkbMultiPolygon25D:
     408             :             case wkbMultiCurveZ:
     409             :             case wkbMultiSurfaceZ:
     410             :             case wkbGeometryCollection25D:
     411             :             case wkbMultiPointM:
     412             :             case wkbMultiLineStringM:
     413             :             case wkbMultiPolygonM:
     414             :             case wkbMultiCurveM:
     415             :             case wkbMultiSurfaceM:
     416             :             case wkbGeometryCollectionM:
     417             :             case wkbMultiPointZM:
     418             :             case wkbMultiLineStringZM:
     419             :             case wkbMultiPolygonZM:
     420             :             case wkbMultiCurveZM:
     421             :             case wkbMultiSurfaceZM:
     422             :             case wkbGeometryCollectionZM:
     423             :             {
     424           1 :                 const OGRGeometryCollection *poColl = toGeometryCollection();
     425             :                 osRet +=
     426           1 :                     CPLOPrintf("%d geometries:\n", poColl->getNumGeometries());
     427           2 :                 for (auto &&poSubGeom : *poColl)
     428             :                 {
     429           1 :                     osRet += pszPrefix;
     430           1 :                     osRet += poSubGeom->dumpReadable(pszPrefix, papszOptions);
     431             :                 }
     432           1 :                 break;
     433             :             }
     434           0 :             case wkbLinearRing:
     435             :             case wkbCurve:
     436             :             case wkbSurface:
     437             :             case wkbCurveZ:
     438             :             case wkbSurfaceZ:
     439             :             case wkbCurveM:
     440             :             case wkbSurfaceM:
     441             :             case wkbCurveZM:
     442             :             case wkbSurfaceZM:
     443           0 :                 break;
     444          15 :         }
     445             :     }
     446         292 :     else if (pszDisplayGeometry != nullptr && EQUAL(pszDisplayGeometry, "WKT"))
     447             :     {
     448           0 :         exportToWktWithOpts(/* bIso=*/false);
     449             :     }
     450         292 :     else if (pszDisplayGeometry == nullptr || CPLTestBool(pszDisplayGeometry) ||
     451           0 :              EQUAL(pszDisplayGeometry, "ISO_WKT"))
     452             :     {
     453         292 :         exportToWktWithOpts(/* bIso=*/true);
     454             :     }
     455             : 
     456         614 :     return osRet;
     457             : }
     458             : 
     459             : /************************************************************************/
     460             : /*                         OGR_G_DumpReadable()                         */
     461             : /************************************************************************/
     462             : /**
     463             :  * \brief Dump geometry in well known text format to indicated output file.
     464             :  *
     465             :  * This method is the same as the CPP method OGRGeometry::dumpReadable.
     466             :  *
     467             :  * @param hGeom handle on the geometry to dump.
     468             :  * @param fp the text file to write the geometry to.
     469             :  * @param pszPrefix the prefix to put on each line of output.
     470             :  */
     471             : 
     472           0 : void OGR_G_DumpReadable(OGRGeometryH hGeom, FILE *fp, const char *pszPrefix)
     473             : 
     474             : {
     475           0 :     VALIDATE_POINTER0(hGeom, "OGR_G_DumpReadable");
     476             : 
     477           0 :     OGRGeometry::FromHandle(hGeom)->dumpReadable(fp, pszPrefix);
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                       assignSpatialReference()                       */
     482             : /************************************************************************/
     483             : 
     484             : /**
     485             :  * \brief Assign spatial reference to this object.
     486             :  *
     487             :  * Any existing spatial reference
     488             :  * is replaced, but under no circumstances does this result in the object
     489             :  * being reprojected.  It is just changing the interpretation of the existing
     490             :  * geometry.  Note that assigning a spatial reference increments the
     491             :  * reference count on the OGRSpatialReference, but does not copy it.
     492             :  *
     493             :  * This will also assign the spatial reference to
     494             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     495             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     496             :  * derived classes).
     497             :  *
     498             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     499             :  *
     500             :  * This method is the same as the C function OGR_G_AssignSpatialReference().
     501             :  *
     502             :  * @param poSR new spatial reference system to apply.
     503             :  */
     504             : 
     505     5608960 : void OGRGeometry::assignSpatialReference(const OGRSpatialReference *poSR)
     506             : 
     507             : {
     508             :     // Do in that order to properly handle poSR == poSRS
     509     5608960 :     if (poSR != nullptr)
     510     3597650 :         const_cast<OGRSpatialReference *>(poSR)->Reference();
     511     5608960 :     if (poSRS != nullptr)
     512       40926 :         const_cast<OGRSpatialReference *>(poSRS)->Release();
     513             : 
     514     5608960 :     poSRS = poSR;
     515     5608960 : }
     516             : 
     517             : /************************************************************************/
     518             : /*                    OGR_G_AssignSpatialReference()                    */
     519             : /************************************************************************/
     520             : /**
     521             :  * \brief Assign spatial reference to this object.
     522             :  *
     523             :  * Any existing spatial reference
     524             :  * is replaced, but under no circumstances does this result in the object
     525             :  * being reprojected.  It is just changing the interpretation of the existing
     526             :  * geometry.  Note that assigning a spatial reference increments the
     527             :  * reference count on the OGRSpatialReference, but does not copy it.
     528             :  *
     529             :  * This will also assign the spatial reference to
     530             :  * potential sub-geometries of the geometry (OGRGeometryCollection,
     531             :  * OGRCurvePolygon/OGRPolygon, OGRCompoundCurve, OGRPolyhedralSurface and their
     532             :  * derived classes).
     533             :  *
     534             :  * This is similar to the SFCOM IGeometry::put_SpatialReference() method.
     535             :  *
     536             :  * This function is the same as the CPP method
     537             :  * OGRGeometry::assignSpatialReference.
     538             :  *
     539             :  * @param hGeom handle on the geometry to apply the new spatial reference
     540             :  * system.
     541             :  * @param hSRS handle on the new spatial reference system to apply.
     542             :  */
     543             : 
     544          80 : void OGR_G_AssignSpatialReference(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     545             : 
     546             : {
     547          80 :     VALIDATE_POINTER0(hGeom, "OGR_G_AssignSpatialReference");
     548             : 
     549         160 :     OGRGeometry::FromHandle(hGeom)->assignSpatialReference(
     550          80 :         OGRSpatialReference::FromHandle(hSRS));
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                             Intersects()                             */
     555             : /************************************************************************/
     556             : 
     557             : /**
     558             :  * \brief Do these features intersect?
     559             :  *
     560             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     561             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     562             :  * envelopes (bounding boxes) of the two geometries overlap.
     563             :  *
     564             :  * The poOtherGeom argument may be safely NULL, but in this case the method
     565             :  * will always return TRUE.   That is, a NULL geometry is treated as being
     566             :  * everywhere.
     567             :  *
     568             :  * This method is the same as the C function OGR_G_Intersects().
     569             :  *
     570             :  * @param poOtherGeom the other geometry to test against.
     571             :  *
     572             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     573             :  */
     574             : 
     575          44 : bool OGRGeometry::Intersects(const OGRGeometry *poOtherGeom) const
     576             : 
     577             : {
     578          44 :     if (poOtherGeom == nullptr)
     579           0 :         return TRUE;
     580             : 
     581          44 :     OGREnvelope oEnv1;
     582          44 :     getEnvelope(&oEnv1);
     583             : 
     584          44 :     OGREnvelope oEnv2;
     585          44 :     poOtherGeom->getEnvelope(&oEnv2);
     586             : 
     587          44 :     if (oEnv1.MaxX < oEnv2.MinX || oEnv1.MaxY < oEnv2.MinY ||
     588          26 :         oEnv2.MaxX < oEnv1.MinX || oEnv2.MaxY < oEnv1.MinY)
     589          18 :         return FALSE;
     590             : 
     591             : #ifndef HAVE_GEOS
     592             :     // Without GEOS we assume that envelope overlap is equivalent to
     593             :     // actual intersection.
     594             :     return TRUE;
     595             : #else
     596             : 
     597          26 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
     598          26 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
     599          26 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
     600             : 
     601          26 :     bool bResult = false;
     602          26 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
     603             :     {
     604          26 :         bResult =
     605          26 :             GEOSIntersects_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
     606             :     }
     607             : 
     608          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
     609          26 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
     610          26 :     freeGEOSContext(hGEOSCtxt);
     611             : 
     612          26 :     return bResult;
     613             : #endif  // HAVE_GEOS
     614             : }
     615             : 
     616             : // Old API compatibility function.
     617             : 
     618             : //! @cond Doxygen_Suppress
     619           0 : bool OGRGeometry::Intersect(OGRGeometry *poOtherGeom) const
     620             : 
     621             : {
     622           0 :     return Intersects(poOtherGeom);
     623             : }
     624             : 
     625             : //! @endcond
     626             : 
     627             : /************************************************************************/
     628             : /*                          OGR_G_Intersects()                          */
     629             : /************************************************************************/
     630             : /**
     631             :  * \brief Do these features intersect?
     632             :  *
     633             :  * Determines whether two geometries intersect.  If GEOS is enabled, then
     634             :  * this is done in rigorous fashion otherwise TRUE is returned if the
     635             :  * envelopes (bounding boxes) of the two geometries overlap.
     636             :  *
     637             :  * This function is the same as the CPP method OGRGeometry::Intersects.
     638             :  *
     639             :  * @param hGeom handle on the first geometry.
     640             :  * @param hOtherGeom handle on the other geometry to test against.
     641             :  *
     642             :  * @return TRUE if the geometries intersect, otherwise FALSE.
     643             :  */
     644             : 
     645          11 : int OGR_G_Intersects(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     646             : 
     647             : {
     648          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersects", FALSE);
     649          11 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersects", FALSE);
     650             : 
     651          22 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     652          22 :         OGRGeometry::FromHandle(hOtherGeom));
     653             : }
     654             : 
     655             : //! @cond Doxygen_Suppress
     656           0 : int OGR_G_Intersect(OGRGeometryH hGeom, OGRGeometryH hOtherGeom)
     657             : 
     658             : {
     659           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_Intersect", FALSE);
     660           0 :     VALIDATE_POINTER1(hOtherGeom, "OGR_G_Intersect", FALSE);
     661             : 
     662           0 :     return OGRGeometry::FromHandle(hGeom)->Intersects(
     663           0 :         OGRGeometry::FromHandle(hOtherGeom));
     664             : }
     665             : 
     666             : //! @endcond
     667             : 
     668             : /************************************************************************/
     669             : /*                            transformTo()                             */
     670             : /************************************************************************/
     671             : 
     672             : /**
     673             :  * \brief Transform geometry to new spatial reference system.
     674             :  *
     675             :  * This method will transform the coordinates of a geometry from
     676             :  * their current spatial reference system to a new target spatial
     677             :  * reference system.  Normally this means reprojecting the vectors,
     678             :  * but it could include datum shifts, and changes of units.
     679             :  *
     680             :  * This method will only work if the geometry already has an assigned
     681             :  * spatial reference system, and if it is transformable to the target
     682             :  * coordinate system.
     683             :  *
     684             :  * Because this method requires internal creation and initialization of an
     685             :  * OGRCoordinateTransformation object it is significantly more expensive to
     686             :  * use this method to transform many geometries than it is to create the
     687             :  * OGRCoordinateTransformation in advance, and call transform() with that
     688             :  * transformation.  This method exists primarily for convenience when only
     689             :  * transforming a single geometry.
     690             :  *
     691             :  * This method is the same as the C function OGR_G_TransformTo().
     692             :  *
     693             :  * @param poSR spatial reference system to transform to.
     694             :  *
     695             :  * @return OGRERR_NONE on success, or an error code.
     696             :  */
     697             : 
     698          33 : OGRErr OGRGeometry::transformTo(const OGRSpatialReference *poSR)
     699             : 
     700             : {
     701          33 :     if (getSpatialReference() == nullptr)
     702             :     {
     703           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Geometry has no SRS");
     704           1 :         return OGRERR_FAILURE;
     705             :     }
     706             : 
     707          32 :     if (poSR == nullptr)
     708             :     {
     709           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Target SRS is NULL");
     710           0 :         return OGRERR_FAILURE;
     711             :     }
     712             : 
     713             :     OGRCoordinateTransformation *poCT =
     714          32 :         OGRCreateCoordinateTransformation(getSpatialReference(), poSR);
     715          32 :     if (poCT == nullptr)
     716           0 :         return OGRERR_FAILURE;
     717             : 
     718          32 :     const OGRErr eErr = transform(poCT);
     719             : 
     720          32 :     delete poCT;
     721             : 
     722          32 :     return eErr;
     723             : }
     724             : 
     725             : /************************************************************************/
     726             : /*                         OGR_G_TransformTo()                          */
     727             : /************************************************************************/
     728             : /**
     729             :  * \brief Transform geometry to new spatial reference system.
     730             :  *
     731             :  * This function will transform the coordinates of a geometry from
     732             :  * their current spatial reference system to a new target spatial
     733             :  * reference system.  Normally this means reprojecting the vectors,
     734             :  * but it could include datum shifts, and changes of units.
     735             :  *
     736             :  * This function will only work if the geometry already has an assigned
     737             :  * spatial reference system, and if it is transformable to the target
     738             :  * coordinate system.
     739             :  *
     740             :  * Because this function requires internal creation and initialization of an
     741             :  * OGRCoordinateTransformation object it is significantly more expensive to
     742             :  * use this function to transform many geometries than it is to create the
     743             :  * OGRCoordinateTransformation in advance, and call transform() with that
     744             :  * transformation.  This function exists primarily for convenience when only
     745             :  * transforming a single geometry.
     746             :  *
     747             :  * This function is the same as the CPP method OGRGeometry::transformTo.
     748             :  *
     749             :  * @param hGeom handle on the geometry to apply the transform to.
     750             :  * @param hSRS handle on the spatial reference system to apply.
     751             :  *
     752             :  * @return OGRERR_NONE on success, or an error code.
     753             :  */
     754             : 
     755           9 : OGRErr OGR_G_TransformTo(OGRGeometryH hGeom, OGRSpatialReferenceH hSRS)
     756             : 
     757             : {
     758           9 :     VALIDATE_POINTER1(hGeom, "OGR_G_TransformTo", OGRERR_FAILURE);
     759             : 
     760          18 :     return OGRGeometry::FromHandle(hGeom)->transformTo(
     761          18 :         OGRSpatialReference::FromHandle(hSRS));
     762             : }
     763             : 
     764             : /**
     765             :  * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT );
     766             :  *
     767             :  * \brief Apply arbitrary coordinate transformation to geometry.
     768             :  *
     769             :  * This method will transform the coordinates of a geometry from
     770             :  * their current spatial reference system to a new target spatial
     771             :  * reference system.  Normally this means reprojecting the vectors,
     772             :  * but it could include datum shifts, and changes of units.
     773             :  *
     774             :  * Note that this method does not require that the geometry already
     775             :  * have a spatial reference system.  It will be assumed that they can
     776             :  * be treated as having the source spatial reference system of the
     777             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     778             :  * will be ignored.  On successful completion the output OGRSpatialReference
     779             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     780             :  *
     781             :  * This method only does reprojection on a point-by-point basis. It does not
     782             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     783             :  * For that, use the OGRGeometryFactory::transformWithOptions() method.
     784             :  *
     785             :  * This method is the same as the C function OGR_G_Transform().
     786             :  *
     787             :  * @param poCT the transformation to apply.
     788             :  *
     789             :  * @return OGRERR_NONE on success or an error code.
     790             :  */
     791             : 
     792             : /************************************************************************/
     793             : /*                          OGR_G_Transform()                           */
     794             : /************************************************************************/
     795             : /**
     796             :  * \brief Apply arbitrary coordinate transformation to geometry.
     797             :  *
     798             :  * This function will transform the coordinates of a geometry from
     799             :  * their current spatial reference system to a new target spatial
     800             :  * reference system.  Normally this means reprojecting the vectors,
     801             :  * but it could include datum shifts, and changes of units.
     802             :  *
     803             :  * Note that this function does not require that the geometry already
     804             :  * have a spatial reference system.  It will be assumed that they can
     805             :  * be treated as having the source spatial reference system of the
     806             :  * OGRCoordinateTransformation object, and the actual SRS of the geometry
     807             :  * will be ignored.  On successful completion the output OGRSpatialReference
     808             :  * of the OGRCoordinateTransformation will be assigned to the geometry.
     809             :  *
     810             :  * This function only does reprojection on a point-by-point basis. It does not
     811             :  * include advanced logic to deal with discontinuities at poles or antimeridian.
     812             :  * For that, use the OGR_GeomTransformer_Create() and
     813             :  * OGR_GeomTransformer_Transform() functions.
     814             :  *
     815             :  * This function is the same as the CPP method OGRGeometry::transform.
     816             :  *
     817             :  * @param hGeom handle on the geometry to apply the transform to.
     818             :  * @param hTransform handle on the transformation to apply.
     819             :  *
     820             :  * @return OGRERR_NONE on success or an error code.
     821             :  */
     822             : 
     823          11 : OGRErr OGR_G_Transform(OGRGeometryH hGeom,
     824             :                        OGRCoordinateTransformationH hTransform)
     825             : 
     826             : {
     827          11 :     VALIDATE_POINTER1(hGeom, "OGR_G_Transform", OGRERR_FAILURE);
     828             : 
     829          22 :     return OGRGeometry::FromHandle(hGeom)->transform(
     830          11 :         OGRCoordinateTransformation::FromHandle(hTransform));
     831             : }
     832             : 
     833             : /**
     834             :  * \fn int OGRGeometry::getDimension() const;
     835             :  *
     836             :  * \brief Get the dimension of this object.
     837             :  *
     838             :  * This method corresponds to the SFCOM IGeometry::GetDimension() method.
     839             :  * It indicates the dimension of the object, but does not indicate the
     840             :  * dimension of the underlying space (as indicated by
     841             :  * OGRGeometry::getCoordinateDimension()).
     842             :  *
     843             :  * This method is the same as the C function OGR_G_GetDimension().
     844             :  *
     845             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     846             :  */
     847             : 
     848             : /**
     849             :  * \brief Get the geometry type that conforms with ISO SQL/MM Part3
     850             :  *
     851             :  * @return the geometry type that conforms with ISO SQL/MM Part3
     852             :  */
     853      720652 : OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const
     854             : {
     855      720652 :     OGRwkbGeometryType nGType = wkbFlatten(getGeometryType());
     856             : 
     857      720652 :     if (flags & OGR_G_3D)
     858      214269 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 1000);
     859      720652 :     if (flags & OGR_G_MEASURED)
     860       26025 :         nGType = static_cast<OGRwkbGeometryType>(nGType + 2000);
     861             : 
     862      720652 :     return nGType;
     863             : }
     864             : 
     865             : /************************************************************************/
     866             : /*                      OGRGeometry::segmentize()                       */
     867             : /************************************************************************/
     868             : /**
     869             :  *
     870             :  * \brief Modify the geometry such it has no segment longer then the
     871             :  * given distance.
     872             :  *
     873             :  * This method modifies the geometry to add intermediate vertices if necessary
     874             :  * so that the maximum length between 2 consecutive vertices is lower than
     875             :  * dfMaxLength.
     876             :  *
     877             :  * Interpolated points will have Z and M values (if needed) set to 0.
     878             :  * Distance computation is performed in 2d only
     879             :  *
     880             :  * This function is the same as the C function OGR_G_Segmentize()
     881             :  *
     882             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     883             :  * @return (since 3.10) true in case of success, false in case of error.
     884             :  */
     885             : 
     886           0 : bool OGRGeometry::segmentize(CPL_UNUSED double dfMaxLength)
     887             : {
     888             :     // Do nothing.
     889           0 :     return true;
     890             : }
     891             : 
     892             : /************************************************************************/
     893             : /*                          OGR_G_Segmentize()                          */
     894             : /************************************************************************/
     895             : 
     896             : /**
     897             :  *
     898             :  * \brief Modify the geometry such it has no segment longer then the given
     899             :  * distance.
     900             :  *
     901             :  * Interpolated points will have Z and M values (if needed) set to 0.
     902             :  * Distance computation is performed in 2d only.
     903             :  *
     904             :  * This function is the same as the CPP method OGRGeometry::segmentize().
     905             :  *
     906             :  * @param hGeom handle on the geometry to segmentize
     907             :  * @param dfMaxLength the maximum distance between 2 points after segmentization
     908             :  */
     909             : 
     910          24 : void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength)
     911             : {
     912          24 :     VALIDATE_POINTER0(hGeom, "OGR_G_Segmentize");
     913             : 
     914          24 :     if (dfMaxLength <= 0)
     915             :     {
     916           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     917             :                  "dfMaxLength must be strictly positive");
     918           0 :         return;
     919             :     }
     920          24 :     OGRGeometry::FromHandle(hGeom)->segmentize(dfMaxLength);
     921             : }
     922             : 
     923             : /************************************************************************/
     924             : /*                         OGR_G_GetDimension()                         */
     925             : /************************************************************************/
     926             : /**
     927             :  *
     928             :  * \brief Get the dimension of this geometry.
     929             :  *
     930             :  * This function corresponds to the SFCOM IGeometry::GetDimension() method.
     931             :  * It indicates the dimension of the geometry, but does not indicate the
     932             :  * dimension of the underlying space (as indicated by
     933             :  * OGR_G_GetCoordinateDimension() function).
     934             :  *
     935             :  * This function is the same as the CPP method OGRGeometry::getDimension().
     936             :  *
     937             :  * @param hGeom handle on the geometry to get the dimension from.
     938             :  * @return 0 for points, 1 for lines and 2 for surfaces.
     939             :  */
     940             : 
     941          21 : int OGR_G_GetDimension(OGRGeometryH hGeom)
     942             : 
     943             : {
     944          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetDimension", 0);
     945             : 
     946          21 :     return OGRGeometry::FromHandle(hGeom)->getDimension();
     947             : }
     948             : 
     949             : /************************************************************************/
     950             : /*                       getCoordinateDimension()                       */
     951             : /************************************************************************/
     952             : /**
     953             :  * \brief Get the dimension of the coordinates in this object.
     954             :  *
     955             :  * This method is the same as the C function OGR_G_GetCoordinateDimension().
     956             :  *
     957             :  * @deprecated use CoordinateDimension().
     958             :  *
     959             :  * @return this will return 2 or 3.
     960             :  */
     961             : 
     962      573474 : int OGRGeometry::getCoordinateDimension() const
     963             : 
     964             : {
     965      573474 :     return (flags & OGR_G_3D) ? 3 : 2;
     966             : }
     967             : 
     968             : /************************************************************************/
     969             : /*                        CoordinateDimension()                         */
     970             : /************************************************************************/
     971             : /**
     972             :  * \brief Get the dimension of the coordinates in this object.
     973             :  *
     974             :  * This method is the same as the C function OGR_G_CoordinateDimension().
     975             :  *
     976             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
     977             :  *
     978             :  */
     979             : 
     980       30434 : int OGRGeometry::CoordinateDimension() const
     981             : 
     982             : {
     983       30434 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     984        7375 :         return 4;
     985       23059 :     else if ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED))
     986        6869 :         return 3;
     987             :     else
     988       16190 :         return 2;
     989             : }
     990             : 
     991             : /************************************************************************/
     992             : /*                    OGR_G_GetCoordinateDimension()                    */
     993             : /************************************************************************/
     994             : /**
     995             :  *
     996             :  * \brief Get the dimension of the coordinates in this geometry.
     997             :  *
     998             :  * This function is the same as the CPP method
     999             :  * OGRGeometry::getCoordinateDimension().
    1000             :  *
    1001             :  * @param hGeom handle on the geometry to get the dimension of the
    1002             :  * coordinates from.
    1003             :  *
    1004             :  * @deprecated use OGR_G_CoordinateDimension(), OGR_G_Is3D(), or
    1005             :  * OGR_G_IsMeasured().
    1006             :  *
    1007             :  * @return this will return 2 or 3.
    1008             :  */
    1009             : 
    1010         724 : int OGR_G_GetCoordinateDimension(OGRGeometryH hGeom)
    1011             : 
    1012             : {
    1013         724 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetCoordinateDimension", 0);
    1014             : 
    1015         724 :     return OGRGeometry::FromHandle(hGeom)->getCoordinateDimension();
    1016             : }
    1017             : 
    1018             : /************************************************************************/
    1019             : /*                     OGR_G_CoordinateDimension()                      */
    1020             : /************************************************************************/
    1021             : /**
    1022             :  *
    1023             :  * \brief Get the dimension of the coordinates in this geometry.
    1024             :  *
    1025             :  * This function is the same as the CPP method
    1026             :  * OGRGeometry::CoordinateDimension().
    1027             :  *
    1028             :  * @param hGeom handle on the geometry to get the dimension of the
    1029             :  * coordinates from.
    1030             :  *
    1031             :  * @return this will return 2 for XY, 3 for XYZ and XYM, and 4 for XYZM data.
    1032             :  *
    1033             :  */
    1034             : 
    1035           4 : int OGR_G_CoordinateDimension(OGRGeometryH hGeom)
    1036             : 
    1037             : {
    1038           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_CoordinateDimension", 0);
    1039             : 
    1040           4 :     return OGRGeometry::FromHandle(hGeom)->CoordinateDimension();
    1041             : }
    1042             : 
    1043             : /**
    1044             :  *
    1045             :  * \brief See whether this geometry has Z coordinates.
    1046             :  *
    1047             :  * This function is the same as the CPP method
    1048             :  * OGRGeometry::Is3D().
    1049             :  *
    1050             :  * @param hGeom handle on the geometry to check whether it has Z coordinates.
    1051             :  *
    1052             :  * @return TRUE if the geometry has Z coordinates.
    1053             :  */
    1054             : 
    1055       37776 : int OGR_G_Is3D(OGRGeometryH hGeom)
    1056             : 
    1057             : {
    1058       37776 :     VALIDATE_POINTER1(hGeom, "OGR_G_Is3D", 0);
    1059             : 
    1060       37776 :     return OGRGeometry::FromHandle(hGeom)->Is3D();
    1061             : }
    1062             : 
    1063             : /**
    1064             :  *
    1065             :  * \brief See whether this geometry is measured.
    1066             :  *
    1067             :  * This function is the same as the CPP method
    1068             :  * OGRGeometry::IsMeasured().
    1069             :  *
    1070             :  * @param hGeom handle on the geometry to check whether it is measured.
    1071             :  *
    1072             :  * @return TRUE if the geometry has M coordinates.
    1073             :  */
    1074             : 
    1075       40185 : int OGR_G_IsMeasured(OGRGeometryH hGeom)
    1076             : 
    1077             : {
    1078       40185 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsMeasured", 0);
    1079             : 
    1080       40185 :     return OGRGeometry::FromHandle(hGeom)->IsMeasured();
    1081             : }
    1082             : 
    1083             : /************************************************************************/
    1084             : /*                       setCoordinateDimension()                       */
    1085             : /************************************************************************/
    1086             : 
    1087             : /**
    1088             :  * \brief Set the coordinate dimension.
    1089             :  *
    1090             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1091             :  * dimension of a geometry to 2 should zero out any existing Z values.  Setting
    1092             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1093             :  * will affect the children geometries.
    1094             :  * This will also remove the M dimension if present before this call.
    1095             :  *
    1096             :  * @deprecated use set3D() or setMeasured().
    1097             :  *
    1098             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1099             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1100             :  */
    1101             : 
    1102       68170 : bool OGRGeometry::setCoordinateDimension(int nNewDimension)
    1103             : 
    1104             : {
    1105       68170 :     if (nNewDimension == 2)
    1106       67678 :         flags &= ~OGR_G_3D;
    1107             :     else
    1108         492 :         flags |= OGR_G_3D;
    1109       68170 :     return setMeasured(FALSE);
    1110             : }
    1111             : 
    1112             : /**
    1113             :  * \brief Add or remove the Z coordinate dimension.
    1114             :  *
    1115             :  * This method adds or removes the explicit Z coordinate dimension.
    1116             :  * Removing the Z coordinate dimension of a geometry will remove any
    1117             :  * existing Z values.  Adding the Z dimension to a geometry
    1118             :  * collection, a compound curve, a polygon, etc.  will affect the
    1119             :  * children geometries.
    1120             :  *
    1121             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1122             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1123             :  */
    1124             : 
    1125     1618800 : bool OGRGeometry::set3D(bool bIs3D)
    1126             : 
    1127             : {
    1128     1618800 :     if (bIs3D)
    1129     1612740 :         flags |= OGR_G_3D;
    1130             :     else
    1131        6053 :         flags &= ~OGR_G_3D;
    1132     1618800 :     return true;
    1133             : }
    1134             : 
    1135             : /**
    1136             :  * \brief Add or remove the M coordinate dimension.
    1137             :  *
    1138             :  * This method adds or removes the explicit M coordinate dimension.
    1139             :  * Removing the M coordinate dimension of a geometry will remove any
    1140             :  * existing M values.  Adding the M dimension to a geometry
    1141             :  * collection, a compound curve, a polygon, etc.  will affect the
    1142             :  * children geometries.
    1143             :  *
    1144             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1145             :  * TRUE or FALSE.
    1146             :  * @return (since 3.10) true in case of success, false in case of memory allocation error
    1147             :  */
    1148             : 
    1149      415491 : bool OGRGeometry::setMeasured(bool bIsMeasured)
    1150             : 
    1151             : {
    1152      415491 :     if (bIsMeasured)
    1153      137729 :         flags |= OGR_G_MEASURED;
    1154             :     else
    1155      277762 :         flags &= ~OGR_G_MEASURED;
    1156      415491 :     return true;
    1157             : }
    1158             : 
    1159             : /************************************************************************/
    1160             : /*                    OGR_G_SetCoordinateDimension()                    */
    1161             : /************************************************************************/
    1162             : 
    1163             : /**
    1164             :  * \brief Set the coordinate dimension.
    1165             :  *
    1166             :  * This method sets the explicit coordinate dimension.  Setting the coordinate
    1167             :  * dimension of a geometry to 2 should zero out any existing Z values. Setting
    1168             :  * the dimension of a geometry collection, a compound curve, a polygon, etc.
    1169             :  * will affect the children geometries.
    1170             :  * This will also remove the M dimension if present before this call.
    1171             :  *
    1172             :  * @deprecated use OGR_G_Set3D() or OGR_G_SetMeasured().
    1173             :  *
    1174             :  * @param hGeom handle on the geometry to set the dimension of the
    1175             :  * coordinates.
    1176             :  * @param nNewDimension New coordinate dimension value, either 2 or 3.
    1177             :  */
    1178             : 
    1179          56 : void OGR_G_SetCoordinateDimension(OGRGeometryH hGeom, int nNewDimension)
    1180             : 
    1181             : {
    1182          56 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetCoordinateDimension");
    1183             : 
    1184          56 :     OGRGeometry::FromHandle(hGeom)->setCoordinateDimension(nNewDimension);
    1185             : }
    1186             : 
    1187             : /************************************************************************/
    1188             : /*                            OGR_G_Set3D()                             */
    1189             : /************************************************************************/
    1190             : 
    1191             : /**
    1192             :  * \brief Add or remove the Z coordinate dimension.
    1193             :  *
    1194             :  * This method adds or removes the explicit Z coordinate dimension.
    1195             :  * Removing the Z coordinate dimension of a geometry will remove any
    1196             :  * existing Z values.  Adding the Z dimension to a geometry
    1197             :  * collection, a compound curve, a polygon, etc.  will affect the
    1198             :  * children geometries.
    1199             :  *
    1200             :  * @param hGeom handle on the geometry to set or unset the Z dimension.
    1201             :  * @param bIs3D Should the geometry have a Z dimension, either TRUE or FALSE.
    1202             :  */
    1203             : 
    1204         154 : void OGR_G_Set3D(OGRGeometryH hGeom, int bIs3D)
    1205             : 
    1206             : {
    1207         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_Set3D");
    1208             : 
    1209         154 :     OGRGeometry::FromHandle(hGeom)->set3D(CPL_TO_BOOL(bIs3D));
    1210             : }
    1211             : 
    1212             : /************************************************************************/
    1213             : /*                         OGR_G_SetMeasured()                          */
    1214             : /************************************************************************/
    1215             : 
    1216             : /**
    1217             :  * \brief Add or remove the M coordinate dimension.
    1218             :  *
    1219             :  * This method adds or removes the explicit M coordinate dimension.
    1220             :  * Removing the M coordinate dimension of a geometry will remove any
    1221             :  * existing M values.  Adding the M dimension to a geometry
    1222             :  * collection, a compound curve, a polygon, etc.  will affect the
    1223             :  * children geometries.
    1224             :  *
    1225             :  * @param hGeom handle on the geometry to set or unset the M dimension.
    1226             :  * @param bIsMeasured Should the geometry have a M dimension, either
    1227             :  * TRUE or FALSE.
    1228             :  */
    1229             : 
    1230         154 : void OGR_G_SetMeasured(OGRGeometryH hGeom, int bIsMeasured)
    1231             : 
    1232             : {
    1233         154 :     VALIDATE_POINTER0(hGeom, "OGR_G_SetMeasured");
    1234             : 
    1235         154 :     OGRGeometry::FromHandle(hGeom)->setMeasured(CPL_TO_BOOL(bIsMeasured));
    1236             : }
    1237             : 
    1238             : /**
    1239             :  * \fn bool OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const;
    1240             :  *
    1241             :  * \brief Returns TRUE if two geometries are equivalent.
    1242             :  *
    1243             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1244             :  *
    1245             :  * The comparison is done in a structural way, that is to say that the geometry
    1246             :  * types must be identical, as well as the number and ordering of sub-geometries
    1247             :  * and vertices.
    1248             :  * Or equivalently, two geometries are considered equal by this method if their
    1249             :  * WKT/WKB representation is equal.
    1250             :  * Note: this must be distinguished for equality in a spatial way (which is
    1251             :  * the purpose of the ST_Equals() operation).
    1252             :  *
    1253             :  * This method is the same as the C function OGR_G_Equals().
    1254             :  *
    1255             :  * @return TRUE if equivalent or FALSE otherwise.
    1256             :  */
    1257             : 
    1258             : // Backward compatibility method.
    1259             : 
    1260             : //! @cond Doxygen_Suppress
    1261           0 : bool OGRGeometry::Equal(OGRGeometry *poOtherGeom) const
    1262             : {
    1263           0 :     return Equals(poOtherGeom);
    1264             : }
    1265             : 
    1266             : //! @endcond
    1267             : 
    1268             : /************************************************************************/
    1269             : /*                            OGR_G_Equals()                            */
    1270             : /************************************************************************/
    1271             : 
    1272             : /**
    1273             :  * \brief Returns TRUE if two geometries are equivalent.
    1274             :  *
    1275             :  * This operation implements the SQL/MM ST_OrderingEquals() operation.
    1276             :  *
    1277             :  * The comparison is done in a structural way, that is to say that the geometry
    1278             :  * types must be identical, as well as the number and ordering of sub-geometries
    1279             :  * and vertices.
    1280             :  * Or equivalently, two geometries are considered equal by this method if their
    1281             :  * WKT/WKB representation is equal.
    1282             :  * Note: this must be distinguished for equality in a spatial way (which is
    1283             :  * the purpose of the ST_Equals() operation).
    1284             :  *
    1285             :  * This function is the same as the CPP method OGRGeometry::Equals() method.
    1286             :  *
    1287             :  * @param hGeom handle on the first geometry.
    1288             :  * @param hOther handle on the other geometry to test against.
    1289             :  * @return TRUE if equivalent or FALSE otherwise.
    1290             :  */
    1291             : 
    1292       28138 : int OGR_G_Equals(OGRGeometryH hGeom, OGRGeometryH hOther)
    1293             : 
    1294             : {
    1295       28138 :     VALIDATE_POINTER1(hGeom, "OGR_G_Equals", FALSE);
    1296             : 
    1297       28138 :     if (hOther == nullptr)
    1298             :     {
    1299           0 :         CPLError(CE_Failure, CPLE_ObjectNull,
    1300             :                  "hOther was NULL in OGR_G_Equals");
    1301           0 :         return 0;
    1302             :     }
    1303             : 
    1304       56276 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1305       56276 :         OGRGeometry::FromHandle(hOther));
    1306             : }
    1307             : 
    1308             : //! @cond Doxygen_Suppress
    1309           0 : int OGR_G_Equal(OGRGeometryH hGeom, OGRGeometryH hOther)
    1310             : 
    1311             : {
    1312           0 :     if (hGeom == nullptr)
    1313             :     {
    1314           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal");
    1315           0 :         return 0;
    1316             :     }
    1317             : 
    1318           0 :     if (hOther == nullptr)
    1319             :     {
    1320           0 :         CPLError(CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal");
    1321           0 :         return 0;
    1322             :     }
    1323             : 
    1324           0 :     return OGRGeometry::FromHandle(hGeom)->Equals(
    1325           0 :         OGRGeometry::FromHandle(hOther));
    1326             : }
    1327             : 
    1328             : //! @endcond
    1329             : 
    1330             : /**
    1331             :  * \fn int OGRGeometry::WkbSize() const;
    1332             :  *
    1333             :  * \brief Returns size of related binary representation.
    1334             :  *
    1335             :  * This method returns the exact number of bytes required to hold the
    1336             :  * well known binary representation of this geometry object.  Its computation
    1337             :  * may be slightly expensive for complex geometries.
    1338             :  *
    1339             :  * This method relates to the SFCOM IWks::WkbSize() method.
    1340             :  *
    1341             :  * This method is the same as the C function OGR_G_WkbSize().
    1342             :  *
    1343             :  * @return size of binary representation in bytes.
    1344             :  */
    1345             : 
    1346             : /************************************************************************/
    1347             : /*                           OGR_G_WkbSize()                            */
    1348             : /************************************************************************/
    1349             : /**
    1350             :  * \brief Returns size of related binary representation.
    1351             :  *
    1352             :  * This function returns the exact number of bytes required to hold the
    1353             :  * well known binary representation of this geometry object.  Its computation
    1354             :  * may be slightly expensive for complex geometries.
    1355             :  *
    1356             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1357             :  *
    1358             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1359             :  *
    1360             :  * Use OGR_G_WkbSizeEx() if called on huge geometries (> 2 GB serialized)
    1361             :  *
    1362             :  * @param hGeom handle on the geometry to get the binary size from.
    1363             :  * @return size of binary representation in bytes.
    1364             :  */
    1365             : 
    1366           1 : int OGR_G_WkbSize(OGRGeometryH hGeom)
    1367             : 
    1368             : {
    1369           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSize", 0);
    1370             : 
    1371           1 :     const size_t nSize = OGRGeometry::FromHandle(hGeom)->WkbSize();
    1372           1 :     if (nSize > static_cast<size_t>(std::numeric_limits<int>::max()))
    1373             :     {
    1374           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1375             :                  "OGR_G_WkbSize() would return a value beyond int range. "
    1376             :                  "Use OGR_G_WkbSizeEx() instead");
    1377           0 :         return 0;
    1378             :     }
    1379           1 :     return static_cast<int>(nSize);
    1380             : }
    1381             : 
    1382             : /************************************************************************/
    1383             : /*                          OGR_G_WkbSizeEx()                           */
    1384             : /************************************************************************/
    1385             : /**
    1386             :  * \brief Returns size of related binary representation.
    1387             :  *
    1388             :  * This function returns the exact number of bytes required to hold the
    1389             :  * well known binary representation of this geometry object.  Its computation
    1390             :  * may be slightly expensive for complex geometries.
    1391             :  *
    1392             :  * This function relates to the SFCOM IWks::WkbSize() method.
    1393             :  *
    1394             :  * This function is the same as the CPP method OGRGeometry::WkbSize().
    1395             :  *
    1396             :  * @param hGeom handle on the geometry to get the binary size from.
    1397             :  * @return size of binary representation in bytes.
    1398             :  * @since GDAL 3.3
    1399             :  */
    1400             : 
    1401       10679 : size_t OGR_G_WkbSizeEx(OGRGeometryH hGeom)
    1402             : 
    1403             : {
    1404       10679 :     VALIDATE_POINTER1(hGeom, "OGR_G_WkbSizeEx", 0);
    1405             : 
    1406       10679 :     return OGRGeometry::FromHandle(hGeom)->WkbSize();
    1407             : }
    1408             : 
    1409             : /**
    1410             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const;
    1411             :  *
    1412             :  * \brief Computes and returns the bounding envelope for this geometry
    1413             :  * in the passed psEnvelope structure.
    1414             :  *
    1415             :  * This method is the same as the C function OGR_G_GetEnvelope().
    1416             :  *
    1417             :  * @param psEnvelope the structure in which to place the results.
    1418             :  */
    1419             : 
    1420             : /************************************************************************/
    1421             : /*                         OGR_G_GetEnvelope()                          */
    1422             : /************************************************************************/
    1423             : /**
    1424             :  * \brief Computes and returns the bounding envelope for this geometry
    1425             :  * in the passed psEnvelope structure.
    1426             :  *
    1427             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1428             :  *
    1429             :  * @param hGeom handle of the geometry to get envelope from.
    1430             :  * @param psEnvelope the structure in which to place the results.
    1431             :  */
    1432             : 
    1433       13363 : void OGR_G_GetEnvelope(OGRGeometryH hGeom, OGREnvelope *psEnvelope)
    1434             : 
    1435             : {
    1436       13363 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope");
    1437             : 
    1438       13363 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1439             : }
    1440             : 
    1441             : /**
    1442             :  * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const;
    1443             :  *
    1444             :  * \brief Computes and returns the bounding envelope (3D) for this
    1445             :  * geometry in the passed psEnvelope structure.
    1446             :  *
    1447             :  * This method is the same as the C function OGR_G_GetEnvelope3D().
    1448             :  *
    1449             :  * @param psEnvelope the structure in which to place the results.
    1450             :  *
    1451             :  */
    1452             : 
    1453             : /************************************************************************/
    1454             : /*                        OGR_G_GetEnvelope3D()                         */
    1455             : /************************************************************************/
    1456             : /**
    1457             :  * \brief Computes and returns the bounding envelope (3D) for this
    1458             :  * geometry in the passed psEnvelope structure.
    1459             :  *
    1460             :  * This function is the same as the CPP method OGRGeometry::getEnvelope().
    1461             :  *
    1462             :  * @param hGeom handle of the geometry to get envelope from.
    1463             :  * @param psEnvelope the structure in which to place the results.
    1464             :  *
    1465             :  */
    1466             : 
    1467          10 : void OGR_G_GetEnvelope3D(OGRGeometryH hGeom, OGREnvelope3D *psEnvelope)
    1468             : 
    1469             : {
    1470          10 :     VALIDATE_POINTER0(hGeom, "OGR_G_GetEnvelope3D");
    1471             : 
    1472          10 :     OGRGeometry::FromHandle(hGeom)->getEnvelope(psEnvelope);
    1473             : }
    1474             : 
    1475             : /************************************************************************/
    1476             : /*                           importFromWkb()                            */
    1477             : /************************************************************************/
    1478             : 
    1479             : /**
    1480             :  * \brief Assign geometry from well known binary data.
    1481             :  *
    1482             :  * The object must have already been instantiated as the correct derived
    1483             :  * type of geometry object to match the binaries type.  This method is used
    1484             :  * by the OGRGeometryFactory class, but not normally called by application
    1485             :  * code.
    1486             :  *
    1487             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1488             :  *
    1489             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1490             :  *
    1491             :  * @param pabyData the binary input data.
    1492             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1493             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1494             :  * done for curve geometries code
    1495             :  *
    1496             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1497             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1498             :  * OGRERR_CORRUPT_DATA may be returned.
    1499             :  */
    1500             : 
    1501         492 : OGRErr OGRGeometry::importFromWkb(const GByte *pabyData, size_t nSize,
    1502             :                                   OGRwkbVariant eWkbVariant)
    1503             : {
    1504         492 :     size_t nBytesConsumedOutIgnored = 0;
    1505         492 :     return importFromWkb(pabyData, nSize, eWkbVariant,
    1506         984 :                          nBytesConsumedOutIgnored);
    1507             : }
    1508             : 
    1509             : /**
    1510             :  * \fn OGRErr OGRGeometry::importFromWkb( const unsigned char * pabyData,
    1511             :  * size_t nSize, OGRwkbVariant eWkbVariant, size_t& nBytesConsumedOut );
    1512             :  *
    1513             :  * \brief Assign geometry from well known binary data.
    1514             :  *
    1515             :  * The object must have already been instantiated as the correct derived
    1516             :  * type of geometry object to match the binaries type.  This method is used
    1517             :  * by the OGRGeometryFactory class, but not normally called by application
    1518             :  * code.
    1519             :  *
    1520             :  * This method relates to the SFCOM IWks::ImportFromWKB() method.
    1521             :  *
    1522             :  * This method is the same as the C function OGR_G_ImportFromWkb().
    1523             :  *
    1524             :  * @param pabyData the binary input data.
    1525             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1526             :  * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is
    1527             :  * done for curve geometries code
    1528             :  * @param nBytesConsumedOut output parameter. Number of bytes consumed.
    1529             :  *
    1530             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1531             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1532             :  * OGRERR_CORRUPT_DATA may be returned.
    1533             :  *
    1534             :  */
    1535             : 
    1536             : /************************************************************************/
    1537             : /*                        OGR_G_ImportFromWkb()                         */
    1538             : /************************************************************************/
    1539             : /**
    1540             :  * \brief Assign geometry from well known binary data.
    1541             :  *
    1542             :  * The object must have already been instantiated as the correct derived
    1543             :  * type of geometry object to match the binaries type.
    1544             :  *
    1545             :  * This function relates to the SFCOM IWks::ImportFromWKB() method.
    1546             :  *
    1547             :  * This function is the same as the CPP method OGRGeometry::importFromWkb().
    1548             :  *
    1549             :  * @param hGeom handle on the geometry to assign the well know binary data to.
    1550             :  * @param pabyData the binary input data.
    1551             :  * @param nSize the size of pabyData in bytes, or -1 if not known.
    1552             :  *
    1553             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1554             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1555             :  * OGRERR_CORRUPT_DATA may be returned.
    1556             :  */
    1557             : 
    1558           0 : OGRErr OGR_G_ImportFromWkb(OGRGeometryH hGeom, const void *pabyData, int nSize)
    1559             : 
    1560             : {
    1561           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE);
    1562             : 
    1563           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkb(
    1564           0 :         static_cast<const GByte *>(pabyData), nSize);
    1565             : }
    1566             : 
    1567             : /************************************************************************/
    1568             : /*                      OGRGeometry::exportToWkb()                      */
    1569             : /************************************************************************/
    1570             : 
    1571             : /* clang-format off */
    1572             : /**
    1573             :  * \brief Convert a geometry into well known binary format.
    1574             :  *
    1575             :  * This method relates to the SFCOM IWks::ExportToWKB() method.
    1576             :  *
    1577             :  * This method is the same as the C function OGR_G_ExportToWkb() or
    1578             :  * OGR_G_ExportToIsoWkb(), depending on the value of eWkbVariant.
    1579             :  *
    1580             :  * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1581             :  *               respectively.
    1582             :  * @param pabyData a buffer into which the binary representation is
    1583             :  *                      written.  This buffer must be at least
    1584             :  *                      OGRGeometry::WkbSize() byte in size.
    1585             :  * @param eWkbVariant What standard to use when exporting geometries
    1586             :  *                      with three dimensions (or more). The default
    1587             :  *                      wkbVariantOldOgc is the historical OGR
    1588             :  *                      variant. wkbVariantIso is the variant defined
    1589             :  *                      in ISO SQL/MM and adopted by OGC for SFSQL
    1590             :  *                      1.2.
    1591             :  *
    1592             :  * @return Currently OGRERR_NONE is always returned.
    1593             :  */
    1594             : /* clang-format on */
    1595             : 
    1596      278673 : OGRErr OGRGeometry::exportToWkb(OGRwkbByteOrder eByteOrder,
    1597             :                                 unsigned char *pabyData,
    1598             :                                 OGRwkbVariant eWkbVariant) const
    1599             : {
    1600      278673 :     OGRwkbExportOptions sOptions;
    1601      278673 :     sOptions.eByteOrder = eByteOrder;
    1602      278673 :     sOptions.eWkbVariant = eWkbVariant;
    1603      557346 :     return exportToWkb(pabyData, &sOptions);
    1604             : }
    1605             : 
    1606             : /************************************************************************/
    1607             : /*                         OGR_G_ExportToWkb()                          */
    1608             : /************************************************************************/
    1609             : /**
    1610             :  * \brief Convert a geometry well known binary format
    1611             :  *
    1612             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1613             :  *
    1614             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1615             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1616             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1617             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb().
    1618             :  *
    1619             :  * This function is the same as the CPP method
    1620             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *,
    1621             :  * OGRwkbVariant) with eWkbVariant = wkbVariantOldOgc.
    1622             :  *
    1623             :  * @param hGeom handle on the geometry to convert to a well know binary
    1624             :  * data from.
    1625             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1626             :  *               respectively.
    1627             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1628             :  *                      written.  This buffer must be at least
    1629             :  *                      OGR_G_WkbSize() byte in size.
    1630             :  *
    1631             :  * @return Currently OGRERR_NONE is always returned.
    1632             :  */
    1633             : 
    1634         109 : OGRErr OGR_G_ExportToWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1635             :                          unsigned char *pabyDstBuffer)
    1636             : 
    1637             : {
    1638         109 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE);
    1639             : 
    1640         109 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer);
    1641             : }
    1642             : 
    1643             : /************************************************************************/
    1644             : /*                        OGR_G_ExportToIsoWkb()                        */
    1645             : /************************************************************************/
    1646             : /**
    1647             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known
    1648             :  * binary format
    1649             :  *
    1650             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1651             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB
    1652             :  * types.
    1653             :  *
    1654             :  * This function is the same as the CPP method
    1655             :  * OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant)
    1656             :  * with eWkbVariant = wkbVariantIso.
    1657             :  *
    1658             :  * @param hGeom handle on the geometry to convert to a well know binary
    1659             :  * data from.
    1660             :  * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order
    1661             :  *               respectively.
    1662             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1663             :  *                      written.  This buffer must be at least
    1664             :  *                      OGR_G_WkbSize() byte in size.
    1665             :  *
    1666             :  * @return Currently OGRERR_NONE is always returned.
    1667             :  *
    1668             :  */
    1669             : 
    1670       10571 : OGRErr OGR_G_ExportToIsoWkb(OGRGeometryH hGeom, OGRwkbByteOrder eOrder,
    1671             :                             unsigned char *pabyDstBuffer)
    1672             : 
    1673             : {
    1674       10571 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE);
    1675             : 
    1676       10571 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(eOrder, pabyDstBuffer,
    1677       10571 :                                                        wkbVariantIso);
    1678             : }
    1679             : 
    1680             : /************************************************************************/
    1681             : /*                        OGR_G_ExportToWkbEx()                         */
    1682             : /************************************************************************/
    1683             : 
    1684             : /* clang-format off */
    1685             : /**
    1686             :  * \fn OGRErr OGRGeometry::exportToWkb(unsigned char *pabyDstBuffer, const OGRwkbExportOptions *psOptions=nullptr) const
    1687             :  *
    1688             :  * \brief Convert a geometry into well known binary format
    1689             :  *
    1690             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1691             :  *
    1692             :  * This function is the same as the C function OGR_G_ExportToWkbEx().
    1693             :  *
    1694             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1695             :  *                      written.  This buffer must be at least
    1696             :  *                      OGR_G_WkbSize() byte in size.
    1697             :  * @param psOptions WKB export options.
    1698             : 
    1699             :  * @return Currently OGRERR_NONE is always returned.
    1700             :  *
    1701             :  * @since GDAL 3.9
    1702             :  */
    1703             : /* clang-format on */
    1704             : 
    1705             : /**
    1706             :  * \brief Convert a geometry into well known binary format
    1707             :  *
    1708             :  * This function relates to the SFCOM IWks::ExportToWKB() method.
    1709             :  *
    1710             :  * This function is the same as the CPP method
    1711             :  * OGRGeometry::exportToWkb(unsigned char *, const OGRwkbExportOptions*)
    1712             :  *
    1713             :  * @param hGeom handle on the geometry to convert to a well know binary
    1714             :  * data from.
    1715             :  * @param pabyDstBuffer a buffer into which the binary representation is
    1716             :  *                      written.  This buffer must be at least
    1717             :  *                      OGR_G_WkbSize() byte in size.
    1718             :  * @param psOptions WKB export options.
    1719             : 
    1720             :  * @return Currently OGRERR_NONE is always returned.
    1721             :  *
    1722             :  * @since GDAL 3.9
    1723             :  */
    1724             : 
    1725           2 : OGRErr OGR_G_ExportToWkbEx(OGRGeometryH hGeom, unsigned char *pabyDstBuffer,
    1726             :                            const OGRwkbExportOptions *psOptions)
    1727             : {
    1728           2 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkbEx", OGRERR_FAILURE);
    1729             : 
    1730           4 :     return OGRGeometry::FromHandle(hGeom)->exportToWkb(pabyDstBuffer,
    1731           2 :                                                        psOptions);
    1732             : }
    1733             : 
    1734             : /**
    1735             :  * \fn OGRErr OGRGeometry::importFromWkt( const char ** ppszInput );
    1736             :  *
    1737             :  * \brief Assign geometry from well known text data.
    1738             :  *
    1739             :  * The object must have already been instantiated as the correct derived
    1740             :  * type of geometry object to match the text type.  This method is used
    1741             :  * by the OGRGeometryFactory class, but not normally called by application
    1742             :  * code.
    1743             :  *
    1744             :  * This method relates to the SFCOM IWks::ImportFromWKT() method.
    1745             :  *
    1746             :  * This method is the same as the C function OGR_G_ImportFromWkt().
    1747             :  *
    1748             :  * @param ppszInput pointer to a pointer to the source text.  The pointer is
    1749             :  *                    updated to pointer after the consumed text.
    1750             :  *
    1751             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1752             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1753             :  * OGRERR_CORRUPT_DATA may be returned.
    1754             :  */
    1755             : 
    1756             : /************************************************************************/
    1757             : /*                        OGR_G_ImportFromWkt()                         */
    1758             : /************************************************************************/
    1759             : /**
    1760             :  * \brief Assign geometry from well known text data.
    1761             :  *
    1762             :  * The object must have already been instantiated as the correct derived
    1763             :  * type of geometry object to match the text type.
    1764             :  *
    1765             :  * This function relates to the SFCOM IWks::ImportFromWKT() method.
    1766             :  *
    1767             :  * This function is the same as the CPP method OGRGeometry::importFromWkt().
    1768             :  *
    1769             :  * @param hGeom handle on the geometry to assign well know text data to.
    1770             :  * @param ppszSrcText pointer to a pointer to the source text.  The pointer is
    1771             :  *                    updated to pointer after the consumed text.
    1772             :  *
    1773             :  * @return OGRERR_NONE if all goes well, otherwise any of
    1774             :  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
    1775             :  * OGRERR_CORRUPT_DATA may be returned.
    1776             :  */
    1777             : 
    1778           0 : OGRErr OGR_G_ImportFromWkt(OGRGeometryH hGeom, char **ppszSrcText)
    1779             : 
    1780             : {
    1781           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE);
    1782             : 
    1783           0 :     return OGRGeometry::FromHandle(hGeom)->importFromWkt(
    1784           0 :         const_cast<const char **>(ppszSrcText));
    1785             : }
    1786             : 
    1787             : /************************************************************************/
    1788             : /*                       importPreambleFromWkt()                        */
    1789             : /************************************************************************/
    1790             : 
    1791             : // Returns -1 if processing must continue.
    1792             : //! @cond Doxygen_Suppress
    1793      123857 : OGRErr OGRGeometry::importPreambleFromWkt(const char **ppszInput, int *pbHasZ,
    1794             :                                           int *pbHasM, bool *pbIsEmpty)
    1795             : {
    1796      123857 :     const char *pszInput = *ppszInput;
    1797             : 
    1798             :     /* -------------------------------------------------------------------- */
    1799             :     /*      Clear existing Geoms.                                           */
    1800             :     /* -------------------------------------------------------------------- */
    1801      123857 :     empty();
    1802      123857 :     *pbIsEmpty = false;
    1803             : 
    1804             :     /* -------------------------------------------------------------------- */
    1805             :     /*      Read and verify the type keyword, and ensure it matches the     */
    1806             :     /*      actual type of this container.                                  */
    1807             :     /* -------------------------------------------------------------------- */
    1808      123857 :     bool bHasM = false;
    1809      123857 :     bool bHasZ = false;
    1810      123857 :     bool bAlreadyGotDimension = false;
    1811             : 
    1812      123857 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
    1813      123857 :     pszInput = OGRWktReadToken(pszInput, szToken);
    1814      123857 :     if (szToken[0] != '\0')
    1815             :     {
    1816             :         // Postgis EWKT: POINTM instead of POINT M.
    1817             :         // Current QGIS versions (at least <= 3.38) also export POINTZ.
    1818      123857 :         const size_t nTokenLen = strlen(szToken);
    1819      123857 :         if (szToken[nTokenLen - 1] == 'M' || szToken[nTokenLen - 1] == 'm')
    1820             :         {
    1821          11 :             szToken[nTokenLen - 1] = '\0';
    1822          11 :             bHasM = true;
    1823          11 :             bAlreadyGotDimension = true;
    1824             : 
    1825          11 :             if (nTokenLen > 2 && (szToken[nTokenLen - 2] == 'Z' ||
    1826           9 :                                   szToken[nTokenLen - 2] == 'z'))
    1827             :             {
    1828           4 :                 bHasZ = true;
    1829           4 :                 szToken[nTokenLen - 2] = '\0';
    1830             :             }
    1831             :         }
    1832      123846 :         else if (szToken[nTokenLen - 1] == 'Z' || szToken[nTokenLen - 1] == 'z')
    1833             :         {
    1834           6 :             szToken[nTokenLen - 1] = '\0';
    1835           6 :             bHasZ = true;
    1836           6 :             bAlreadyGotDimension = true;
    1837             :         }
    1838             :     }
    1839             : 
    1840      123857 :     if (!EQUAL(szToken, getGeometryName()))
    1841           0 :         return OGRERR_CORRUPT_DATA;
    1842             : 
    1843             :     /* -------------------------------------------------------------------- */
    1844             :     /*      Check for Z, M or ZM                                            */
    1845             :     /* -------------------------------------------------------------------- */
    1846      123857 :     if (!bAlreadyGotDimension)
    1847             :     {
    1848      123840 :         const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1849      123840 :         if (EQUAL(szToken, "Z"))
    1850             :         {
    1851        1418 :             pszInput = pszNewInput;
    1852        1418 :             bHasZ = true;
    1853             :         }
    1854      122422 :         else if (EQUAL(szToken, "M"))
    1855             :         {
    1856         353 :             pszInput = pszNewInput;
    1857         353 :             bHasM = true;
    1858             :         }
    1859      122069 :         else if (EQUAL(szToken, "ZM"))
    1860             :         {
    1861         494 :             pszInput = pszNewInput;
    1862         494 :             bHasZ = true;
    1863         494 :             bHasM = true;
    1864             :         }
    1865             :     }
    1866      123857 :     *pbHasZ = bHasZ;
    1867      123857 :     *pbHasM = bHasM;
    1868             : 
    1869             :     /* -------------------------------------------------------------------- */
    1870             :     /*      Check for EMPTY ...                                             */
    1871             :     /* -------------------------------------------------------------------- */
    1872      123857 :     const char *pszNewInput = OGRWktReadToken(pszInput, szToken);
    1873      123857 :     if (EQUAL(szToken, "EMPTY"))
    1874             :     {
    1875        1578 :         *ppszInput = pszNewInput;
    1876        1578 :         *pbIsEmpty = true;
    1877        1578 :         if (bHasZ)
    1878         137 :             set3D(TRUE);
    1879        1578 :         if (bHasM)
    1880          84 :             setMeasured(TRUE);
    1881        1578 :         return OGRERR_NONE;
    1882             :     }
    1883             : 
    1884      122279 :     if (!EQUAL(szToken, "("))
    1885          35 :         return OGRERR_CORRUPT_DATA;
    1886             : 
    1887      122244 :     if (!bHasZ && !bHasM)
    1888             :     {
    1889             :         // Test for old-style XXXXXXXXX(EMPTY).
    1890      120142 :         pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1891      120142 :         if (EQUAL(szToken, "EMPTY"))
    1892             :         {
    1893          69 :             pszNewInput = OGRWktReadToken(pszNewInput, szToken);
    1894             : 
    1895          69 :             if (EQUAL(szToken, ","))
    1896             :             {
    1897             :                 // This is OK according to SFSQL SPEC.
    1898             :             }
    1899          44 :             else if (!EQUAL(szToken, ")"))
    1900             :             {
    1901           9 :                 return OGRERR_CORRUPT_DATA;
    1902             :             }
    1903             :             else
    1904             :             {
    1905          35 :                 *ppszInput = pszNewInput;
    1906          35 :                 empty();
    1907          35 :                 *pbIsEmpty = true;
    1908          35 :                 return OGRERR_NONE;
    1909             :             }
    1910             :         }
    1911             :     }
    1912             : 
    1913      122200 :     *ppszInput = pszInput;
    1914             : 
    1915      122200 :     return OGRERR_NONE;
    1916             : }
    1917             : 
    1918             : //! @endcond
    1919             : 
    1920             : /************************************************************************/
    1921             : /*                           wktTypeString()                            */
    1922             : /************************************************************************/
    1923             : 
    1924             : //! @cond Doxygen_Suppress
    1925             : /** Get a type string for WKT, padded with a space at the end.
    1926             :  *
    1927             :  * @param variant  OGR type variant
    1928             :  * @return  "Z " for 3D, "M " for measured, "ZM " for both, or the empty string.
    1929             :  */
    1930       14263 : std::string OGRGeometry::wktTypeString(OGRwkbVariant variant) const
    1931             : {
    1932       14263 :     std::string s(" ");
    1933             : 
    1934       14263 :     if (variant == wkbVariantIso)
    1935             :     {
    1936        9352 :         if (flags & OGR_G_3D)
    1937        1957 :             s += "Z";
    1938        9352 :         if (flags & OGR_G_MEASURED)
    1939        1200 :             s += "M";
    1940             :     }
    1941       14263 :     if (s.size() > 1)
    1942        2518 :         s += " ";
    1943       14263 :     return s;
    1944             : }
    1945             : 
    1946             : //! @endcond
    1947             : 
    1948             : /**
    1949             :  * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText,
    1950             :  * OGRwkbVariant variant = wkbVariantOldOgc ) const;
    1951             :  *
    1952             :  * \brief Convert a geometry into well known text format.
    1953             :  *
    1954             :  * This method relates to the SFCOM IWks::ExportToWKT() method.
    1955             :  *
    1956             :  * This method is the same as the C function OGR_G_ExportToWkt().
    1957             :  *
    1958             :  * @param ppszDstText a text buffer is allocated by the program, and assigned
    1959             :  *                    to the passed pointer. After use, *ppszDstText should be
    1960             :  *                    freed with CPLFree().
    1961             :  * @param variant the specification that must be conformed too :
    1962             :  *                    - wkbVariantOgc for old-style 99-402 extended
    1963             :  *                      dimension (Z) WKB types
    1964             :  *                    - wkbVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3
    1965             :  *
    1966             :  * @return Currently OGRERR_NONE is always returned.
    1967             :  */
    1968        8883 : OGRErr OGRGeometry::exportToWkt(char **ppszDstText, OGRwkbVariant variant) const
    1969             : {
    1970        8883 :     OGRWktOptions opts;
    1971        8883 :     opts.variant = variant;
    1972        8883 :     OGRErr err(OGRERR_NONE);
    1973             : 
    1974        8883 :     std::string wkt = exportToWkt(opts, &err);
    1975        8883 :     *ppszDstText = CPLStrdup(wkt.data());
    1976       17766 :     return err;
    1977             : }
    1978             : 
    1979             : /************************************************************************/
    1980             : /*                         OGR_G_ExportToWkt()                          */
    1981             : /************************************************************************/
    1982             : 
    1983             : /**
    1984             :  * \brief Convert a geometry into well known text format.
    1985             :  *
    1986             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    1987             :  *
    1988             :  * For backward compatibility purposes, it exports the Old-style 99-402
    1989             :  * extended dimension (Z) WKB types for types Point, LineString, Polygon,
    1990             :  * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection.
    1991             :  * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt().
    1992             :  *
    1993             :  * This function is the same as the CPP method OGRGeometry::exportToWkt().
    1994             :  *
    1995             :  * @param hGeom handle on the geometry to convert to a text format from.
    1996             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    1997             :  *                    to the passed pointer. After use, *ppszDstText should be
    1998             :  *                    freed with CPLFree().
    1999             :  *
    2000             :  * @return Currently OGRERR_NONE is always returned.
    2001             :  */
    2002             : 
    2003        2551 : OGRErr OGR_G_ExportToWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2004             : 
    2005             : {
    2006        2551 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE);
    2007             : 
    2008        2551 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText);
    2009             : }
    2010             : 
    2011             : /************************************************************************/
    2012             : /*                        OGR_G_ExportToIsoWkt()                        */
    2013             : /************************************************************************/
    2014             : 
    2015             : /**
    2016             :  * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well
    2017             :  * known text format.
    2018             :  *
    2019             :  * This function relates to the SFCOM IWks::ExportToWKT() method.
    2020             :  * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension
    2021             :  * (Z&M) WKB types.
    2022             :  *
    2023             :  * This function is the same as the CPP method
    2024             :  * OGRGeometry::exportToWkt(wkbVariantIso).
    2025             :  *
    2026             :  * @param hGeom handle on the geometry to convert to a text format from.
    2027             :  * @param ppszSrcText a text buffer is allocated by the program, and assigned
    2028             :  *                    to the passed pointer. After use, *ppszDstText should be
    2029             :  *                    freed with CPLFree().
    2030             :  *
    2031             :  * @return Currently OGRERR_NONE is always returned.
    2032             :  *
    2033             :  */
    2034             : 
    2035        5500 : OGRErr OGR_G_ExportToIsoWkt(OGRGeometryH hGeom, char **ppszSrcText)
    2036             : 
    2037             : {
    2038        5500 :     VALIDATE_POINTER1(hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE);
    2039             : 
    2040        5500 :     return OGRGeometry::FromHandle(hGeom)->exportToWkt(ppszSrcText,
    2041        5500 :                                                        wkbVariantIso);
    2042             : }
    2043             : 
    2044             : /**
    2045             :  * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const;
    2046             :  *
    2047             :  * \brief Fetch geometry type.
    2048             :  *
    2049             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2050             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2051             :  * to the return result.
    2052             :  *
    2053             :  * This method is the same as the C function OGR_G_GetGeometryType().
    2054             :  *
    2055             :  * @return the geometry type code.
    2056             :  */
    2057             : 
    2058             : /************************************************************************/
    2059             : /*                       OGR_G_GetGeometryType()                        */
    2060             : /************************************************************************/
    2061             : /**
    2062             :  * \brief Fetch geometry type.
    2063             :  *
    2064             :  * Note that the geometry type may include the 2.5D flag.  To get a 2D
    2065             :  * flattened version of the geometry type apply the wkbFlatten() macro
    2066             :  * to the return result.
    2067             :  *
    2068             :  * This function is the same as the CPP method OGRGeometry::getGeometryType().
    2069             :  *
    2070             :  * @param hGeom handle on the geometry to get type from.
    2071             :  * @return the geometry type code.
    2072             :  */
    2073             : 
    2074        5794 : OGRwkbGeometryType OGR_G_GetGeometryType(OGRGeometryH hGeom)
    2075             : 
    2076             : {
    2077        5794 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryType", wkbUnknown);
    2078             : 
    2079        5794 :     return OGRGeometry::FromHandle(hGeom)->getGeometryType();
    2080             : }
    2081             : 
    2082             : /**
    2083             :  * \fn const char * OGRGeometry::getGeometryName() const;
    2084             :  *
    2085             :  * \brief Fetch WKT name for geometry type.
    2086             :  *
    2087             :  * There is no SFCOM analog to this method.
    2088             :  *
    2089             :  * This method is the same as the C function OGR_G_GetGeometryName().
    2090             :  *
    2091             :  * @return name used for this geometry type in well known text format.  The
    2092             :  * returned pointer is to a static internal string and should not be modified
    2093             :  * or freed.
    2094             :  */
    2095             : 
    2096             : /************************************************************************/
    2097             : /*                       OGR_G_GetGeometryName()                        */
    2098             : /************************************************************************/
    2099             : /**
    2100             :  * \brief Fetch WKT name for geometry type.
    2101             :  *
    2102             :  * There is no SFCOM analog to this function.
    2103             :  *
    2104             :  * This function is the same as the CPP method OGRGeometry::getGeometryName().
    2105             :  *
    2106             :  * @param hGeom handle on the geometry to get name from.
    2107             :  * @return name used for this geometry type in well known text format.
    2108             :  */
    2109             : 
    2110       18986 : const char *OGR_G_GetGeometryName(OGRGeometryH hGeom)
    2111             : 
    2112             : {
    2113       18986 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetGeometryName", "");
    2114             : 
    2115       18986 :     return OGRGeometry::FromHandle(hGeom)->getGeometryName();
    2116             : }
    2117             : 
    2118             : /**
    2119             :  * \fn OGRGeometry *OGRGeometry::clone() const;
    2120             :  *
    2121             :  * \brief Make a copy of this object.
    2122             :  *
    2123             :  * This method relates to the SFCOM IGeometry::clone() method.
    2124             :  *
    2125             :  * This method is the same as the C function OGR_G_Clone().
    2126             :  *
    2127             :  * @return a new object instance with the same geometry, and spatial
    2128             :  * reference system as the original.
    2129             :  */
    2130             : 
    2131             : /************************************************************************/
    2132             : /*                            OGR_G_Clone()                             */
    2133             : /************************************************************************/
    2134             : /**
    2135             :  * \brief Make a copy of this object.
    2136             :  *
    2137             :  * This function relates to the SFCOM IGeometry::clone() method.
    2138             :  *
    2139             :  * This function is the same as the CPP method OGRGeometry::clone().
    2140             :  *
    2141             :  * @param hGeom handle on the geometry to clone from.
    2142             :  * @return a handle on the copy of the geometry with the spatial
    2143             :  * reference system as the original.
    2144             :  */
    2145             : 
    2146       13574 : OGRGeometryH OGR_G_Clone(OGRGeometryH hGeom)
    2147             : 
    2148             : {
    2149       13574 :     VALIDATE_POINTER1(hGeom, "OGR_G_Clone", nullptr);
    2150             : 
    2151       13574 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->clone());
    2152             : }
    2153             : 
    2154             : /**
    2155             :  * \fn OGRSpatialReference *OGRGeometry::getSpatialReference();
    2156             :  *
    2157             :  * \brief Returns spatial reference system for object.
    2158             :  *
    2159             :  * This method relates to the SFCOM IGeometry::get_SpatialReference() method.
    2160             :  *
    2161             :  * This method is the same as the C function OGR_G_GetSpatialReference().
    2162             :  *
    2163             :  * @return a reference to the spatial reference object.  The object may be
    2164             :  * shared with many geometry objects, and should not be modified.
    2165             :  */
    2166             : 
    2167             : /************************************************************************/
    2168             : /*                     OGR_G_GetSpatialReference()                      */
    2169             : /************************************************************************/
    2170             : /**
    2171             :  * \brief Returns spatial reference system for geometry.
    2172             :  *
    2173             :  * This function relates to the SFCOM IGeometry::get_SpatialReference() method.
    2174             :  *
    2175             :  * This function is the same as the CPP method
    2176             :  * OGRGeometry::getSpatialReference().
    2177             :  *
    2178             :  * @param hGeom handle on the geometry to get spatial reference from.
    2179             :  * @return a reference to the spatial reference geometry, which should not be
    2180             :  * modified.
    2181             :  */
    2182             : 
    2183         126 : OGRSpatialReferenceH OGR_G_GetSpatialReference(OGRGeometryH hGeom)
    2184             : 
    2185             : {
    2186         126 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetSpatialReference", nullptr);
    2187             : 
    2188         126 :     return OGRSpatialReference::ToHandle(const_cast<OGRSpatialReference *>(
    2189         252 :         OGRGeometry::FromHandle(hGeom)->getSpatialReference()));
    2190             : }
    2191             : 
    2192             : /**
    2193             :  * \fn void OGRGeometry::empty();
    2194             :  *
    2195             :  * \brief Clear geometry information.
    2196             :  * This restores the geometry to its initial
    2197             :  * state after construction, and before assignment of actual geometry.
    2198             :  *
    2199             :  * This method relates to the SFCOM IGeometry::Empty() method.
    2200             :  *
    2201             :  * This method is the same as the C function OGR_G_Empty().
    2202             :  */
    2203             : 
    2204             : /************************************************************************/
    2205             : /*                            OGR_G_Empty()                             */
    2206             : /************************************************************************/
    2207             : /**
    2208             :  * \brief Clear geometry information.
    2209             :  * This restores the geometry to its initial
    2210             :  * state after construction, and before assignment of actual geometry.
    2211             :  *
    2212             :  * This function relates to the SFCOM IGeometry::Empty() method.
    2213             :  *
    2214             :  * This function is the same as the CPP method OGRGeometry::empty().
    2215             :  *
    2216             :  * @param hGeom handle on the geometry to empty.
    2217             :  */
    2218             : 
    2219           4 : void OGR_G_Empty(OGRGeometryH hGeom)
    2220             : 
    2221             : {
    2222           4 :     VALIDATE_POINTER0(hGeom, "OGR_G_Empty");
    2223             : 
    2224           4 :     OGRGeometry::FromHandle(hGeom)->empty();
    2225             : }
    2226             : 
    2227             : /**
    2228             :  * \fn bool OGRGeometry::IsEmpty() const;
    2229             :  *
    2230             :  * \brief Returns TRUE (non-zero) if the object has no points.
    2231             :  *
    2232             :  * Normally this
    2233             :  * returns FALSE except between when an object is instantiated and points
    2234             :  * have been assigned.
    2235             :  *
    2236             :  * This method relates to the SFCOM IGeometry::IsEmpty() method.
    2237             :  *
    2238             :  * @return TRUE if object is empty, otherwise FALSE.
    2239             :  */
    2240             : 
    2241             : /************************************************************************/
    2242             : /*                           OGR_G_IsEmpty()                            */
    2243             : /************************************************************************/
    2244             : 
    2245             : /**
    2246             :  * \brief Test if the geometry is empty.
    2247             :  *
    2248             :  * This method is the same as the CPP method OGRGeometry::IsEmpty().
    2249             :  *
    2250             :  * @param hGeom The Geometry to test.
    2251             :  *
    2252             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2253             :  */
    2254             : 
    2255        2391 : int OGR_G_IsEmpty(OGRGeometryH hGeom)
    2256             : 
    2257             : {
    2258        2391 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsEmpty", TRUE);
    2259             : 
    2260        2391 :     return OGRGeometry::FromHandle(hGeom)->IsEmpty();
    2261             : }
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                              IsValid()                               */
    2265             : /************************************************************************/
    2266             : 
    2267             : /**
    2268             :  * \brief Test if the geometry is valid.
    2269             :  *
    2270             :  * This method is the same as the C functions OGR_G_IsValid() and
    2271             :  * OGR_G_GetInvalidityReason().
    2272             :  *
    2273             :  * This method is built on the GEOS library, check it for the definition
    2274             :  * of the geometry operation.
    2275             :  * If OGR is built without the GEOS library, this method will always return
    2276             :  * FALSE.
    2277             :  *
    2278             :  * @param[out] posReason (since 3.13) Pointer to a string to receive the reason
    2279             :  *                       for invalidity, or nullptr. When nullptr, invalidity
    2280             :  *                       reasons are emitted as CPL warnings.
    2281             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2282             :  */
    2283             : 
    2284        2971 : bool OGRGeometry::IsValid(std::string *posReason) const
    2285             : 
    2286             : {
    2287        2971 :     if (posReason)
    2288         851 :         posReason->clear();
    2289             : 
    2290        2971 :     if (IsSFCGALCompatible())
    2291             :     {
    2292             : #ifndef HAVE_SFCGAL
    2293             : 
    2294             : #ifdef HAVE_GEOS
    2295           3 :         if (wkbFlatten(getGeometryType()) == wkbTriangle)
    2296             :         {
    2297             :             // go on
    2298             :         }
    2299             :         else
    2300             : #endif
    2301             :         {
    2302           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2303             :                      "SFCGAL support not enabled.");
    2304           1 :             return FALSE;
    2305             :         }
    2306             : #else
    2307             :         sfcgal_init();
    2308             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    2309             :         if (poThis == nullptr)
    2310             :         {
    2311             :             CPLError(CE_Failure, CPLE_IllegalArg,
    2312             :                      "SFCGAL geometry returned is NULL");
    2313             :             return FALSE;
    2314             :         }
    2315             : 
    2316             :         const int res = sfcgal_geometry_is_valid(poThis);
    2317             :         if (res != 1 && posReason)
    2318             :         {
    2319             :             char *pszReason = nullptr;
    2320             :             sfcgal_geometry_is_valid_detail(poThis, &pszReason, nullptr);
    2321             :             if (pszReason)
    2322             :             {
    2323             :                 *posReason = pszReason;
    2324             :                 free(pszReason);
    2325             :             }
    2326             :             else
    2327             :                 *posReason = "unknown reason";
    2328             :         }
    2329             :         sfcgal_geometry_delete(poThis);
    2330             :         return res == 1;
    2331             : #endif
    2332             :     }
    2333             : 
    2334             :     {
    2335             : #ifndef HAVE_GEOS
    2336             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2337             :         return FALSE;
    2338             : 
    2339             : #else
    2340        2970 :         bool bResult = false;
    2341             : 
    2342             :         // Some invalid geometries, such as lines with one point, or
    2343             :         // rings that do not close, cannot be converted to GEOS.
    2344             :         // For validity checking we initialize the GEOS context with
    2345             :         // the warning handler as the error handler to avoid emitting
    2346             :         // CE_Failure when a geometry cannot be converted to GEOS.
    2347             :         GEOSContextHandle_t hGEOSCtxt =
    2348        2970 :             initGEOS_r(OGRGEOSWarningHandler, OGRGEOSWarningHandler);
    2349             : 
    2350             :         GEOSGeom hThisGeosGeom;
    2351        2970 :         if (posReason)
    2352             :         {
    2353        1702 :             CPLErrorAccumulator oAccumulator;
    2354             :             {
    2355         851 :                 auto oContext = oAccumulator.InstallForCurrentScope();
    2356         851 :                 CPL_IGNORE_RET_VAL(oContext);
    2357         851 :                 hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2358             :             }
    2359         851 :             if (!hThisGeosGeom && oAccumulator.GetErrors().size() == 1)
    2360             :             {
    2361           2 :                 std::string msg = oAccumulator.GetErrors()[0].msg;
    2362             : 
    2363             :                 // Trim GEOS exception name
    2364           1 :                 const auto subMsgPos = msg.find(": ");
    2365           1 :                 if (subMsgPos != std::string::npos)
    2366             :                 {
    2367           1 :                     msg = msg.substr(subMsgPos + strlen(": "));
    2368             :                 }
    2369             : 
    2370             :                 // Trim newline from end of GEOS exception message
    2371           1 :                 if (!msg.empty() && msg.back() == '\n')
    2372             :                 {
    2373           1 :                     msg.pop_back();
    2374             :                 }
    2375             : 
    2376           1 :                 *posReason = std::move(msg);
    2377             :             }
    2378             :         }
    2379             :         else
    2380             :         {
    2381        2119 :             hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2382             :         }
    2383             : 
    2384        2970 :         if (hThisGeosGeom != nullptr)
    2385             :         {
    2386        2969 :             if (posReason)
    2387             :             {
    2388        1700 :                 CPLErrorAccumulator oAccumulator;
    2389             :                 {
    2390         850 :                     auto oContext = oAccumulator.InstallForCurrentScope();
    2391         850 :                     CPL_IGNORE_RET_VAL(oContext);
    2392         850 :                     bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2393             :                 }
    2394         850 :                 if (!bResult && oAccumulator.GetErrors().size() == 1)
    2395             :                 {
    2396          27 :                     *posReason = oAccumulator.GetErrors()[0].msg;
    2397             :                 }
    2398             :             }
    2399             :             else
    2400             :             {
    2401        2119 :                 bResult = GEOSisValid_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2402             :             }
    2403             : #ifdef DEBUG_VERBOSE
    2404             :             if (!bResult && !posReason)
    2405             :             {
    2406             :                 char *pszReason = GEOSisValidReason_r(hGEOSCtxt, hThisGeosGeom);
    2407             :                 CPLDebug("OGR", "%s", pszReason);
    2408             :                 GEOSFree_r(hGEOSCtxt, pszReason);
    2409             :             }
    2410             : #endif
    2411        2969 :             GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2412             :         }
    2413        2970 :         freeGEOSContext(hGEOSCtxt);
    2414             : 
    2415        2970 :         return bResult;
    2416             : 
    2417             : #endif  // HAVE_GEOS
    2418             :     }
    2419             : }
    2420             : 
    2421             : /************************************************************************/
    2422             : /*                           OGR_G_IsValid()                            */
    2423             : /************************************************************************/
    2424             : 
    2425             : /**
    2426             :  * \brief Test if the geometry is valid.
    2427             :  *
    2428             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2429             :  *
    2430             :  * This function is built on the GEOS library, check it for the definition
    2431             :  * of the geometry operation.
    2432             :  * If OGR is built without the GEOS library, this function will always return
    2433             :  * FALSE.
    2434             :  *
    2435             :  * If the geometry is invalid, the reason for its invalidity is emitted as a
    2436             :  * CPL warning. To get it in a string instead, use OGR_G_GetInvalidityReason()
    2437             :  *
    2438             :  * @param hGeom The Geometry to test.
    2439             :  *
    2440             :  * @return TRUE if the geometry is valid, otherwise FALSE.
    2441             :  */
    2442             : 
    2443          22 : int OGR_G_IsValid(OGRGeometryH hGeom)
    2444             : 
    2445             : {
    2446          22 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsValid", FALSE);
    2447             : 
    2448          22 :     return OGRGeometry::FromHandle(hGeom)->IsValid();
    2449             : }
    2450             : 
    2451             : /************************************************************************/
    2452             : /*                     OGR_G_GetInvalidityReason()                      */
    2453             : /************************************************************************/
    2454             : 
    2455             : /**
    2456             :  * \brief Test if the geometry is valid and, if not, return the invalidity reason.
    2457             :  *
    2458             :  * This function is the same as the C++ method OGRGeometry::IsValid().
    2459             :  *
    2460             :  * This function is built on the GEOS library, check it for the definition
    2461             :  * of the geometry operation.
    2462             :  * If OGR is built without the GEOS library, this function will always return
    2463             :  * FALSE.
    2464             :  *
    2465             :  * @param hGeom The Geometry to test.
    2466             :  * @return a string with the invalidity reason, to free with CPLFree(),
    2467             :  * if the geometry is invalid, or nullptr if the geometry is valid.
    2468             :  *
    2469             :  * @since 3.13
    2470             :  */
    2471             : 
    2472           3 : char *OGR_G_GetInvalidityReason(OGRGeometryH hGeom)
    2473             : 
    2474             : {
    2475           3 :     VALIDATE_POINTER1(hGeom, "OGR_G_GetInvalidityReason", nullptr);
    2476             : 
    2477           6 :     std::string osReason;
    2478           3 :     const int nRet = OGRGeometry::FromHandle(hGeom)->IsValid(&osReason);
    2479           3 :     if (osReason.empty())
    2480             :     {
    2481           1 :         if (!nRet)
    2482             :         {
    2483             :             // not sure if that can happen
    2484           0 :             return CPLStrdup("unknown reason");
    2485             :         }
    2486             :         else
    2487           1 :             return nullptr;
    2488             :     }
    2489             :     else
    2490             :     {
    2491           2 :         return CPLStrdup(osReason.c_str());
    2492             :     }
    2493             : }
    2494             : 
    2495             : /************************************************************************/
    2496             : /*                              IsSimple()                              */
    2497             : /************************************************************************/
    2498             : 
    2499             : /**
    2500             :  * \brief Test if the geometry is simple.
    2501             :  *
    2502             :  * This method is the same as the C function OGR_G_IsSimple().
    2503             :  *
    2504             :  * This method is built on the GEOS library, check it for the definition
    2505             :  * of the geometry operation.
    2506             :  * If OGR is built without the GEOS library, this method will always return
    2507             :  * FALSE.
    2508             :  *
    2509             :  *
    2510             :  * @return TRUE if the geometry has no points, otherwise FALSE.
    2511             :  */
    2512             : 
    2513           5 : bool OGRGeometry::IsSimple() const
    2514             : 
    2515             : {
    2516             : #ifndef HAVE_GEOS
    2517             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2518             :     return FALSE;
    2519             : 
    2520             : #else
    2521             : 
    2522           5 :     bool bResult = false;
    2523             : 
    2524           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2525           5 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2526             : 
    2527           5 :     if (hThisGeosGeom != nullptr)
    2528             :     {
    2529           5 :         bResult = GEOSisSimple_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2530           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2531             :     }
    2532           5 :     freeGEOSContext(hGEOSCtxt);
    2533             : 
    2534           5 :     return bResult;
    2535             : 
    2536             : #endif  // HAVE_GEOS
    2537             : }
    2538             : 
    2539             : /**
    2540             :  * \brief Returns TRUE if the geometry is simple.
    2541             :  *
    2542             :  * Returns TRUE if the geometry has no anomalous geometric points, such
    2543             :  * as self intersection or self tangency. The description of each
    2544             :  * instantiable geometric class will include the specific conditions that
    2545             :  * cause an instance of that class to be classified as not simple.
    2546             :  *
    2547             :  * This function is the same as the C++ method OGRGeometry::IsSimple() method.
    2548             :  *
    2549             :  * If OGR is built without the GEOS library, this function will always return
    2550             :  * FALSE.
    2551             :  *
    2552             :  * @param hGeom The Geometry to test.
    2553             :  *
    2554             :  * @return TRUE if object is simple, otherwise FALSE.
    2555             :  */
    2556             : 
    2557           5 : int OGR_G_IsSimple(OGRGeometryH hGeom)
    2558             : 
    2559             : {
    2560           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsSimple", TRUE);
    2561             : 
    2562           5 :     return OGRGeometry::FromHandle(hGeom)->IsSimple();
    2563             : }
    2564             : 
    2565             : /************************************************************************/
    2566             : /*                               IsRing()                               */
    2567             : /************************************************************************/
    2568             : 
    2569             : /**
    2570             :  * \brief Test if the geometry is a ring
    2571             :  *
    2572             :  * This method is the same as the C function OGR_G_IsRing().
    2573             :  *
    2574             :  * This method is built on the GEOS library, check it for the definition
    2575             :  * of the geometry operation.
    2576             :  * If OGR is built without the GEOS library, this method will always return
    2577             :  * FALSE.
    2578             :  *
    2579             :  *
    2580             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2581             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2582             :  */
    2583             : 
    2584           1 : bool OGRGeometry::IsRing() const
    2585             : 
    2586             : {
    2587             : #ifndef HAVE_GEOS
    2588             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    2589             :     return FALSE;
    2590             : 
    2591             : #else
    2592             : 
    2593           1 :     bool bResult = false;
    2594             : 
    2595           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    2596           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    2597             : 
    2598           1 :     if (hThisGeosGeom != nullptr)
    2599             :     {
    2600           1 :         bResult = GEOSisRing_r(hGEOSCtxt, hThisGeosGeom) == 1;
    2601           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    2602             :     }
    2603           1 :     freeGEOSContext(hGEOSCtxt);
    2604             : 
    2605           1 :     return bResult;
    2606             : 
    2607             : #endif  // HAVE_GEOS
    2608             : }
    2609             : 
    2610             : /************************************************************************/
    2611             : /*                            OGR_G_IsRing()                            */
    2612             : /************************************************************************/
    2613             : 
    2614             : /**
    2615             :  * \brief Test if the geometry is a ring
    2616             :  *
    2617             :  * This function is the same as the C++ method OGRGeometry::IsRing().
    2618             :  *
    2619             :  * This function is built on the GEOS library, check it for the definition
    2620             :  * of the geometry operation.
    2621             :  * If OGR is built without the GEOS library, this function will always return
    2622             :  * FALSE.
    2623             :  *
    2624             :  * @param hGeom The Geometry to test.
    2625             :  *
    2626             :  * @return TRUE if the coordinates of the geometry form a ring, by checking
    2627             :  * length and closure (self-intersection is not checked), otherwise FALSE.
    2628             :  */
    2629             : 
    2630           1 : int OGR_G_IsRing(OGRGeometryH hGeom)
    2631             : 
    2632             : {
    2633           1 :     VALIDATE_POINTER1(hGeom, "OGR_G_IsRing", FALSE);
    2634             : 
    2635           1 :     return OGRGeometry::FromHandle(hGeom)->IsRing();
    2636             : }
    2637             : 
    2638             : /************************************************************************/
    2639             : /*                         OGRFromOGCGeomType()                         */
    2640             : /************************************************************************/
    2641             : 
    2642             : /** Map OGC geometry format type to corresponding OGR constants.
    2643             :  * @param pszGeomType POINT[ ][Z][M], LINESTRING[ ][Z][M], etc...
    2644             :  * @return OGR constant.
    2645             :  */
    2646        3267 : OGRwkbGeometryType OGRFromOGCGeomType(const char *pszGeomType)
    2647             : {
    2648        3267 :     OGRwkbGeometryType eType = wkbUnknown;
    2649        3267 :     bool bConvertTo3D = false;
    2650        3267 :     bool bIsMeasured = false;
    2651        3267 :     if (*pszGeomType != '\0')
    2652             :     {
    2653        3261 :         char ch = pszGeomType[strlen(pszGeomType) - 1];
    2654        3261 :         if (ch == 'm' || ch == 'M')
    2655             :         {
    2656           2 :             bIsMeasured = true;
    2657           2 :             if (strlen(pszGeomType) > 1)
    2658           2 :                 ch = pszGeomType[strlen(pszGeomType) - 2];
    2659             :         }
    2660        3261 :         if (ch == 'z' || ch == 'Z')
    2661             :         {
    2662          34 :             bConvertTo3D = true;
    2663             :         }
    2664             :     }
    2665             : 
    2666        3267 :     if (STARTS_WITH_CI(pszGeomType, "POINT"))
    2667         971 :         eType = wkbPoint;
    2668        2296 :     else if (STARTS_WITH_CI(pszGeomType, "LINESTRING"))
    2669         184 :         eType = wkbLineString;
    2670        2112 :     else if (STARTS_WITH_CI(pszGeomType, "POLYGON"))
    2671         943 :         eType = wkbPolygon;
    2672        1169 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOINT"))
    2673          24 :         eType = wkbMultiPoint;
    2674        1145 :     else if (STARTS_WITH_CI(pszGeomType, "MULTILINESTRING"))
    2675          13 :         eType = wkbMultiLineString;
    2676        1132 :     else if (STARTS_WITH_CI(pszGeomType, "MULTIPOLYGON"))
    2677          82 :         eType = wkbMultiPolygon;
    2678        1050 :     else if (STARTS_WITH_CI(pszGeomType, "GEOMETRYCOLLECTION"))
    2679           4 :         eType = wkbGeometryCollection;
    2680        1046 :     else if (STARTS_WITH_CI(pszGeomType, "CIRCULARSTRING"))
    2681         356 :         eType = wkbCircularString;
    2682         690 :     else if (STARTS_WITH_CI(pszGeomType, "COMPOUNDCURVE"))
    2683           0 :         eType = wkbCompoundCurve;
    2684         690 :     else if (STARTS_WITH_CI(pszGeomType, "CURVEPOLYGON"))
    2685          16 :         eType = wkbCurvePolygon;
    2686         674 :     else if (STARTS_WITH_CI(pszGeomType, "MULTICURVE"))
    2687           2 :         eType = wkbMultiCurve;
    2688         672 :     else if (STARTS_WITH_CI(pszGeomType, "MULTISURFACE"))
    2689           0 :         eType = wkbMultiSurface;
    2690         672 :     else if (STARTS_WITH_CI(pszGeomType, "TRIANGLE"))
    2691           0 :         eType = wkbTriangle;
    2692         672 :     else if (STARTS_WITH_CI(pszGeomType, "POLYHEDRALSURFACE"))
    2693           1 :         eType = wkbPolyhedralSurface;
    2694         671 :     else if (STARTS_WITH_CI(pszGeomType, "TIN"))
    2695           5 :         eType = wkbTIN;
    2696         666 :     else if (STARTS_WITH_CI(pszGeomType, "CURVE"))
    2697           3 :         eType = wkbCurve;
    2698         663 :     else if (STARTS_WITH_CI(pszGeomType, "SURFACE"))
    2699           3 :         eType = wkbSurface;
    2700             :     else
    2701         660 :         eType = wkbUnknown;
    2702             : 
    2703        3267 :     if (bConvertTo3D)
    2704          34 :         eType = wkbSetZ(eType);
    2705        3267 :     if (bIsMeasured)
    2706           2 :         eType = wkbSetM(eType);
    2707             : 
    2708        3267 :     return eType;
    2709             : }
    2710             : 
    2711             : /************************************************************************/
    2712             : /*                          OGRToOGCGeomType()                          */
    2713             : /************************************************************************/
    2714             : 
    2715             : /** Map OGR geometry format constants to corresponding OGC geometry type.
    2716             :  * @param eGeomType OGR geometry type
    2717             :  * @param bCamelCase Whether the return should be like "MultiPoint"
    2718             :  *        (bCamelCase=true) or "MULTIPOINT" (bCamelCase=false, default)
    2719             :  * @param bAddZM Whether to include Z, M or ZM suffix for non-2D geometries.
    2720             :  *               Default is false.
    2721             :  * @param bSpaceBeforeZM Whether to include a space character before the Z/M/ZM
    2722             :  *                       suffix. Default is false.
    2723             :  * @return string with OGC geometry type (without dimensionality)
    2724             :  */
    2725        3159 : const char *OGRToOGCGeomType(OGRwkbGeometryType eGeomType, bool bCamelCase,
    2726             :                              bool bAddZM, bool bSpaceBeforeZM)
    2727             : {
    2728        3159 :     const char *pszRet = "";
    2729        3159 :     switch (wkbFlatten(eGeomType))
    2730             :     {
    2731        1699 :         case wkbUnknown:
    2732        1699 :             pszRet = "Geometry";
    2733        1699 :             break;
    2734         562 :         case wkbPoint:
    2735         562 :             pszRet = "Point";
    2736         562 :             break;
    2737         159 :         case wkbLineString:
    2738         159 :             pszRet = "LineString";
    2739         159 :             break;
    2740         404 :         case wkbPolygon:
    2741         404 :             pszRet = "Polygon";
    2742         404 :             break;
    2743          48 :         case wkbMultiPoint:
    2744          48 :             pszRet = "MultiPoint";
    2745          48 :             break;
    2746          56 :         case wkbMultiLineString:
    2747          56 :             pszRet = "MultiLineString";
    2748          56 :             break;
    2749          74 :         case wkbMultiPolygon:
    2750          74 :             pszRet = "MultiPolygon";
    2751          74 :             break;
    2752          54 :         case wkbGeometryCollection:
    2753          54 :             pszRet = "GeometryCollection";
    2754          54 :             break;
    2755           9 :         case wkbCircularString:
    2756           9 :             pszRet = "CircularString";
    2757           9 :             break;
    2758           3 :         case wkbCompoundCurve:
    2759           3 :             pszRet = "CompoundCurve";
    2760           3 :             break;
    2761          12 :         case wkbCurvePolygon:
    2762          12 :             pszRet = "CurvePolygon";
    2763          12 :             break;
    2764           2 :         case wkbMultiCurve:
    2765           2 :             pszRet = "MultiCurve";
    2766           2 :             break;
    2767           3 :         case wkbMultiSurface:
    2768           3 :             pszRet = "MultiSurface";
    2769           3 :             break;
    2770           3 :         case wkbTriangle:
    2771           3 :             pszRet = "Triangle";
    2772           3 :             break;
    2773           5 :         case wkbPolyhedralSurface:
    2774           5 :             pszRet = "PolyhedralSurface";
    2775           5 :             break;
    2776           1 :         case wkbTIN:
    2777           1 :             pszRet = "Tin";
    2778           1 :             break;
    2779           3 :         case wkbCurve:
    2780           3 :             pszRet = "Curve";
    2781           3 :             break;
    2782           3 :         case wkbSurface:
    2783           3 :             pszRet = "Surface";
    2784           3 :             break;
    2785          59 :         default:
    2786          59 :             break;
    2787             :     }
    2788        3159 :     if (bAddZM)
    2789             :     {
    2790          71 :         const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    2791          71 :         const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    2792          71 :         if (bHasZ || bHasM)
    2793             :         {
    2794          10 :             if (bSpaceBeforeZM)
    2795           1 :                 pszRet = CPLSPrintf("%s ", pszRet);
    2796          10 :             if (bHasZ)
    2797           9 :                 pszRet = CPLSPrintf("%sZ", pszRet);
    2798          10 :             if (bHasM)
    2799           5 :                 pszRet = CPLSPrintf("%sM", pszRet);
    2800             :         }
    2801             :     }
    2802        3159 :     if (!bCamelCase)
    2803        3087 :         pszRet = CPLSPrintf("%s", CPLString(pszRet).toupper().c_str());
    2804        3159 :     return pszRet;
    2805             : }
    2806             : 
    2807             : /************************************************************************/
    2808             : /*                       OGRGeometryTypeToName()                        */
    2809             : /************************************************************************/
    2810             : 
    2811             : /**
    2812             :  * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType
    2813             :  * value.  The returned value should not be modified, or freed by the
    2814             :  * application.
    2815             :  *
    2816             :  * This function is C callable.
    2817             :  *
    2818             :  * @param eType the geometry type.
    2819             :  *
    2820             :  * @return internal human readable string, or NULL on failure.
    2821             :  */
    2822             : 
    2823         393 : const char *OGRGeometryTypeToName(OGRwkbGeometryType eType)
    2824             : 
    2825             : {
    2826         393 :     bool b3D = wkbHasZ(eType);
    2827         393 :     bool bMeasured = wkbHasM(eType);
    2828             : 
    2829         393 :     switch (wkbFlatten(eType))
    2830             :     {
    2831          34 :         case wkbUnknown:
    2832          34 :             if (b3D && bMeasured)
    2833           0 :                 return "3D Measured Unknown (any)";
    2834          34 :             else if (b3D)
    2835           1 :                 return "3D Unknown (any)";
    2836          33 :             else if (bMeasured)
    2837           0 :                 return "Measured Unknown (any)";
    2838             :             else
    2839          33 :                 return "Unknown (any)";
    2840             : 
    2841          59 :         case wkbPoint:
    2842          59 :             if (b3D && bMeasured)
    2843           3 :                 return "3D Measured Point";
    2844          56 :             else if (b3D)
    2845          12 :                 return "3D Point";
    2846          44 :             else if (bMeasured)
    2847           5 :                 return "Measured Point";
    2848             :             else
    2849          39 :                 return "Point";
    2850             : 
    2851          37 :         case wkbLineString:
    2852          37 :             if (b3D && bMeasured)
    2853           0 :                 return "3D Measured Line String";
    2854          37 :             else if (b3D)
    2855           9 :                 return "3D Line String";
    2856          28 :             else if (bMeasured)
    2857           0 :                 return "Measured Line String";
    2858             :             else
    2859          28 :                 return "Line String";
    2860             : 
    2861          62 :         case wkbPolygon:
    2862          62 :             if (b3D && bMeasured)
    2863           0 :                 return "3D Measured Polygon";
    2864          62 :             else if (b3D)
    2865           8 :                 return "3D Polygon";
    2866          54 :             else if (bMeasured)
    2867           0 :                 return "Measured Polygon";
    2868             :             else
    2869          54 :                 return "Polygon";
    2870             : 
    2871          19 :         case wkbMultiPoint:
    2872          19 :             if (b3D && bMeasured)
    2873           0 :                 return "3D Measured Multi Point";
    2874          19 :             else if (b3D)
    2875           9 :                 return "3D Multi Point";
    2876          10 :             else if (bMeasured)
    2877           0 :                 return "Measured Multi Point";
    2878             :             else
    2879          10 :                 return "Multi Point";
    2880             : 
    2881          14 :         case wkbMultiLineString:
    2882          14 :             if (b3D && bMeasured)
    2883           0 :                 return "3D Measured Multi Line String";
    2884          14 :             else if (b3D)
    2885           6 :                 return "3D Multi Line String";
    2886           8 :             else if (bMeasured)
    2887           0 :                 return "Measured Multi Line String";
    2888             :             else
    2889           8 :                 return "Multi Line String";
    2890             : 
    2891          19 :         case wkbMultiPolygon:
    2892          19 :             if (b3D && bMeasured)
    2893           0 :                 return "3D Measured Multi Polygon";
    2894          19 :             else if (b3D)
    2895           8 :                 return "3D Multi Polygon";
    2896          11 :             else if (bMeasured)
    2897           0 :                 return "Measured Multi Polygon";
    2898             :             else
    2899          11 :                 return "Multi Polygon";
    2900             : 
    2901          25 :         case wkbGeometryCollection:
    2902          25 :             if (b3D && bMeasured)
    2903           0 :                 return "3D Measured Geometry Collection";
    2904          25 :             else if (b3D)
    2905          10 :                 return "3D Geometry Collection";
    2906          15 :             else if (bMeasured)
    2907           0 :                 return "Measured Geometry Collection";
    2908             :             else
    2909          15 :                 return "Geometry Collection";
    2910             : 
    2911           0 :         case wkbCircularString:
    2912           0 :             if (b3D && bMeasured)
    2913           0 :                 return "3D Measured Circular String";
    2914           0 :             else if (b3D)
    2915           0 :                 return "3D Circular String";
    2916           0 :             else if (bMeasured)
    2917           0 :                 return "Measured Circular String";
    2918             :             else
    2919           0 :                 return "Circular String";
    2920             : 
    2921           1 :         case wkbCompoundCurve:
    2922           1 :             if (b3D && bMeasured)
    2923           0 :                 return "3D Measured Compound Curve";
    2924           1 :             else if (b3D)
    2925           0 :                 return "3D Compound Curve";
    2926           1 :             else if (bMeasured)
    2927           0 :                 return "Measured Compound Curve";
    2928             :             else
    2929           1 :                 return "Compound Curve";
    2930             : 
    2931           0 :         case wkbCurvePolygon:
    2932           0 :             if (b3D && bMeasured)
    2933           0 :                 return "3D Measured Curve Polygon";
    2934           0 :             else if (b3D)
    2935           0 :                 return "3D Curve Polygon";
    2936           0 :             else if (bMeasured)
    2937           0 :                 return "Measured Curve Polygon";
    2938             :             else
    2939           0 :                 return "Curve Polygon";
    2940             : 
    2941           0 :         case wkbMultiCurve:
    2942           0 :             if (b3D && bMeasured)
    2943           0 :                 return "3D Measured Multi Curve";
    2944           0 :             else if (b3D)
    2945           0 :                 return "3D Multi Curve";
    2946           0 :             else if (bMeasured)
    2947           0 :                 return "Measured Multi Curve";
    2948             :             else
    2949           0 :                 return "Multi Curve";
    2950             : 
    2951           0 :         case wkbMultiSurface:
    2952           0 :             if (b3D && bMeasured)
    2953           0 :                 return "3D Measured Multi Surface";
    2954           0 :             else if (b3D)
    2955           0 :                 return "3D Multi Surface";
    2956           0 :             else if (bMeasured)
    2957           0 :                 return "Measured Multi Surface";
    2958             :             else
    2959           0 :                 return "Multi Surface";
    2960             : 
    2961           4 :         case wkbCurve:
    2962           4 :             if (b3D && bMeasured)
    2963           1 :                 return "3D Measured Curve";
    2964           3 :             else if (b3D)
    2965           1 :                 return "3D Curve";
    2966           2 :             else if (bMeasured)
    2967           1 :                 return "Measured Curve";
    2968             :             else
    2969           1 :                 return "Curve";
    2970             : 
    2971           4 :         case wkbSurface:
    2972           4 :             if (b3D && bMeasured)
    2973           1 :                 return "3D Measured Surface";
    2974           3 :             else if (b3D)
    2975           1 :                 return "3D Surface";
    2976           2 :             else if (bMeasured)
    2977           1 :                 return "Measured Surface";
    2978             :             else
    2979           1 :                 return "Surface";
    2980             : 
    2981           0 :         case wkbTriangle:
    2982           0 :             if (b3D && bMeasured)
    2983           0 :                 return "3D Measured Triangle";
    2984           0 :             else if (b3D)
    2985           0 :                 return "3D Triangle";
    2986           0 :             else if (bMeasured)
    2987           0 :                 return "Measured Triangle";
    2988             :             else
    2989           0 :                 return "Triangle";
    2990             : 
    2991           0 :         case wkbPolyhedralSurface:
    2992           0 :             if (b3D && bMeasured)
    2993           0 :                 return "3D Measured PolyhedralSurface";
    2994           0 :             else if (b3D)
    2995           0 :                 return "3D PolyhedralSurface";
    2996           0 :             else if (bMeasured)
    2997           0 :                 return "Measured PolyhedralSurface";
    2998             :             else
    2999           0 :                 return "PolyhedralSurface";
    3000             : 
    3001           2 :         case wkbTIN:
    3002           2 :             if (b3D && bMeasured)
    3003           0 :                 return "3D Measured TIN";
    3004           2 :             else if (b3D)
    3005           0 :                 return "3D TIN";
    3006           2 :             else if (bMeasured)
    3007           0 :                 return "Measured TIN";
    3008             :             else
    3009           2 :                 return "TIN";
    3010             : 
    3011         112 :         case wkbNone:
    3012         112 :             return "None";
    3013             : 
    3014           1 :         default:
    3015             :         {
    3016           1 :             return CPLSPrintf("Unrecognized: %d", static_cast<int>(eType));
    3017             :         }
    3018             :     }
    3019             : }
    3020             : 
    3021             : /************************************************************************/
    3022             : /*                       OGRMergeGeometryTypes()                        */
    3023             : /************************************************************************/
    3024             : 
    3025             : /**
    3026             :  * \brief Find common geometry type.
    3027             :  *
    3028             :  * Given two geometry types, find the most specific common
    3029             :  * type.  Normally used repeatedly with the geometries in a
    3030             :  * layer to try and establish the most specific geometry type
    3031             :  * that can be reported for the layer.
    3032             :  *
    3033             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    3034             :  * geometry types with nothing in common but the base geometry
    3035             :  * type.  wkbNone should be used to indicate that no geometries
    3036             :  * have been encountered yet, and means the first geometry
    3037             :  * encountered will establish the preliminary type.
    3038             :  *
    3039             :  * @param eMain the first input geometry type.
    3040             :  * @param eExtra the second input geometry type.
    3041             :  *
    3042             :  * @return the merged geometry type.
    3043             :  */
    3044             : 
    3045           0 : OGRwkbGeometryType OGRMergeGeometryTypes(OGRwkbGeometryType eMain,
    3046             :                                          OGRwkbGeometryType eExtra)
    3047             : 
    3048             : {
    3049           0 :     return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE);
    3050             : }
    3051             : 
    3052             : /**
    3053             :  * \brief Find common geometry type.
    3054             :  *
    3055             :  * Given two geometry types, find the most specific common
    3056             :  * type.  Normally used repeatedly with the geometries in a
    3057             :  * layer to try and establish the most specific geometry type
    3058             :  * that can be reported for the layer.
    3059             :  *
    3060             :  * NOTE: wkbUnknown is the "worst case" indicating a mixture of
    3061             :  * geometry types with nothing in common but the base geometry
    3062             :  * type.  wkbNone should be used to indicate that no geometries
    3063             :  * have been encountered yet, and means the first geometry
    3064             :  * encountered will establish the preliminary type.
    3065             :  *
    3066             :  * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon
    3067             :  * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve
    3068             :  * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return
    3069             :  * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve.
    3070             :  *
    3071             :  * @param eMain the first input geometry type.
    3072             :  * @param eExtra the second input geometry type.
    3073             :  * @param bAllowPromotingToCurves determine if promotion to curve type
    3074             :  * must be done.
    3075             :  *
    3076             :  * @return the merged geometry type.
    3077             :  *
    3078             :  */
    3079             : 
    3080         575 : OGRwkbGeometryType OGRMergeGeometryTypesEx(OGRwkbGeometryType eMain,
    3081             :                                            OGRwkbGeometryType eExtra,
    3082             :                                            int bAllowPromotingToCurves)
    3083             : 
    3084             : {
    3085         575 :     OGRwkbGeometryType eFMain = wkbFlatten(eMain);
    3086         575 :     OGRwkbGeometryType eFExtra = wkbFlatten(eExtra);
    3087             : 
    3088         575 :     const bool bHasZ = (wkbHasZ(eMain) || wkbHasZ(eExtra));
    3089         575 :     const bool bHasM = (wkbHasM(eMain) || wkbHasM(eExtra));
    3090             : 
    3091         575 :     if (eFMain == wkbUnknown || eFExtra == wkbUnknown)
    3092          17 :         return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3093             : 
    3094         558 :     if (eFMain == wkbNone)
    3095           2 :         return eExtra;
    3096             : 
    3097         556 :     if (eFExtra == wkbNone)
    3098           0 :         return eMain;
    3099             : 
    3100         556 :     if (eFMain == eFExtra)
    3101             :     {
    3102         539 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3103             :     }
    3104             : 
    3105          17 :     if (bAllowPromotingToCurves)
    3106             :     {
    3107          17 :         if (OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra))
    3108           4 :             return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, bHasM);
    3109             : 
    3110          13 :         if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3111           3 :             return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3112             : 
    3113          10 :         if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3114           6 :             return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3115             :     }
    3116             : 
    3117             :     // One is subclass of the other one
    3118           4 :     if (OGR_GT_IsSubClassOf(eFMain, eFExtra))
    3119             :     {
    3120           0 :         return OGR_GT_SetModifier(eFExtra, bHasZ, bHasM);
    3121             :     }
    3122           4 :     else if (OGR_GT_IsSubClassOf(eFExtra, eFMain))
    3123             :     {
    3124           0 :         return OGR_GT_SetModifier(eFMain, bHasZ, bHasM);
    3125             :     }
    3126             : 
    3127             :     // Nothing apparently in common.
    3128           4 :     return OGR_GT_SetModifier(wkbUnknown, bHasZ, bHasM);
    3129             : }
    3130             : 
    3131             : /**
    3132             :  * \fn void OGRGeometry::flattenTo2D();
    3133             :  *
    3134             :  * \brief Convert geometry to strictly 2D.
    3135             :  * In a sense this converts all Z coordinates
    3136             :  * to 0.0.
    3137             :  *
    3138             :  * This method is the same as the C function OGR_G_FlattenTo2D().
    3139             :  */
    3140             : 
    3141             : /************************************************************************/
    3142             : /*                         OGR_G_FlattenTo2D()                          */
    3143             : /************************************************************************/
    3144             : /**
    3145             :  * \brief Convert geometry to strictly 2D.
    3146             :  * In a sense this converts all Z coordinates
    3147             :  * to 0.0.
    3148             :  *
    3149             :  * This function is the same as the CPP method OGRGeometry::flattenTo2D().
    3150             :  *
    3151             :  * @param hGeom handle on the geometry to convert.
    3152             :  */
    3153             : 
    3154          31 : void OGR_G_FlattenTo2D(OGRGeometryH hGeom)
    3155             : 
    3156             : {
    3157          31 :     OGRGeometry::FromHandle(hGeom)->flattenTo2D();
    3158          31 : }
    3159             : 
    3160             : /************************************************************************/
    3161             : /*                            exportToGML()                             */
    3162             : /************************************************************************/
    3163             : 
    3164             : /**
    3165             :  * \fn char *OGRGeometry::exportToGML( const char* const *
    3166             :  * papszOptions = NULL ) const;
    3167             :  *
    3168             :  * \brief Convert a geometry into GML format.
    3169             :  *
    3170             :  * The GML geometry is expressed directly in terms of GML basic data
    3171             :  * types assuming the this is available in the gml namespace.  The returned
    3172             :  * string should be freed with CPLFree() when no longer required.
    3173             :  *
    3174             :  * The supported options are :
    3175             :  * <ul>
    3176             :  * <li> FORMAT=GML2/GML3/GML32.
    3177             :  *      If not set, it will default to GML 2.1.2 output.
    3178             :  * </li>
    3179             :  * <li> GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3)
    3180             :  *      To use gml:Curve element for linestrings.
    3181             :  *      Otherwise gml:LineString will be used .
    3182             :  * </li>
    3183             :  * <li> GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3, deprecated by
    3184             :  *      SRSNAME_FORMAT in GDAL &gt;=2.2). Defaults to YES.
    3185             :  *      If YES, SRS with EPSG authority will be written with the
    3186             :  *      "urn:ogc:def:crs:EPSG::" prefix.
    3187             :  *      In the case the SRS should be treated as lat/long or
    3188             :  *      northing/easting, then the function will take care of coordinate order
    3189             :  *      swapping if the data axis to CRS axis mapping indicates it.
    3190             :  *      If set to NO, SRS with EPSG authority will be written with the "EPSG:"
    3191             :  *      prefix, even if they are in lat/long order.
    3192             :  * </li>
    3193             :  * <li> SRSNAME_FORMAT=SHORT/OGC_URN/OGC_URL (Only valid for FORMAT=GML3).
    3194             :  *      Defaults to OGC_URN.  If SHORT, then srsName will be in
    3195             :  *      the form AUTHORITY_NAME:AUTHORITY_CODE. If OGC_URN, then srsName will be
    3196             :  *      in the form urn:ogc:def:crs:AUTHORITY_NAME::AUTHORITY_CODE. If OGC_URL,
    3197             :  *      then srsName will be in the form
    3198             :  *      http://www.opengis.net/def/crs/AUTHORITY_NAME/0/AUTHORITY_CODE. For
    3199             :  *      OGC_URN and OGC_URL, in the case the SRS should be treated as lat/long
    3200             :  *      or northing/easting, then the function will take care of coordinate
    3201             :  *      order swapping if the data axis to CRS axis mapping indicates it.
    3202             :  * </li>
    3203             :  * <li> GMLID=astring. If specified, a gml:id attribute will be written in the
    3204             :  *      top-level geometry element with the provided value.
    3205             :  *      Required for GML 3.2 compatibility.
    3206             :  * </li>
    3207             :  * <li> SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for
    3208             :  *      FORMAT=GML3/GML32) Default to POSLIST.
    3209             :  *      For 2.5D geometries, define the location where to attach the
    3210             :  *      srsDimension attribute.
    3211             :  *      There are diverging implementations. Some put in on the
    3212             :  *      &lt;gml:posList&gt; element, other on the top geometry element.
    3213             :  * </li>
    3214             :  * <li> NAMESPACE_DECL=YES/NO. If set to YES,
    3215             :  *      xmlns:gml="http://www.opengis.net/gml" will be added to the root node
    3216             :  *      for GML < 3.2 or xmlns:gml="http://www.opengis.net/gml/3.2" for GML 3.2
    3217             :  * </li>
    3218             :  * <li> XY_COORD_RESOLUTION=double (added in GDAL 3.9):
    3219             :  *      Resolution for the coordinate precision of the X and Y coordinates.
    3220             :  *      Expressed in the units of the X and Y axis of the SRS. eg 1e-5 for up
    3221             :  *      to 5 decimal digits. 0 for the default behavior.
    3222             :  * </li>
    3223             :  * <li> Z_COORD_RESOLUTION=double (added in GDAL 3.9):
    3224             :  *      Resolution for the coordinate precision of the Z coordinates.
    3225             :  *      Expressed in the units of the Z axis of the SRS.
    3226             :  *      0 for the default behavior.
    3227             :  * </li>
    3228             :  * </ul>
    3229             :  *
    3230             :  * This method is the same as the C function OGR_G_ExportToGMLEx().
    3231             :  *
    3232             :  * @param papszOptions NULL-terminated list of options.
    3233             :  * @return A GML fragment to be freed with CPLFree() or NULL in case of error.
    3234             :  */
    3235             : 
    3236         251 : char *OGRGeometry::exportToGML(const char *const *papszOptions) const
    3237             : {
    3238         251 :     return OGR_G_ExportToGMLEx(
    3239             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)),
    3240         251 :         const_cast<char **>(papszOptions));
    3241             : }
    3242             : 
    3243             : /************************************************************************/
    3244             : /*                            exportToKML()                             */
    3245             : /************************************************************************/
    3246             : 
    3247             : /**
    3248             :  * \fn char *OGRGeometry::exportToKML() const;
    3249             :  *
    3250             :  * \brief Convert a geometry into KML format.
    3251             :  *
    3252             :  * The returned string should be freed with CPLFree() when no longer required.
    3253             :  *
    3254             :  * This method is the same as the C function OGR_G_ExportToKML().
    3255             :  *
    3256             :  * @return A KML fragment to be freed with CPLFree() or NULL in case of error.
    3257             :  */
    3258             : 
    3259           0 : char *OGRGeometry::exportToKML() const
    3260             : {
    3261           0 :     return OGR_G_ExportToKML(
    3262           0 :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)), nullptr);
    3263             : }
    3264             : 
    3265             : /************************************************************************/
    3266             : /*                            exportToJson()                            */
    3267             : /************************************************************************/
    3268             : 
    3269             : /**
    3270             :  * \fn char *OGRGeometry::exportToJson() const;
    3271             :  *
    3272             :  * \brief Convert a geometry into GeoJSON format.
    3273             :  *
    3274             :  * The returned string should be freed with CPLFree() when no longer required.
    3275             :  *
    3276             :  * The following options are supported :
    3277             :  * <ul>
    3278             :  * <li>XY_COORD_PRECISION=integer: number of decimal figures for X,Y coordinates
    3279             :  * (added in GDAL 3.9)</li>
    3280             :  * <li>Z_COORD_PRECISION=integer: number of decimal figures for Z coordinates
    3281             :  * (added in GDAL 3.9)</li>
    3282             :  * </ul>
    3283             :  *
    3284             :  * This method is the same as the C function OGR_G_ExportToJson().
    3285             :  *
    3286             :  * @param papszOptions Null terminated list of options, or null (added in 3.9)
    3287             :  * @return A GeoJSON fragment to be freed with CPLFree() or NULL in case of error.
    3288             :  */
    3289             : 
    3290          43 : char *OGRGeometry::exportToJson(CSLConstList papszOptions) const
    3291             : {
    3292          43 :     OGRGeometry *poGeometry = const_cast<OGRGeometry *>(this);
    3293          43 :     return OGR_G_ExportToJsonEx(OGRGeometry::ToHandle(poGeometry),
    3294          43 :                                 const_cast<char **>(papszOptions));
    3295             : }
    3296             : 
    3297             : /************************************************************************/
    3298             : /*                 OGRSetGenerate_DB2_V72_BYTE_ORDER()                  */
    3299             : /************************************************************************/
    3300             : 
    3301             : /**
    3302             :  * \brief Special entry point to enable the hack for generating DB2 V7.2 style
    3303             :  * WKB.
    3304             :  *
    3305             :  * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte
    3306             :  * order in WKB.  This entry point is used to turn on or off the generation of
    3307             :  * such WKB.
    3308             :  */
    3309           4 : OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER(int bGenerate_DB2_V72_BYTE_ORDER)
    3310             : 
    3311             : {
    3312             : #if defined(HACK_FOR_IBM_DB2_V72)
    3313           4 :     OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER;
    3314           4 :     return OGRERR_NONE;
    3315             : #else
    3316             :     if (bGenerate_DB2_V72_BYTE_ORDER)
    3317             :         return OGRERR_FAILURE;
    3318             :     else
    3319             :         return OGRERR_NONE;
    3320             : #endif
    3321             : }
    3322             : 
    3323             : /************************************************************************/
    3324             : /*                 OGRGetGenerate_DB2_V72_BYTE_ORDER()                  */
    3325             : /*                                                                      */
    3326             : /*      This is a special entry point to get the value of static flag   */
    3327             : /*      OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER.                      */
    3328             : /************************************************************************/
    3329           0 : int OGRGetGenerate_DB2_V72_BYTE_ORDER()
    3330             : {
    3331           0 :     return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER;
    3332             : }
    3333             : 
    3334             : /************************************************************************/
    3335             : /*                         createGEOSContext()                          */
    3336             : /************************************************************************/
    3337             : 
    3338             : /** Create a new GEOS context.
    3339             :  * @return a new GEOS context (to be freed with freeGEOSContext())
    3340             :  */
    3341       86694 : GEOSContextHandle_t OGRGeometry::createGEOSContext()
    3342             : {
    3343             : #ifndef HAVE_GEOS
    3344             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3345             :     return nullptr;
    3346             : #else
    3347       86694 :     return initGEOS_r(OGRGEOSWarningHandler, OGRGEOSErrorHandler);
    3348             : #endif
    3349             : }
    3350             : 
    3351             : /************************************************************************/
    3352             : /*                          freeGEOSContext()                           */
    3353             : /************************************************************************/
    3354             : 
    3355             : /** Destroy a GEOS context.
    3356             :  * @param hGEOSCtxt GEOS context
    3357             :  */
    3358       84014 : void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt)
    3359             : {
    3360             :     (void)hGEOSCtxt;
    3361             : #ifdef HAVE_GEOS
    3362       84014 :     if (hGEOSCtxt != nullptr)
    3363             :     {
    3364       84014 :         finishGEOS_r(hGEOSCtxt);
    3365             :     }
    3366             : #endif
    3367       84014 : }
    3368             : #ifdef HAVE_GEOS
    3369             : 
    3370             : /************************************************************************/
    3371             : /*                      canConvertToMultiPolygon()                      */
    3372             : /************************************************************************/
    3373             : 
    3374         155 : static bool CanConvertToMultiPolygon(const OGRGeometryCollection *poGC)
    3375             : {
    3376         686 :     for (const auto *poSubGeom : *poGC)
    3377             :     {
    3378             :         const OGRwkbGeometryType eSubGeomType =
    3379         613 :             wkbFlatten(poSubGeom->getGeometryType());
    3380         613 :         if (eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN &&
    3381         505 :             eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon)
    3382             :         {
    3383          82 :             return false;
    3384             :         }
    3385             :     }
    3386             : 
    3387          73 :     return true;
    3388             : }
    3389             : 
    3390             : /************************************************************************/
    3391             : /*                         GEOSWarningSilencer                          */
    3392             : /************************************************************************/
    3393             : 
    3394             : /** Class that can be used to silence GEOS messages while in-scope. */
    3395             : class GEOSWarningSilencer
    3396             : {
    3397             :   public:
    3398         152 :     explicit GEOSWarningSilencer(GEOSContextHandle_t poContext)
    3399         152 :         : m_poContext(poContext)
    3400             :     {
    3401         152 :         GEOSContext_setErrorHandler_r(m_poContext, nullptr);
    3402         152 :         GEOSContext_setNoticeHandler_r(m_poContext, nullptr);
    3403         152 :     }
    3404             : 
    3405         152 :     ~GEOSWarningSilencer()
    3406         152 :     {
    3407         152 :         GEOSContext_setErrorHandler_r(m_poContext, OGRGEOSErrorHandler);
    3408         152 :         GEOSContext_setNoticeHandler_r(m_poContext, OGRGEOSWarningHandler);
    3409         152 :     }
    3410             : 
    3411             :     CPL_DISALLOW_COPY_ASSIGN(GEOSWarningSilencer)
    3412             : 
    3413             :   private:
    3414             :     GEOSContextHandle_t m_poContext{nullptr};
    3415             : };
    3416             : 
    3417             : /************************************************************************/
    3418             : /*                           repairForGEOS()                            */
    3419             : /************************************************************************/
    3420             : 
    3421             : /** Modify an OGRGeometry so that it can be converted into GEOS.
    3422             :  *  Modifications include closing unclosed rings and adding redundant vertices
    3423             :  *  to reach minimum point limits in GEOS.
    3424             :  *
    3425             :  *  It is assumed that the input is a non-curved type that can be
    3426             :  *  represented in GEOS.
    3427             :  *
    3428             :  * @param poGeom the geometry to modify
    3429             :  * @return an OGRGeometry that can be converted to GEOS using WKB
    3430             :  */
    3431          22 : static std::unique_ptr<OGRGeometry> repairForGEOS(const OGRGeometry *poGeom)
    3432             : {
    3433             : #if GEOS_VERSION_MAJOR >= 3 ||                                                 \
    3434             :     (GEOS_VERSION_MINOR == 3 && GEOS_VERSION_MINOR >= 10)
    3435             :     static constexpr int MIN_RING_POINTS = 3;
    3436             : #else
    3437             :     static constexpr int MIN_RING_POINTS = 4;
    3438             : #endif
    3439             : 
    3440          22 :     const auto eType = wkbFlatten(poGeom->getGeometryType());
    3441             : 
    3442          22 :     if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    3443             :     {
    3444           4 :         std::unique_ptr<OGRGeometryCollection> poRet;
    3445           4 :         if (eType == wkbGeometryCollection)
    3446             :         {
    3447           2 :             poRet = std::make_unique<OGRGeometryCollection>();
    3448             :         }
    3449           2 :         else if (eType == wkbMultiPolygon)
    3450             :         {
    3451           2 :             poRet = std::make_unique<OGRMultiPolygon>();
    3452             :         }
    3453           0 :         else if (eType == wkbMultiLineString)
    3454             :         {
    3455           0 :             poRet = std::make_unique<OGRMultiLineString>();
    3456             :         }
    3457           0 :         else if (eType == wkbMultiPoint)
    3458             :         {
    3459           0 :             poRet = std::make_unique<OGRMultiPoint>();
    3460             :         }
    3461             :         else
    3462             :         {
    3463           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3464             :                      "Unexpected geometry type: %s",
    3465             :                      OGRGeometryTypeToName(eType));
    3466           0 :             return nullptr;
    3467             :         }
    3468             : 
    3469           4 :         const OGRGeometryCollection *poColl = poGeom->toGeometryCollection();
    3470          12 :         for (const auto *poSubGeomIn : *poColl)
    3471             :         {
    3472           8 :             std::unique_ptr<OGRGeometry> poSubGeom = repairForGEOS(poSubGeomIn);
    3473           8 :             poRet->addGeometry(std::move(poSubGeom));
    3474             :         }
    3475             : 
    3476           4 :         return poRet;
    3477             :     }
    3478             : 
    3479          18 :     if (eType == wkbPoint)
    3480             :     {
    3481           0 :         return std::unique_ptr<OGRGeometry>(poGeom->clone());
    3482             :     }
    3483          18 :     if (eType == wkbLineString)
    3484             :     {
    3485             :         std::unique_ptr<OGRLineString> poLineString(
    3486           4 :             poGeom->toLineString()->clone());
    3487           2 :         if (poLineString->getNumPoints() == 1)
    3488             :         {
    3489           4 :             OGRPoint oPoint;
    3490           2 :             poLineString->getPoint(0, &oPoint);
    3491           2 :             poLineString->addPoint(&oPoint);
    3492             :         }
    3493           2 :         return poLineString;
    3494             :     }
    3495          16 :     if (eType == wkbPolygon)
    3496             :     {
    3497          32 :         std::unique_ptr<OGRPolygon> poPolygon(poGeom->toPolygon()->clone());
    3498          16 :         poPolygon->closeRings();
    3499             : 
    3500             :         // make sure rings have enough points
    3501          34 :         for (auto *poRing : *poPolygon)
    3502             :         {
    3503          22 :             while (poRing->getNumPoints() < MIN_RING_POINTS)
    3504             :             {
    3505           8 :                 OGRPoint oPoint;
    3506           4 :                 poRing->getPoint(0, &oPoint);
    3507           4 :                 poRing->addPoint(&oPoint);
    3508             :             }
    3509             :         }
    3510             : 
    3511          16 :         return poPolygon;
    3512             :     }
    3513             : 
    3514           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type: %s",
    3515             :              OGRGeometryTypeToName(eType));
    3516           0 :     return nullptr;
    3517             : }
    3518             : 
    3519             : /************************************************************************/
    3520             : /*                         convertToGEOSGeom()                          */
    3521             : /************************************************************************/
    3522             : 
    3523      243470 : static GEOSGeom convertToGEOSGeom(GEOSContextHandle_t hGEOSCtxt,
    3524             :                                   const OGRGeometry *poGeom)
    3525             : {
    3526      243470 :     GEOSGeom hGeom = nullptr;
    3527      243470 :     const size_t nDataSize = poGeom->WkbSize();
    3528             :     unsigned char *pabyData =
    3529      243470 :         static_cast<unsigned char *>(CPLMalloc(nDataSize));
    3530             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    3531             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 12)
    3532      243470 :     OGRwkbVariant eWkbVariant = wkbVariantIso;
    3533             : #else
    3534             :     OGRwkbVariant eWkbVariant = wkbVariantOldOgc;
    3535             : #endif
    3536      243470 :     if (poGeom->exportToWkb(wkbNDR, pabyData, eWkbVariant) == OGRERR_NONE)
    3537             :     {
    3538      242450 :         hGeom = GEOSGeomFromWKB_buf_r(hGEOSCtxt, pabyData, nDataSize);
    3539             :     }
    3540      243470 :     CPLFree(pabyData);
    3541             : 
    3542      243470 :     return hGeom;
    3543             : }
    3544             : #endif
    3545             : 
    3546             : /************************************************************************/
    3547             : /*                            exportToGEOS()                            */
    3548             : /************************************************************************/
    3549             : 
    3550             : /** Returns a GEOSGeom object corresponding to the geometry.
    3551             :  *
    3552             :  * @param hGEOSCtxt GEOS context
    3553             :  * @param bRemoveEmptyParts Whether empty parts of the geometry should be
    3554             :  * removed before exporting to GEOS (GDAL >= 3.10)
    3555             :  * @param bAddPointsIfNeeded Whether to add vertices if needed for the geometry to
    3556             :  * be read by GEOS. Unclosed rings will be closed and duplicate endpoint vertices
    3557             :  * added if needed to satisfy GEOS minimum vertex counts. (GDAL >= 3.13)
    3558             :  * @return a GEOSGeom object corresponding to the geometry (to be freed with
    3559             :  * GEOSGeom_destroy_r()), or NULL in case of error
    3560             :  */
    3561      243456 : GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt,
    3562             :                                    bool bRemoveEmptyParts,
    3563             :                                    bool bAddPointsIfNeeded) const
    3564             : {
    3565             :     (void)hGEOSCtxt;
    3566             :     (void)bRemoveEmptyParts;
    3567             :     (void)bAddPointsIfNeeded;
    3568             : 
    3569             : #ifndef HAVE_GEOS
    3570             : 
    3571             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3572             :     return nullptr;
    3573             : 
    3574             : #else
    3575             : 
    3576      243456 :     if (hGEOSCtxt == nullptr)
    3577           0 :         return nullptr;
    3578             : 
    3579      243456 :     const OGRwkbGeometryType eType = wkbFlatten(getGeometryType());
    3580             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3581             :     // POINT EMPTY is exported to WKB as if it were POINT(0 0),
    3582             :     // so that particular case is necessary.
    3583             :     if (eType == wkbPoint && IsEmpty())
    3584             :     {
    3585             :         return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY");
    3586             :     }
    3587             : #endif
    3588             : 
    3589      243456 :     GEOSGeom hGeom = nullptr;
    3590             : 
    3591      243456 :     std::unique_ptr<OGRGeometry> poModifiedInput = nullptr;
    3592      243456 :     const OGRGeometry *poGeosInput = this;
    3593             : 
    3594      243456 :     const bool bHasZ = poGeosInput->Is3D();
    3595      243456 :     bool bHasM = poGeosInput->IsMeasured();
    3596             : 
    3597      243456 :     if (poGeosInput->hasCurveGeometry())
    3598             :     {
    3599         864 :         poModifiedInput.reset(poGeosInput->getLinearGeometry());
    3600         864 :         poGeosInput = poModifiedInput.get();
    3601             :     }
    3602             : 
    3603      243456 :     if (bRemoveEmptyParts && poGeosInput->hasEmptyParts())
    3604             :     {
    3605           1 :         if (!poModifiedInput)
    3606             :         {
    3607           1 :             poModifiedInput.reset(poGeosInput->clone());
    3608           1 :             poGeosInput = poModifiedInput.get();
    3609             :         }
    3610           1 :         poModifiedInput->removeEmptyParts();
    3611             :     }
    3612             : 
    3613             : #if (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12)
    3614             :     // GEOS < 3.12 doesn't support M dimension
    3615             :     if (bHasM)
    3616             :     {
    3617             :         if (!poModifiedInput)
    3618             :         {
    3619             :             poModifiedInput.reset(poGeosInput->clone());
    3620             :             poGeosInput = poModifiedInput.get();
    3621             :         }
    3622             :         poModifiedInput->setMeasured(false);
    3623             :         bHasM = false;
    3624             :     }
    3625             : #endif
    3626             : 
    3627      243456 :     if (eType == wkbTriangle)
    3628             :     {
    3629             :         poModifiedInput =
    3630          98 :             std::make_unique<OGRPolygon>(*poGeosInput->toPolygon());
    3631          98 :         poGeosInput = poModifiedInput.get();
    3632             :     }
    3633      243358 :     else if (eType == wkbPolyhedralSurface || eType == wkbTIN)
    3634             :     {
    3635         844 :         if (!poModifiedInput)
    3636             :         {
    3637         844 :             poModifiedInput.reset(poGeosInput->clone());
    3638             :         }
    3639             : 
    3640        2532 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3641         844 :             std::move(poModifiedInput),
    3642         844 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM));
    3643         844 :         poGeosInput = poModifiedInput.get();
    3644             :     }
    3645      242669 :     else if (eType == wkbGeometryCollection &&
    3646         155 :              CanConvertToMultiPolygon(poGeosInput->toGeometryCollection()))
    3647             :     {
    3648          73 :         if (!poModifiedInput)
    3649             :         {
    3650          71 :             poModifiedInput.reset(poGeosInput->clone());
    3651             :         }
    3652             : 
    3653             :         // Force into a MultiPolygon, then back to a GeometryCollection.
    3654             :         // This gets rid of fancy types like TIN and PolyhedralSurface that
    3655             :         // GEOS doesn't understand and flattens nested collections.
    3656         219 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3657          73 :             std::move(poModifiedInput),
    3658          73 :             OGR_GT_SetModifier(wkbMultiPolygon, bHasZ, bHasM), nullptr);
    3659         219 :         poModifiedInput = OGRGeometryFactory::forceTo(
    3660          73 :             std::move(poModifiedInput),
    3661          73 :             OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM), nullptr);
    3662             : 
    3663          73 :         poGeosInput = poModifiedInput.get();
    3664             :     }
    3665             : 
    3666             :     {
    3667             :         // Rather than check for conditions that would prevent conversion to
    3668             :         // GEOS (1-point LineStrings, unclosed rings, etc.) we attempt the
    3669             :         // conversion as-is. If the conversion fails, we don't want any
    3670             :         // warnings emitted; we'll repair the input and try again.
    3671           0 :         std::optional<GEOSWarningSilencer> oSilencer;
    3672      243456 :         if (bAddPointsIfNeeded)
    3673             :         {
    3674         152 :             oSilencer.emplace(hGEOSCtxt);
    3675             :         }
    3676             : 
    3677      243456 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3678             :     }
    3679             : 
    3680      243456 :     if (hGeom == nullptr && bAddPointsIfNeeded)
    3681             :     {
    3682          14 :         poModifiedInput = repairForGEOS(poGeosInput);
    3683          14 :         poGeosInput = poModifiedInput.get();
    3684             : 
    3685          14 :         hGeom = convertToGEOSGeom(hGEOSCtxt, poGeosInput);
    3686             :     }
    3687             : 
    3688      243456 :     return hGeom;
    3689             : 
    3690             : #endif  // HAVE_GEOS
    3691             : }
    3692             : 
    3693             : /************************************************************************/
    3694             : /*                          hasCurveGeometry()                          */
    3695             : /************************************************************************/
    3696             : 
    3697             : /**
    3698             :  * \brief Returns if this geometry is or has curve geometry.
    3699             :  *
    3700             :  * Returns if a geometry is, contains or may contain a CIRCULARSTRING,
    3701             :  * COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE or MULTISURFACE.
    3702             :  *
    3703             :  * If bLookForNonLinear is set to TRUE, it will be actually looked if
    3704             :  * the geometry or its subgeometries are or contain a non-linear
    3705             :  * geometry in them. In which case, if the method returns TRUE, it
    3706             :  * means that getLinearGeometry() would return an approximate version
    3707             :  * of the geometry. Otherwise, getLinearGeometry() would do a
    3708             :  * conversion, but with just converting container type, like
    3709             :  * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or
    3710             :  * MULTISURFACE -> MULTIPOLYGON, resulting in a "loss-less"
    3711             :  * conversion.
    3712             :  *
    3713             :  * This method is the same as the C function OGR_G_HasCurveGeometry().
    3714             :  *
    3715             :  * @param bLookForNonLinear set it to TRUE to check if the geometry is
    3716             :  * or contains a CIRCULARSTRING.
    3717             :  *
    3718             :  * @return TRUE if this geometry is or has curve geometry.
    3719             :  *
    3720             :  */
    3721             : 
    3722      280751 : bool OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const
    3723             : {
    3724      280751 :     return FALSE;
    3725             : }
    3726             : 
    3727             : /************************************************************************/
    3728             : /*                         getLinearGeometry()                          */
    3729             : /************************************************************************/
    3730             : 
    3731             : /**
    3732             :  * \brief Return, possibly approximate, non-curve version of this geometry.
    3733             :  *
    3734             :  * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON,
    3735             :  * MULTICURVE or MULTISURFACE in it, by approximating curve geometries.
    3736             :  *
    3737             :  * The ownership of the returned geometry belongs to the caller.
    3738             :  *
    3739             :  * The reverse method is OGRGeometry::getCurveGeometry().
    3740             :  *
    3741             :  * This method is the same as the C function OGR_G_GetLinearGeometry().
    3742             :  *
    3743             :  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
    3744             :  * arc, zero to use the default setting.
    3745             :  * @param papszOptions options as a null-terminated list of strings.
    3746             :  *                     See OGRGeometryFactory::curveToLineString() for
    3747             :  *                     valid options.
    3748             :  *
    3749             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3750             :  *
    3751             :  */
    3752             : 
    3753             : OGRGeometry *
    3754          88 : OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees,
    3755             :                                CPL_UNUSED const char *const *papszOptions) const
    3756             : {
    3757          88 :     return clone();
    3758             : }
    3759             : 
    3760             : /************************************************************************/
    3761             : /*                          getCurveGeometry()                          */
    3762             : /************************************************************************/
    3763             : 
    3764             : /**
    3765             :  * \brief Return curve version of this geometry.
    3766             :  *
    3767             :  * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE,
    3768             :  * CURVEPOLYGON, MULTICURVE or MULTISURFACE in it, by de-approximating
    3769             :  * curve geometries.
    3770             :  *
    3771             :  * If the geometry has no curve portion, the returned geometry will be a clone
    3772             :  * of it.
    3773             :  *
    3774             :  * The ownership of the returned geometry belongs to the caller.
    3775             :  *
    3776             :  * The reverse method is OGRGeometry::getLinearGeometry().
    3777             :  *
    3778             :  * This function is the same as C function OGR_G_GetCurveGeometry().
    3779             :  *
    3780             :  * @param papszOptions options as a null-terminated list of strings.
    3781             :  *                     Unused for now. Must be set to NULL.
    3782             :  *
    3783             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    3784             :  *
    3785             :  */
    3786             : 
    3787             : OGRGeometry *
    3788           5 : OGRGeometry::getCurveGeometry(CPL_UNUSED const char *const *papszOptions) const
    3789             : {
    3790           5 :     return clone();
    3791             : }
    3792             : 
    3793             : /************************************************************************/
    3794             : /*                              Distance()                              */
    3795             : /************************************************************************/
    3796             : 
    3797             : /**
    3798             :  * \brief Compute distance between two geometries.
    3799             :  *
    3800             :  * Returns the shortest distance between the two geometries. The distance is
    3801             :  * expressed into the same unit as the coordinates of the geometries.
    3802             :  *
    3803             :  * This method is the same as the C function OGR_G_Distance().
    3804             :  *
    3805             :  * This method is built on the GEOS library, check it for the definition
    3806             :  * of the geometry operation.
    3807             :  * If OGR is built without the GEOS library, this method will always fail,
    3808             :  * issuing a CPLE_NotSupported error.
    3809             :  *
    3810             :  * @param poOtherGeom the other geometry to compare against.
    3811             :  *
    3812             :  * @return the distance between the geometries or -1 if an error occurs.
    3813             :  */
    3814             : 
    3815          25 : double OGRGeometry::Distance(const OGRGeometry *poOtherGeom) const
    3816             : 
    3817             : {
    3818          25 :     if (nullptr == poOtherGeom)
    3819             :     {
    3820           0 :         CPLDebug("OGR",
    3821             :                  "OGRGeometry::Distance called with NULL geometry pointer");
    3822           0 :         return -1.0;
    3823             :     }
    3824             : 
    3825          25 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    3826             :     {
    3827             : #ifndef HAVE_SFCGAL
    3828             : 
    3829           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3830           0 :         return -1.0;
    3831             : 
    3832             : #else
    3833             : 
    3834             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3835             :         if (poThis == nullptr)
    3836             :             return -1.0;
    3837             : 
    3838             :         sfcgal_geometry_t *poOther =
    3839             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3840             :         if (poOther == nullptr)
    3841             :         {
    3842             :             sfcgal_geometry_delete(poThis);
    3843             :             return -1.0;
    3844             :         }
    3845             : 
    3846             :         const double dfDistance = sfcgal_geometry_distance(poThis, poOther);
    3847             : 
    3848             :         sfcgal_geometry_delete(poThis);
    3849             :         sfcgal_geometry_delete(poOther);
    3850             : 
    3851             :         return dfDistance > 0.0 ? dfDistance : -1.0;
    3852             : 
    3853             : #endif
    3854             :     }
    3855             : 
    3856             :     else
    3857             :     {
    3858             : #ifndef HAVE_GEOS
    3859             : 
    3860             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    3861             :         return -1.0;
    3862             : 
    3863             : #else
    3864             : 
    3865          25 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    3866             :         // GEOSGeom is a pointer
    3867          25 :         GEOSGeom hOther = poOtherGeom->exportToGEOS(hGEOSCtxt);
    3868          25 :         GEOSGeom hThis = exportToGEOS(hGEOSCtxt);
    3869             : 
    3870          25 :         int bIsErr = 0;
    3871          25 :         double dfDistance = 0.0;
    3872             : 
    3873          25 :         if (hThis != nullptr && hOther != nullptr)
    3874             :         {
    3875          25 :             bIsErr = GEOSDistance_r(hGEOSCtxt, hThis, hOther, &dfDistance);
    3876             :         }
    3877             : 
    3878          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hThis);
    3879          25 :         GEOSGeom_destroy_r(hGEOSCtxt, hOther);
    3880          25 :         freeGEOSContext(hGEOSCtxt);
    3881             : 
    3882          25 :         if (bIsErr > 0)
    3883             :         {
    3884          25 :             return dfDistance;
    3885             :         }
    3886             : 
    3887             :         /* Calculations error */
    3888           0 :         return -1.0;
    3889             : 
    3890             : #endif /* HAVE_GEOS */
    3891             :     }
    3892             : }
    3893             : 
    3894             : /************************************************************************/
    3895             : /*                           OGR_G_Distance()                           */
    3896             : /************************************************************************/
    3897             : /**
    3898             :  * \brief Compute distance between two geometries.
    3899             :  *
    3900             :  * Returns the shortest distance between the two geometries. The distance is
    3901             :  * expressed into the same unit as the coordinates of the geometries.
    3902             :  *
    3903             :  * This function is the same as the C++ method OGRGeometry::Distance().
    3904             :  *
    3905             :  * This function is built on the GEOS library, check it for the definition
    3906             :  * of the geometry operation.
    3907             :  * If OGR is built without the GEOS library, this function will always fail,
    3908             :  * issuing a CPLE_NotSupported error.
    3909             :  *
    3910             :  * @param hFirst the first geometry to compare against.
    3911             :  * @param hOther the other geometry to compare against.
    3912             :  *
    3913             :  * @return the distance between the geometries or -1 if an error occurs.
    3914             :  */
    3915             : 
    3916           2 : double OGR_G_Distance(OGRGeometryH hFirst, OGRGeometryH hOther)
    3917             : 
    3918             : {
    3919           2 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance", 0.0);
    3920             : 
    3921           4 :     return OGRGeometry::FromHandle(hFirst)->Distance(
    3922           4 :         OGRGeometry::FromHandle(hOther));
    3923             : }
    3924             : 
    3925             : /************************************************************************/
    3926             : /*                             Distance3D()                             */
    3927             : /************************************************************************/
    3928             : 
    3929             : /**
    3930             :  * \brief Returns the 3D distance between two geometries
    3931             :  *
    3932             :  * The distance is expressed into the same unit as the coordinates of the
    3933             :  * geometries.
    3934             :  *
    3935             :  * This method is built on the SFCGAL library, check it for the definition
    3936             :  * of the geometry operation.
    3937             :  * If OGR is built without the SFCGAL library, this method will always return
    3938             :  * -1.0
    3939             :  *
    3940             :  * This function is the same as the C function OGR_G_Distance3D().
    3941             :  *
    3942             :  * @return distance between the two geometries
    3943             :  */
    3944             : 
    3945           1 : double OGRGeometry::Distance3D(
    3946             :     UNUSED_IF_NO_SFCGAL const OGRGeometry *poOtherGeom) const
    3947             : {
    3948           1 :     if (poOtherGeom == nullptr)
    3949             :     {
    3950           0 :         CPLDebug("OGR",
    3951             :                  "OGRTriangle::Distance3D called with NULL geometry pointer");
    3952           0 :         return -1.0;
    3953             :     }
    3954             : 
    3955           1 :     if (!(poOtherGeom->Is3D() && Is3D()))
    3956             :     {
    3957           0 :         CPLDebug("OGR", "OGRGeometry::Distance3D called with two dimensional "
    3958             :                         "geometry(geometries)");
    3959           0 :         return -1.0;
    3960             :     }
    3961             : 
    3962             : #ifndef HAVE_SFCGAL
    3963             : 
    3964           1 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    3965           1 :     return -1.0;
    3966             : 
    3967             : #else
    3968             : 
    3969             :     sfcgal_init();
    3970             :     sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    3971             :     if (poThis == nullptr)
    3972             :         return -1.0;
    3973             : 
    3974             :     sfcgal_geometry_t *poOther = OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    3975             :     if (poOther == nullptr)
    3976             :     {
    3977             :         sfcgal_geometry_delete(poThis);
    3978             :         return -1.0;
    3979             :     }
    3980             : 
    3981             :     const double dfDistance = sfcgal_geometry_distance_3d(poThis, poOther);
    3982             : 
    3983             :     sfcgal_geometry_delete(poThis);
    3984             :     sfcgal_geometry_delete(poOther);
    3985             : 
    3986             :     return dfDistance > 0 ? dfDistance : -1.0;
    3987             : 
    3988             : #endif
    3989             : }
    3990             : 
    3991             : /************************************************************************/
    3992             : /*                          OGR_G_Distance3D()                          */
    3993             : /************************************************************************/
    3994             : /**
    3995             :  * \brief Returns the 3D distance between two geometries
    3996             :  *
    3997             :  * The distance is expressed into the same unit as the coordinates of the
    3998             :  * geometries.
    3999             :  *
    4000             :  * This method is built on the SFCGAL library, check it for the definition
    4001             :  * of the geometry operation.
    4002             :  * If OGR is built without the SFCGAL library, this method will always return
    4003             :  * -1.0
    4004             :  *
    4005             :  * This function is the same as the C++ method OGRGeometry::Distance3D().
    4006             :  *
    4007             :  * @param hFirst the first geometry to compare against.
    4008             :  * @param hOther the other geometry to compare against.
    4009             :  * @return distance between the two geometries
    4010             :  *
    4011             :  * @return the distance between the geometries or -1 if an error occurs.
    4012             :  */
    4013             : 
    4014           1 : double OGR_G_Distance3D(OGRGeometryH hFirst, OGRGeometryH hOther)
    4015             : 
    4016             : {
    4017           1 :     VALIDATE_POINTER1(hFirst, "OGR_G_Distance3D", 0.0);
    4018             : 
    4019           2 :     return OGRGeometry::FromHandle(hFirst)->Distance3D(
    4020           2 :         OGRGeometry::FromHandle(hOther));
    4021             : }
    4022             : 
    4023             : /************************************************************************/
    4024             : /*                      OGRGeometryRebuildCurves()                      */
    4025             : /************************************************************************/
    4026             : 
    4027             : #ifdef HAVE_GEOS
    4028        5087 : static OGRGeometry *OGRGeometryRebuildCurves(const OGRGeometry *poGeom,
    4029             :                                              const OGRGeometry *poOtherGeom,
    4030             :                                              OGRGeometry *poOGRProduct)
    4031             : {
    4032       10174 :     if (poOGRProduct != nullptr &&
    4033       10091 :         wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint &&
    4034        5004 :         (poGeom->hasCurveGeometry(true) ||
    4035        3849 :          (poOtherGeom && poOtherGeom->hasCurveGeometry(true))))
    4036             :     {
    4037           8 :         OGRGeometry *poCurveGeom = poOGRProduct->getCurveGeometry();
    4038           8 :         delete poOGRProduct;
    4039           8 :         return poCurveGeom;
    4040             :     }
    4041        5079 :     return poOGRProduct;
    4042             : }
    4043             : 
    4044             : /************************************************************************/
    4045             : /*                       BuildGeometryFromGEOS()                        */
    4046             : /************************************************************************/
    4047             : 
    4048        4937 : static OGRGeometry *BuildGeometryFromGEOS(GEOSContextHandle_t hGEOSCtxt,
    4049             :                                           GEOSGeom hGeosProduct,
    4050             :                                           const OGRGeometry *poSelf,
    4051             :                                           const OGRGeometry *poOtherGeom)
    4052             : {
    4053        4937 :     OGRGeometry *poOGRProduct = nullptr;
    4054        4937 :     if (hGeosProduct != nullptr)
    4055             :     {
    4056             :         poOGRProduct =
    4057        4935 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct);
    4058        4935 :         if (poOGRProduct != nullptr &&
    4059       12682 :             poSelf->getSpatialReference() != nullptr &&
    4060        2812 :             (poOtherGeom == nullptr ||
    4061        2812 :              (poOtherGeom->getSpatialReference() != nullptr &&
    4062        2677 :               poOtherGeom->getSpatialReference()->IsSame(
    4063             :                   poSelf->getSpatialReference()))))
    4064             :         {
    4065        2733 :             poOGRProduct->assignSpatialReference(poSelf->getSpatialReference());
    4066             :         }
    4067             :         poOGRProduct =
    4068        4935 :             OGRGeometryRebuildCurves(poSelf, poOtherGeom, poOGRProduct);
    4069        4935 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosProduct);
    4070             :     }
    4071        4937 :     return poOGRProduct;
    4072             : }
    4073             : 
    4074             : /************************************************************************/
    4075             : /*                     BuildGeometryFromTwoGeoms()                      */
    4076             : /************************************************************************/
    4077             : 
    4078        3920 : static OGRGeometry *BuildGeometryFromTwoGeoms(
    4079             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    4080             :     GEOSGeometry *(*pfnGEOSFunction_r)(GEOSContextHandle_t,
    4081             :                                        const GEOSGeometry *,
    4082             :                                        const GEOSGeometry *))
    4083             : {
    4084        3920 :     OGRGeometry *poOGRProduct = nullptr;
    4085             : 
    4086        3920 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    4087        3920 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    4088        3920 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    4089        3920 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    4090             :     {
    4091             :         GEOSGeom hGeosProduct =
    4092        3920 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom);
    4093             : 
    4094             :         poOGRProduct =
    4095        3920 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, poSelf, poOtherGeom);
    4096             :     }
    4097        3920 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    4098        3920 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    4099        3920 :     poSelf->freeGEOSContext(hGEOSCtxt);
    4100             : 
    4101        3920 :     return poOGRProduct;
    4102             : }
    4103             : 
    4104             : /************************************************************************/
    4105             : /*                      OGRGEOSBooleanPredicate()                       */
    4106             : /************************************************************************/
    4107             : 
    4108       22773 : static bool OGRGEOSBooleanPredicate(
    4109             :     const OGRGeometry *poSelf, const OGRGeometry *poOtherGeom,
    4110             :     char (*pfnGEOSFunction_r)(GEOSContextHandle_t, const GEOSGeometry *,
    4111             :                               const GEOSGeometry *))
    4112             : {
    4113       22773 :     bool bResult = false;
    4114             : 
    4115       22773 :     GEOSContextHandle_t hGEOSCtxt = poSelf->createGEOSContext();
    4116       22773 :     GEOSGeom hThisGeosGeom = poSelf->exportToGEOS(hGEOSCtxt);
    4117       22773 :     GEOSGeom hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt);
    4118       22773 :     if (hThisGeosGeom != nullptr && hOtherGeosGeom != nullptr)
    4119             :     {
    4120       22214 :         bResult =
    4121       22214 :             pfnGEOSFunction_r(hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom) == 1;
    4122             :     }
    4123       22773 :     GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    4124       22773 :     GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    4125       22773 :     poSelf->freeGEOSContext(hGEOSCtxt);
    4126             : 
    4127       22773 :     return bResult;
    4128             : }
    4129             : 
    4130             : #endif  // HAVE_GEOS
    4131             : 
    4132             : /************************************************************************/
    4133             : /*                             MakeValid()                              */
    4134             : /************************************************************************/
    4135             : 
    4136             : /**
    4137             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4138             :  *
    4139             :  * Already-valid geometries are cloned without further intervention
    4140             :  * for default MODE=LINEWORK. Already-valid geometries with MODE=STRUCTURE
    4141             :  * may be subject to non-significant transformations, such as duplicated point
    4142             :  * removal, change in ring winding order, etc. (before GDAL 3.10, single-part
    4143             :  * geometry collections could be returned a single geometry. GDAL 3.10
    4144             :  * returns the same type of geometry).
    4145             :  *
    4146             :  * Running OGRGeometryFactory::removeLowerDimensionSubGeoms() as a
    4147             :  * post-processing step is often desired.
    4148             :  *
    4149             :  * This method is the same as the C function OGR_G_MakeValid().
    4150             :  *
    4151             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4152             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4153             :  * library, this function will return a clone of the input geometry if it is
    4154             :  * valid, or NULL if it is invalid.
    4155             :  *
    4156             :  * Certain geometries cannot be read using GEOS, for example if Polygon rings
    4157             :  * are not closed or do not contain enough vertices. If a geometry cannot be
    4158             :  * read by GEOS, NULL will be returned. Starting with GDAL 3.13, GDAL will
    4159             :  * attempt to modify these geometries such that they can be read and
    4160             :  * repaired by GEOS.
    4161             :  *
    4162             :  * @param papszOptions NULL terminated list of options, or NULL. The following
    4163             :  * options are available:
    4164             :  * <ul>
    4165             :  * <li>METHOD=LINEWORK/STRUCTURE.
    4166             :  *     LINEWORK is the default method, which combines all rings into a set of
    4167             :  *     noded lines and then extracts valid polygons from that linework.
    4168             :  *     The STRUCTURE method (requires GEOS >= 3.10 and GDAL >= 3.4) first makes
    4169             :  *     all rings valid, then merges shells and
    4170             :  *     subtracts holes from shells to generate valid result. Assumes that
    4171             :  *     holes and shells are correctly categorized.</li>
    4172             :  * <li>KEEP_COLLAPSED=YES/NO. Only for METHOD=STRUCTURE.
    4173             :  *     NO (default): collapses are converted to empty geometries
    4174             :  *     YES: collapses are converted to a valid geometry of lower dimension.</li>
    4175             :  * </ul>
    4176             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4177             :  *
    4178             :  * @since GDAL 3.0
    4179             :  */
    4180         153 : OGRGeometry *OGRGeometry::MakeValid(CSLConstList papszOptions) const
    4181             : {
    4182             :     (void)papszOptions;
    4183             : #ifndef HAVE_GEOS
    4184             :     if (IsValid())
    4185             :         return clone();
    4186             : 
    4187             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4188             :     return nullptr;
    4189             : #else
    4190         153 :     if (IsSFCGALCompatible())
    4191             :     {
    4192           0 :         if (IsValid())
    4193           0 :             return clone();
    4194             :     }
    4195         153 :     else if (wkbFlatten(getGeometryType()) == wkbCurvePolygon)
    4196             :     {
    4197           3 :         GEOSContextHandle_t hGEOSCtxt = initGEOS_r(nullptr, nullptr);
    4198           3 :         bool bIsValid = false;
    4199           3 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4200           3 :         if (hGeosGeom)
    4201             :         {
    4202           3 :             bIsValid = GEOSisValid_r(hGEOSCtxt, hGeosGeom) == 1;
    4203           3 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4204             :         }
    4205           3 :         freeGEOSContext(hGEOSCtxt);
    4206           3 :         if (bIsValid)
    4207           1 :             return clone();
    4208             :     }
    4209             : 
    4210         152 :     const bool bStructureMethod = EQUAL(
    4211             :         CSLFetchNameValueDef(papszOptions, "METHOD", "LINEWORK"), "STRUCTURE");
    4212         152 :     CPL_IGNORE_RET_VAL(bStructureMethod);
    4213             : #if !(GEOS_VERSION_MAJOR > 3 ||                                                \
    4214             :       (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    4215             :     if (bStructureMethod)
    4216             :     {
    4217             :         CPLError(CE_Failure, CPLE_NotSupported,
    4218             :                  "GEOS 3.10 or later needed for METHOD=STRUCTURE.");
    4219             :         return nullptr;
    4220             :     }
    4221             : #endif
    4222             : 
    4223         152 :     OGRGeometry *poOGRProduct = nullptr;
    4224             : 
    4225         152 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4226         152 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt, false, true);
    4227         152 :     if (hGeosGeom != nullptr)
    4228             :     {
    4229             :         GEOSGeom hGEOSRet;
    4230             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4231             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4232         152 :         if (bStructureMethod)
    4233             :         {
    4234             :             GEOSMakeValidParams *params =
    4235          15 :                 GEOSMakeValidParams_create_r(hGEOSCtxt);
    4236          15 :             CPLAssert(params);
    4237          15 :             GEOSMakeValidParams_setMethod_r(hGEOSCtxt, params,
    4238             :                                             GEOS_MAKE_VALID_STRUCTURE);
    4239          15 :             GEOSMakeValidParams_setKeepCollapsed_r(
    4240             :                 hGEOSCtxt, params,
    4241          15 :                 CPLFetchBool(papszOptions, "KEEP_COLLAPSED", false));
    4242          15 :             hGEOSRet = GEOSMakeValidWithParams_r(hGEOSCtxt, hGeosGeom, params);
    4243          15 :             GEOSMakeValidParams_destroy_r(hGEOSCtxt, params);
    4244             :         }
    4245             :         else
    4246             : #endif
    4247             :         {
    4248         137 :             hGEOSRet = GEOSMakeValid_r(hGEOSCtxt, hGeosGeom);
    4249             :         }
    4250         152 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4251             : 
    4252         152 :         if (hGEOSRet != nullptr)
    4253             :         {
    4254             :             poOGRProduct =
    4255         152 :                 OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGEOSRet);
    4256         152 :             if (poOGRProduct != nullptr && getSpatialReference() != nullptr)
    4257           6 :                 poOGRProduct->assignSpatialReference(getSpatialReference());
    4258             :             poOGRProduct =
    4259         152 :                 OGRGeometryRebuildCurves(this, nullptr, poOGRProduct);
    4260         152 :             GEOSGeom_destroy_r(hGEOSCtxt, hGEOSRet);
    4261             : 
    4262             : #if GEOS_VERSION_MAJOR > 3 ||                                                  \
    4263             :     (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10)
    4264             :             // METHOD=STRUCTURE is not guaranteed to return a multiple geometry
    4265             :             // if the input is a multiple geometry
    4266         152 :             if (poOGRProduct && bStructureMethod &&
    4267         310 :                 OGR_GT_IsSubClassOf(getGeometryType(), wkbGeometryCollection) &&
    4268           6 :                 !OGR_GT_IsSubClassOf(poOGRProduct->getGeometryType(),
    4269             :                                      wkbGeometryCollection))
    4270             :             {
    4271           6 :                 poOGRProduct = OGRGeometryFactory::forceTo(
    4272           6 :                                    std::unique_ptr<OGRGeometry>(poOGRProduct),
    4273           3 :                                    getGeometryType())
    4274           3 :                                    .release();
    4275             :             }
    4276             : #endif
    4277             :         }
    4278             :     }
    4279         152 :     freeGEOSContext(hGEOSCtxt);
    4280             : 
    4281         152 :     return poOGRProduct;
    4282             : #endif
    4283             : }
    4284             : 
    4285             : /************************************************************************/
    4286             : /*                          OGR_G_MakeValid()                           */
    4287             : /************************************************************************/
    4288             : 
    4289             : /**
    4290             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4291             :  *
    4292             :  * Already-valid geometries are cloned without further intervention.
    4293             :  *
    4294             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4295             :  *
    4296             :  * This function is built on the GEOS >= 3.8 library, check it for the
    4297             :  * definition of the geometry operation. If OGR is built without the GEOS >= 3.8
    4298             :  * library, this function will return a clone of the input geometry if it is
    4299             :  * valid, or NULL if it is invalid
    4300             :  *
    4301             :  * @param hGeom The Geometry to make valid.
    4302             :  *
    4303             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4304             :  * or NULL if an error occurs.
    4305             :  *
    4306             :  * @since GDAL 3.0
    4307             :  */
    4308             : 
    4309           0 : OGRGeometryH OGR_G_MakeValid(OGRGeometryH hGeom)
    4310             : 
    4311             : {
    4312           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValid", nullptr);
    4313             : 
    4314           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->MakeValid());
    4315             : }
    4316             : 
    4317             : /************************************************************************/
    4318             : /*                         OGR_G_MakeValidEx()                          */
    4319             : /************************************************************************/
    4320             : 
    4321             : /**
    4322             :  * \brief Attempts to make an invalid geometry valid without losing vertices.
    4323             :  *
    4324             :  * Already-valid geometries are cloned without further intervention.
    4325             :  *
    4326             :  * This function is the same as the C++ method OGRGeometry::MakeValid().
    4327             :  *
    4328             :  * See documentation of that method for possible options.
    4329             :  *
    4330             :  * @param hGeom The Geometry to make valid.
    4331             :  * @param papszOptions Options.
    4332             :  *
    4333             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4334             :  * or NULL if an error occurs.
    4335             :  *
    4336             :  * @since GDAL 3.4
    4337             :  */
    4338             : 
    4339          25 : OGRGeometryH OGR_G_MakeValidEx(OGRGeometryH hGeom, CSLConstList papszOptions)
    4340             : 
    4341             : {
    4342          25 :     VALIDATE_POINTER1(hGeom, "OGR_G_MakeValidEx", nullptr);
    4343             : 
    4344          25 :     return OGRGeometry::ToHandle(
    4345          50 :         OGRGeometry::FromHandle(hGeom)->MakeValid(papszOptions));
    4346             : }
    4347             : 
    4348             : /************************************************************************/
    4349             : /*                             Normalize()                              */
    4350             : /************************************************************************/
    4351             : 
    4352             : /**
    4353             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4354             :  *
    4355             :  * This method is the same as the C function OGR_G_Normalize().
    4356             :  *
    4357             :  * This function is built on the GEOS library; check it for the definition
    4358             :  * of the geometry operation.
    4359             :  * If OGR is built without the GEOS library, this function will always fail,
    4360             :  * issuing a CPLE_NotSupported error.
    4361             :  *
    4362             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4363             :  *
    4364             :  * @since GDAL 3.3
    4365             :  */
    4366          51 : OGRGeometry *OGRGeometry::Normalize() const
    4367             : {
    4368             : #ifndef HAVE_GEOS
    4369             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4370             :     return nullptr;
    4371             : #else
    4372          51 :     OGRGeometry *poOGRProduct = nullptr;
    4373             : 
    4374          51 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4375          51 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4376          51 :     if (hGeosGeom != nullptr)
    4377             :     {
    4378             : 
    4379          51 :         int hGEOSRet = GEOSNormalize_r(hGEOSCtxt, hGeosGeom);
    4380             : 
    4381          51 :         if (hGEOSRet == 0)
    4382             :         {
    4383             :             poOGRProduct =
    4384          51 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosGeom, this, nullptr);
    4385             :         }
    4386             :         else
    4387             :         {
    4388           0 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4389             :         }
    4390             :     }
    4391          51 :     freeGEOSContext(hGEOSCtxt);
    4392             : 
    4393          51 :     return poOGRProduct;
    4394             : #endif
    4395             : }
    4396             : 
    4397             : /************************************************************************/
    4398             : /*                          OGR_G_Normalize()                           */
    4399             : /************************************************************************/
    4400             : 
    4401             : /**
    4402             :  * \brief Attempts to bring geometry into normalized/canonical form.
    4403             :  *
    4404             :  * This function is the same as the C++ method OGRGeometry::Normalize().
    4405             :  *
    4406             :  * This function is built on the GEOS library; check it for the definition
    4407             :  * of the geometry operation.
    4408             :  * If OGR is built without the GEOS library, this function will always fail,
    4409             :  * issuing a CPLE_NotSupported error.
    4410             :  * @param hGeom The Geometry to normalize.
    4411             :  *
    4412             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4413             :  * or NULL if an error occurs.
    4414             :  *
    4415             :  * @since GDAL 3.3
    4416             :  */
    4417             : 
    4418          21 : OGRGeometryH OGR_G_Normalize(OGRGeometryH hGeom)
    4419             : 
    4420             : {
    4421          21 :     VALIDATE_POINTER1(hGeom, "OGR_G_Normalize", nullptr);
    4422             : 
    4423          21 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->Normalize());
    4424             : }
    4425             : 
    4426             : /************************************************************************/
    4427             : /*                             ConvexHull()                             */
    4428             : /************************************************************************/
    4429             : 
    4430             : /**
    4431             :  * \brief Compute convex hull.
    4432             :  *
    4433             :  * A new geometry object is created and returned containing the convex
    4434             :  * hull of the geometry on which the method is invoked.
    4435             :  *
    4436             :  * This method is the same as the C function OGR_G_ConvexHull().
    4437             :  *
    4438             :  * This method is built on the GEOS library, check it for the definition
    4439             :  * of the geometry operation.
    4440             :  * If OGR is built without the GEOS library, this method will always fail,
    4441             :  * issuing a CPLE_NotSupported error.
    4442             :  *
    4443             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4444             :  */
    4445             : 
    4446           6 : OGRGeometry *OGRGeometry::ConvexHull() const
    4447             : 
    4448             : {
    4449           6 :     if (IsSFCGALCompatible())
    4450             :     {
    4451             : #ifndef HAVE_SFCGAL
    4452             : 
    4453           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    4454           0 :         return nullptr;
    4455             : 
    4456             : #else
    4457             : 
    4458             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    4459             :         if (poThis == nullptr)
    4460             :             return nullptr;
    4461             : 
    4462             :         sfcgal_geometry_t *poRes = sfcgal_geometry_convexhull_3d(poThis);
    4463             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    4464             :         if (h_prodGeom)
    4465             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    4466             : 
    4467             :         sfcgal_geometry_delete(poThis);
    4468             :         sfcgal_geometry_delete(poRes);
    4469             : 
    4470             :         return h_prodGeom;
    4471             : 
    4472             : #endif
    4473             :     }
    4474             : 
    4475             :     else
    4476             :     {
    4477             : #ifndef HAVE_GEOS
    4478             : 
    4479             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4480             :         return nullptr;
    4481             : 
    4482             : #else
    4483             : 
    4484           6 :         OGRGeometry *poOGRProduct = nullptr;
    4485             : 
    4486           6 :         GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4487           6 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4488           6 :         if (hGeosGeom != nullptr)
    4489             :         {
    4490           6 :             GEOSGeom hGeosHull = GEOSConvexHull_r(hGEOSCtxt, hGeosGeom);
    4491           6 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4492             : 
    4493             :             poOGRProduct =
    4494           6 :                 BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4495             :         }
    4496           6 :         freeGEOSContext(hGEOSCtxt);
    4497             : 
    4498           6 :         return poOGRProduct;
    4499             : 
    4500             : #endif /* HAVE_GEOS */
    4501             :     }
    4502             : }
    4503             : 
    4504             : /************************************************************************/
    4505             : /*                          OGR_G_ConvexHull()                          */
    4506             : /************************************************************************/
    4507             : /**
    4508             :  * \brief Compute convex hull.
    4509             :  *
    4510             :  * A new geometry object is created and returned containing the convex
    4511             :  * hull of the geometry on which the method is invoked.
    4512             :  *
    4513             :  * This function is the same as the C++ method OGRGeometry::ConvexHull().
    4514             :  *
    4515             :  * This function is built on the GEOS library, check it for the definition
    4516             :  * of the geometry operation.
    4517             :  * If OGR is built without the GEOS library, this function will always fail,
    4518             :  * issuing a CPLE_NotSupported error.
    4519             :  *
    4520             :  * @param hTarget The Geometry to calculate the convex hull of.
    4521             :  *
    4522             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4523             :  * or NULL if an error occurs.
    4524             :  */
    4525             : 
    4526           1 : OGRGeometryH OGR_G_ConvexHull(OGRGeometryH hTarget)
    4527             : 
    4528             : {
    4529           1 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConvexHull", nullptr);
    4530             : 
    4531           1 :     return OGRGeometry::ToHandle(
    4532           1 :         OGRGeometry::FromHandle(hTarget)->ConvexHull());
    4533             : }
    4534             : 
    4535             : /************************************************************************/
    4536             : /*                            ConcaveHull()                             */
    4537             : /************************************************************************/
    4538             : 
    4539             : /**
    4540             :  * \brief Compute the concave hull of a geometry.
    4541             :  *
    4542             :  * The concave hull is fully contained within the convex hull and also
    4543             :  * contains all the points of the input, but in a smaller area.
    4544             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4545             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4546             :  * that contains all the points in the input Geometry.
    4547             :  *
    4548             :  * A new geometry object is created and returned containing the concave
    4549             :  * hull of the geometry on which the method is invoked.
    4550             :  *
    4551             :  * This method is the same as the C function OGR_G_ConcaveHull().
    4552             :  *
    4553             :  * This method is built on the GEOS >= 3.11 library
    4554             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4555             :  * fail, issuing a CPLE_NotSupported error.
    4556             :  *
    4557             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4558             :  * @param bAllowHoles Whether holes are allowed.
    4559             :  *
    4560             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4561             :  *
    4562             :  * @since GDAL 3.6
    4563             :  * @see OGRGeometry::ConcaveHullOfPolygons()
    4564             :  */
    4565             : 
    4566           8 : OGRGeometry *OGRGeometry::ConcaveHull(double dfRatio, bool bAllowHoles) const
    4567             : {
    4568             : #ifndef HAVE_GEOS
    4569             :     (void)dfRatio;
    4570             :     (void)bAllowHoles;
    4571             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4572             :     return nullptr;
    4573             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4574             :     (void)dfRatio;
    4575             :     (void)bAllowHoles;
    4576             :     CPLError(CE_Failure, CPLE_NotSupported,
    4577             :              "GEOS 3.11 or later needed for ConcaveHull.");
    4578             :     return nullptr;
    4579             : #else
    4580           8 :     OGRGeometry *poOGRProduct = nullptr;
    4581             : 
    4582           8 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4583           8 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4584           8 :     if (hGeosGeom != nullptr)
    4585             :     {
    4586             :         GEOSGeom hGeosHull =
    4587           8 :             GEOSConcaveHull_r(hGEOSCtxt, hGeosGeom, dfRatio, bAllowHoles);
    4588           8 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4589             : 
    4590             :         poOGRProduct =
    4591           8 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4592             :     }
    4593           8 :     freeGEOSContext(hGEOSCtxt);
    4594             : 
    4595           8 :     return poOGRProduct;
    4596             : #endif /* HAVE_GEOS */
    4597             : }
    4598             : 
    4599             : /************************************************************************/
    4600             : /*                         OGR_G_ConcaveHull()                          */
    4601             : /************************************************************************/
    4602             : /**
    4603             :  * \brief Compute the concave hull of a geometry.
    4604             :  *
    4605             :  * The concave hull is fully contained within the convex hull and also
    4606             :  * contains all the points of the input, but in a smaller area.
    4607             :  * The area ratio is the ratio of the area of the convex hull and the concave
    4608             :  * hull. Frequently used to convert a multi-point into a polygonal area.
    4609             :  * that contains all the points in the input Geometry.
    4610             :  *
    4611             :  * A new geometry object is created and returned containing the convex
    4612             :  * hull of the geometry on which the function is invoked.
    4613             :  *
    4614             :  * This function is the same as the C++ method OGRGeometry::ConcaveHull().
    4615             :  *
    4616             :  * This function is built on the GEOS >= 3.11 library
    4617             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4618             :  * fail, issuing a CPLE_NotSupported error.
    4619             :  *
    4620             :  * @param hTarget The Geometry to calculate the concave hull of.
    4621             :  * @param dfRatio Ratio of the area of the convex hull and the concave hull.
    4622             :  * @param bAllowHoles Whether holes are allowed.
    4623             :  *
    4624             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4625             :  * or NULL if an error occurs.
    4626             :  *
    4627             :  * @since GDAL 3.6
    4628             :  * @see OGR_G_ConcaveHullOfPolygons()
    4629             :  */
    4630             : 
    4631           2 : OGRGeometryH OGR_G_ConcaveHull(OGRGeometryH hTarget, double dfRatio,
    4632             :                                bool bAllowHoles)
    4633             : 
    4634             : {
    4635           2 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHull", nullptr);
    4636             : 
    4637           2 :     return OGRGeometry::ToHandle(
    4638           2 :         OGRGeometry::FromHandle(hTarget)->ConcaveHull(dfRatio, bAllowHoles));
    4639             : }
    4640             : 
    4641             : /************************************************************************/
    4642             : /*                       ConcaveHullOfPolygons()                        */
    4643             : /************************************************************************/
    4644             : 
    4645             : /**
    4646             :  * \brief Compute the concave hull of a set of polygons, respecting
    4647             :  * the polygons as constraints.
    4648             :  *
    4649             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4650             :  * polygons.
    4651             :  * The computed hull "fills the gap" between the polygons,
    4652             :  * and does not intersect their interior.
    4653             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4654             :  * determined by a numeric target parameter.
    4655             :  *
    4656             :  * The concave hull is constructed by removing the longest outer edges
    4657             :  * of the Delaunay Triangulation of the space between the polygons,
    4658             :  * until the target criterion parameter is reached.
    4659             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4660             :  * between polygons to be no larger than this value.
    4661             :  * This can be expressed as a ratio between the lengths of the longest and
    4662             :  * shortest edges.
    4663             :  *
    4664             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4665             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4666             :  * for more details.
    4667             :  *
    4668             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4669             :  * be non-overlapping).
    4670             :  *
    4671             :  * A new geometry object is created and returned containing the concave
    4672             :  * hull of the geometry on which the method is invoked.
    4673             :  *
    4674             :  * This method is the same as the C function OGR_G_ConcaveHullOfPolygons().
    4675             :  *
    4676             :  * This method is built on the GEOS >= 3.11 library
    4677             :  * If OGR is built without the GEOS >= 3.11 library, this method will always
    4678             :  * fail, issuing a CPLE_NotSupported error.
    4679             :  *
    4680             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4681             :  *                      difference between the longest and shortest edge lengths
    4682             :  *                      between the polygons.
    4683             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4684             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4685             :  *                      the original polygons.
    4686             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4687             :  *                 polygons.
    4688             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4689             :  *
    4690             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4691             :  *
    4692             :  * @since GDAL 3.13
    4693             :  * @see OGRGeometry::ConcaveHull()
    4694             :  */
    4695             : 
    4696          13 : OGRGeometry *OGRGeometry::ConcaveHullOfPolygons(double dfLengthRatio,
    4697             :                                                 bool bIsTight,
    4698             :                                                 bool bAllowHoles) const
    4699             : {
    4700             : #ifndef HAVE_GEOS
    4701             :     (void)dfLengthRatio;
    4702             :     (void)bIsTight;
    4703             :     (void)bAllowHoles;
    4704             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4705             :     return nullptr;
    4706             : #elif GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    4707             :     (void)dfLengthRatio;
    4708             :     (void)bIsTight;
    4709             :     (void)bAllowHoles;
    4710             :     CPLError(CE_Failure, CPLE_NotSupported,
    4711             :              "GEOS 3.11 or later needed for ConcaveHullOfPolygons.");
    4712             :     return nullptr;
    4713             : #else
    4714          13 :     OGRGeometry *poOGRProduct = nullptr;
    4715             : 
    4716          13 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4717          13 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4718          13 :     if (hGeosGeom != nullptr)
    4719             :     {
    4720          13 :         GEOSGeom hGeosHull = GEOSConcaveHullOfPolygons_r(
    4721             :             hGEOSCtxt, hGeosGeom, dfLengthRatio, bIsTight, bAllowHoles);
    4722          13 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4723             : 
    4724             :         poOGRProduct =
    4725          13 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosHull, this, nullptr);
    4726             :     }
    4727          13 :     freeGEOSContext(hGEOSCtxt);
    4728             : 
    4729          13 :     return poOGRProduct;
    4730             : #endif /* HAVE_GEOS */
    4731             : }
    4732             : 
    4733             : /************************************************************************/
    4734             : /*                    OGR_G_ConcaveHullOfPolygons()                     */
    4735             : /************************************************************************/
    4736             : /**
    4737             :  * \brief Compute the concave hull of a set of polygons, respecting
    4738             :  * the polygons as constraints.
    4739             :  *
    4740             :  * A concave hull is a (possibly) non-convex polygon containing all the input
    4741             :  * polygons.
    4742             :  * The computed hull "fills the gap" between the polygons,
    4743             :  * and does not intersect their interior.
    4744             :  * A set of polygons has a sequence of hulls of increasing concaveness,
    4745             :  * determined by a numeric target parameter.
    4746             :  *
    4747             :  * The concave hull is constructed by removing the longest outer edges
    4748             :  * of the Delaunay Triangulation of the space between the polygons,
    4749             :  * until the target criterion parameter is reached.
    4750             :  * The "Maximum Edge Length" parameter limits the length of the longest edge
    4751             :  * between polygons to be no larger than this value.
    4752             :  * This can be expressed as a ratio between the lengths of the longest and
    4753             :  * shortest edges.
    4754             :  *
    4755             :  * See https://lin-ear-th-inking.blogspot.com/2022/05/concave-hulls-of-polygons.html
    4756             :  * and https://lin-ear-th-inking.blogspot.com/2022/05/algorithm-for-concave-hull-of-polygons.html
    4757             :  * for more details.
    4758             :  *
    4759             :  * The input geometry must be a valid Polygon or MultiPolygon (i.e. they must
    4760             :  * be non-overlapping).
    4761             :  *
    4762             :  * A new geometry object is created and returned containing the concave
    4763             :  * hull of the geometry on which the method is invoked.
    4764             :  *
    4765             :  * This function is the same as the C++ method OGRGeometry::ConcaveHullOfPolygons().
    4766             :  *
    4767             :  * This function is built on the GEOS >= 3.11 library
    4768             :  * If OGR is built without the GEOS >= 3.11 library, this function will always
    4769             :  * fail, issuing a CPLE_NotSupported error.
    4770             :  *
    4771             :  * @param hTarget The Geometry to calculate the concave hull of.
    4772             :  * @param dfLengthRatio Specifies the Maximum Edge Length as a fraction of the
    4773             :  *                      difference between the longest and shortest edge lengths
    4774             :  *                      between the polygons.
    4775             :  *                      This normalizes the Maximum Edge Length to be scale-free.
    4776             :  *                      A value of 1 produces the convex hull; a value of 0 produces
    4777             :  *                      the original polygons.
    4778             :  * @param bIsTight Whether the hull must follow the outer boundaries of the input
    4779             :  *                 polygons.
    4780             :  * @param bAllowHoles Whether the concave hull is allowed to contain holes
    4781             :  *
    4782             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4783             :  * or NULL if an error occurs.
    4784             :  *
    4785             :  * @since GDAL 3.13
    4786             :  * @see OGR_G_ConcaveHull()
    4787             :  */
    4788             : 
    4789           7 : OGRGeometryH OGR_G_ConcaveHullOfPolygons(OGRGeometryH hTarget,
    4790             :                                          double dfLengthRatio, bool bIsTight,
    4791             :                                          bool bAllowHoles)
    4792             : 
    4793             : {
    4794           7 :     VALIDATE_POINTER1(hTarget, "OGR_G_ConcaveHullOfPolygons", nullptr);
    4795             : 
    4796           7 :     return OGRGeometry::ToHandle(
    4797             :         OGRGeometry::FromHandle(hTarget)->ConcaveHullOfPolygons(
    4798           7 :             dfLengthRatio, bIsTight, bAllowHoles));
    4799             : }
    4800             : 
    4801             : /************************************************************************/
    4802             : /*                              Boundary()                              */
    4803             : /************************************************************************/
    4804             : 
    4805             : /**
    4806             :  * \brief Compute boundary.
    4807             :  *
    4808             :  * A new geometry object is created and returned containing the boundary
    4809             :  * of the geometry on which the method is invoked.
    4810             :  *
    4811             :  * This method is the same as the C function OGR_G_Boundary().
    4812             :  *
    4813             :  * This method is built on the GEOS library, check it for the definition
    4814             :  * of the geometry operation.
    4815             :  * If OGR is built without the GEOS library, this method will always fail,
    4816             :  * issuing a CPLE_NotSupported error.
    4817             :  *
    4818             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4819             :  *
    4820             :  */
    4821             : 
    4822           6 : OGRGeometry *OGRGeometry::Boundary() const
    4823             : 
    4824             : {
    4825             : #ifndef HAVE_GEOS
    4826             : 
    4827             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4828             :     return nullptr;
    4829             : 
    4830             : #else
    4831             : 
    4832           6 :     OGRGeometry *poOGRProduct = nullptr;
    4833             : 
    4834           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4835           6 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4836           6 :     if (hGeosGeom != nullptr)
    4837             :     {
    4838           6 :         GEOSGeom hGeosProduct = GEOSBoundary_r(hGEOSCtxt, hGeosGeom);
    4839           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4840             : 
    4841             :         poOGRProduct =
    4842           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4843             :     }
    4844           6 :     freeGEOSContext(hGEOSCtxt);
    4845             : 
    4846           6 :     return poOGRProduct;
    4847             : 
    4848             : #endif  // HAVE_GEOS
    4849             : }
    4850             : 
    4851             : //! @cond Doxygen_Suppress
    4852             : /**
    4853             :  * \brief Compute boundary (deprecated)
    4854             :  *
    4855             :  * @deprecated
    4856             :  *
    4857             :  * @see Boundary()
    4858             :  */
    4859           0 : OGRGeometry *OGRGeometry::getBoundary() const
    4860             : 
    4861             : {
    4862           0 :     return Boundary();
    4863             : }
    4864             : 
    4865             : //! @endcond
    4866             : 
    4867             : /************************************************************************/
    4868             : /*                           OGR_G_Boundary()                           */
    4869             : /************************************************************************/
    4870             : /**
    4871             :  * \brief Compute boundary.
    4872             :  *
    4873             :  * A new geometry object is created and returned containing the boundary
    4874             :  * of the geometry on which the method is invoked.
    4875             :  *
    4876             :  * This function is the same as the C++ method OGR_G_Boundary().
    4877             :  *
    4878             :  * This function is built on the GEOS library, check it for the definition
    4879             :  * of the geometry operation.
    4880             :  * If OGR is built without the GEOS library, this function will always fail,
    4881             :  * issuing a CPLE_NotSupported error.
    4882             :  *
    4883             :  * @param hTarget The Geometry to calculate the boundary of.
    4884             :  *
    4885             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    4886             :  * or NULL if an error occurs.
    4887             :  *
    4888             :  */
    4889           6 : OGRGeometryH OGR_G_Boundary(OGRGeometryH hTarget)
    4890             : 
    4891             : {
    4892           6 :     VALIDATE_POINTER1(hTarget, "OGR_G_Boundary", nullptr);
    4893             : 
    4894           6 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4895             : }
    4896             : 
    4897             : /**
    4898             :  * \brief Compute boundary (deprecated)
    4899             :  *
    4900             :  * @deprecated
    4901             :  *
    4902             :  * @see OGR_G_Boundary()
    4903             :  */
    4904           0 : OGRGeometryH OGR_G_GetBoundary(OGRGeometryH hTarget)
    4905             : 
    4906             : {
    4907           0 :     VALIDATE_POINTER1(hTarget, "OGR_G_GetBoundary", nullptr);
    4908             : 
    4909           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hTarget)->Boundary());
    4910             : }
    4911             : 
    4912             : /************************************************************************/
    4913             : /*                               Buffer()                               */
    4914             : /************************************************************************/
    4915             : 
    4916             : /**
    4917             :  * \brief Compute buffer of geometry.
    4918             :  *
    4919             :  * Builds a new geometry containing the buffer region around the geometry
    4920             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4921             :  * the buffer distance of the original geometry.
    4922             :  *
    4923             :  * Some buffer sections are properly described as curves, but are converted to
    4924             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4925             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4926             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4927             :  * large numbers of vertices in the resulting buffer geometry while small
    4928             :  * numbers reduce the accuracy of the result.
    4929             :  *
    4930             :  * This method is the same as the C function OGR_G_Buffer().
    4931             :  *
    4932             :  * This method is built on the GEOS library, check it for the definition
    4933             :  * of the geometry operation.
    4934             :  * If OGR is built without the GEOS library, this method will always fail,
    4935             :  * issuing a CPLE_NotSupported error.
    4936             :  *
    4937             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    4938             :  *               the same unit as the coordinates of the geometry.
    4939             :  *
    4940             :  * @param nQuadSegs the number of segments used to approximate a 90
    4941             :  * degree (quadrant) of curvature.
    4942             :  *
    4943             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    4944             :  */
    4945             : 
    4946          42 : OGRGeometry *OGRGeometry::Buffer(double dfDist, int nQuadSegs) const
    4947             : 
    4948             : {
    4949             :     (void)dfDist;
    4950             :     (void)nQuadSegs;
    4951             : #ifndef HAVE_GEOS
    4952             : 
    4953             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    4954             :     return nullptr;
    4955             : 
    4956             : #else
    4957             : 
    4958          42 :     OGRGeometry *poOGRProduct = nullptr;
    4959             : 
    4960          42 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    4961          42 :     GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    4962          42 :     if (hGeosGeom != nullptr)
    4963             :     {
    4964             :         GEOSGeom hGeosProduct =
    4965          42 :             GEOSBuffer_r(hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs);
    4966          42 :         GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    4967             : 
    4968             :         poOGRProduct =
    4969          42 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    4970             :     }
    4971          42 :     freeGEOSContext(hGEOSCtxt);
    4972             : 
    4973          42 :     return poOGRProduct;
    4974             : 
    4975             : #endif  // HAVE_GEOS
    4976             : }
    4977             : 
    4978             : /************************************************************************/
    4979             : /*                            OGR_G_Buffer()                            */
    4980             : /************************************************************************/
    4981             : 
    4982             : /**
    4983             :  * \brief Compute buffer of geometry.
    4984             :  *
    4985             :  * Builds a new geometry containing the buffer region around the geometry
    4986             :  * on which it is invoked.  The buffer is a polygon containing the region within
    4987             :  * the buffer distance of the original geometry.
    4988             :  *
    4989             :  * Some buffer sections are properly described as curves, but are converted to
    4990             :  * approximate polygons.  The nQuadSegs parameter can be used to control how
    4991             :  * many segments should be used to define a 90 degree curve - a quadrant of a
    4992             :  * circle.  A value of 30 is a reasonable default.  Large values result in
    4993             :  * large numbers of vertices in the resulting buffer geometry while small
    4994             :  * numbers reduce the accuracy of the result.
    4995             :  *
    4996             :  * This function is the same as the C++ method OGRGeometry::Buffer().
    4997             :  *
    4998             :  * This function is built on the GEOS library, check it for the definition
    4999             :  * of the geometry operation.
    5000             :  * If OGR is built without the GEOS library, this function will always fail,
    5001             :  * issuing a CPLE_NotSupported error.
    5002             :  *
    5003             :  * @param hTarget the geometry.
    5004             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5005             :  *               the same unit as the coordinates of the geometry.
    5006             :  *
    5007             :  * @param nQuadSegs the number of segments used to approximate a 90 degree
    5008             :  * (quadrant) of curvature.
    5009             :  *
    5010             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5011             :  * or NULL if an error occurs.
    5012             :  */
    5013             : 
    5014          42 : OGRGeometryH OGR_G_Buffer(OGRGeometryH hTarget, double dfDist, int nQuadSegs)
    5015             : 
    5016             : {
    5017          42 :     VALIDATE_POINTER1(hTarget, "OGR_G_Buffer", nullptr);
    5018             : 
    5019          42 :     return OGRGeometry::ToHandle(
    5020          42 :         OGRGeometry::FromHandle(hTarget)->Buffer(dfDist, nQuadSegs));
    5021             : }
    5022             : 
    5023             : /**
    5024             :  * \brief Compute buffer of geometry.
    5025             :  *
    5026             :  * Builds a new geometry containing the buffer region around the geometry
    5027             :  * on which it is invoked.  The buffer is a polygon containing the region within
    5028             :  * the buffer distance of the original geometry.
    5029             :  *
    5030             :  * This function is built on the GEOS library, check it for the definition
    5031             :  * of the geometry operation.
    5032             :  * If OGR is built without the GEOS library, this function will always fail,
    5033             :  * issuing a CPLE_NotSupported error.
    5034             :  *
    5035             :  * The following options are supported. See the GEOS library for more detailed
    5036             :  * descriptions.
    5037             :  *
    5038             :  * <ul>
    5039             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    5040             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    5041             :  * <li>MITRE_LIMIT=double</li>
    5042             :  * <li>QUADRANT_SEGMENTS=int</li>
    5043             :  * <li>SINGLE_SIDED=YES/NO</li>
    5044             :  * </ul>
    5045             :  *
    5046             :  * This function is the same as the C function OGR_G_BufferEx().
    5047             :  *
    5048             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5049             :  *               the same unit as the coordinates of the geometry.
    5050             :  * @param papszOptions NULL terminated list of options (may be NULL)
    5051             :  *
    5052             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5053             :  *
    5054             :  * @since GDAL 3.10
    5055             :  */
    5056             : 
    5057          35 : OGRGeometry *OGRGeometry::BufferEx(double dfDist,
    5058             :                                    CSLConstList papszOptions) const
    5059             : {
    5060             :     (void)dfDist;
    5061             :     (void)papszOptions;
    5062             : #ifndef HAVE_GEOS
    5063             : 
    5064             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5065             :     return nullptr;
    5066             : 
    5067             : #else
    5068          35 :     OGRGeometry *poOGRProduct = nullptr;
    5069          35 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5070             : 
    5071          35 :     auto hParams = GEOSBufferParams_create_r(hGEOSCtxt);
    5072          35 :     bool bParamsAreValid = true;
    5073             : 
    5074         166 :     for (const auto &[pszParam, pszValue] : cpl::IterateNameValue(papszOptions))
    5075             :     {
    5076         131 :         if (EQUAL(pszParam, "ENDCAP_STYLE"))
    5077             :         {
    5078             :             int nStyle;
    5079          25 :             if (EQUAL(pszValue, "ROUND"))
    5080             :             {
    5081          22 :                 nStyle = GEOSBUF_CAP_ROUND;
    5082             :             }
    5083           3 :             else if (EQUAL(pszValue, "FLAT"))
    5084             :             {
    5085           1 :                 nStyle = GEOSBUF_CAP_FLAT;
    5086             :             }
    5087           2 :             else if (EQUAL(pszValue, "SQUARE"))
    5088             :             {
    5089           1 :                 nStyle = GEOSBUF_CAP_SQUARE;
    5090             :             }
    5091             :             else
    5092             :             {
    5093           1 :                 bParamsAreValid = false;
    5094           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    5095             :                          "Invalid value for ENDCAP_STYLE: %s", pszValue);
    5096           2 :                 break;
    5097             :             }
    5098             : 
    5099          24 :             if (!GEOSBufferParams_setEndCapStyle_r(hGEOSCtxt, hParams, nStyle))
    5100             :             {
    5101           0 :                 bParamsAreValid = false;
    5102             :             }
    5103             :         }
    5104         106 :         else if (EQUAL(pszParam, "JOIN_STYLE"))
    5105             :         {
    5106             :             int nStyle;
    5107          25 :             if (EQUAL(pszValue, "ROUND"))
    5108             :             {
    5109          21 :                 nStyle = GEOSBUF_JOIN_ROUND;
    5110             :             }
    5111           4 :             else if (EQUAL(pszValue, "MITRE"))
    5112             :             {
    5113           3 :                 nStyle = GEOSBUF_JOIN_MITRE;
    5114             :             }
    5115           1 :             else if (EQUAL(pszValue, "BEVEL"))
    5116             :             {
    5117           0 :                 nStyle = GEOSBUF_JOIN_BEVEL;
    5118             :             }
    5119             :             else
    5120             :             {
    5121           1 :                 bParamsAreValid = false;
    5122           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    5123             :                          "Invalid value for JOIN_STYLE: %s", pszValue);
    5124           1 :                 break;
    5125             :             }
    5126             : 
    5127          24 :             if (!GEOSBufferParams_setJoinStyle_r(hGEOSCtxt, hParams, nStyle))
    5128             :             {
    5129           0 :                 bParamsAreValid = false;
    5130           0 :                 break;
    5131             :             }
    5132             :         }
    5133          81 :         else if (EQUAL(pszParam, "MITRE_LIMIT"))
    5134             :         {
    5135             :             try
    5136             :             {
    5137             :                 std::size_t end;
    5138          30 :                 double dfLimit = std::stod(pszValue, &end);
    5139             : 
    5140          24 :                 if (end != strlen(pszValue))
    5141             :                 {
    5142           0 :                     throw std::invalid_argument("");
    5143             :                 }
    5144             : 
    5145          24 :                 if (!GEOSBufferParams_setMitreLimit_r(hGEOSCtxt, hParams,
    5146             :                                                       dfLimit))
    5147             :                 {
    5148           0 :                     bParamsAreValid = false;
    5149           0 :                     break;
    5150             :                 }
    5151             :             }
    5152           4 :             catch (const std::invalid_argument &)
    5153             :             {
    5154           2 :                 bParamsAreValid = false;
    5155           2 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5156             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5157             :             }
    5158           0 :             catch (const std::out_of_range &)
    5159             :             {
    5160           0 :                 bParamsAreValid = false;
    5161           0 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5162             :                          "Invalid value for MITRE_LIMIT: %s", pszValue);
    5163             :             }
    5164             :         }
    5165          55 :         else if (EQUAL(pszParam, "QUADRANT_SEGMENTS"))
    5166             :         {
    5167             :             try
    5168             :             {
    5169             :                 std::size_t end;
    5170          38 :                 int nQuadSegs = std::stoi(pszValue, &end, 10);
    5171             : 
    5172          26 :                 if (end != strlen(pszValue))
    5173             :                 {
    5174           0 :                     throw std::invalid_argument("");
    5175             :                 }
    5176             : 
    5177          26 :                 if (!GEOSBufferParams_setQuadrantSegments_r(hGEOSCtxt, hParams,
    5178             :                                                             nQuadSegs))
    5179             :                 {
    5180           0 :                     bParamsAreValid = false;
    5181           0 :                     break;
    5182             :                 }
    5183             :             }
    5184           6 :             catch (const std::invalid_argument &)
    5185             :             {
    5186           3 :                 bParamsAreValid = false;
    5187           3 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5188             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5189             :             }
    5190           2 :             catch (const std::out_of_range &)
    5191             :             {
    5192           1 :                 bParamsAreValid = false;
    5193           1 :                 CPLError(CE_Failure, CPLE_IllegalArg,
    5194             :                          "Invalid value for QUADRANT_SEGMENTS: %s", pszValue);
    5195             :             }
    5196             :         }
    5197          25 :         else if (EQUAL(pszParam, "SINGLE_SIDED"))
    5198             :         {
    5199          24 :             bool bSingleSided = CPLTestBool(pszValue);
    5200             : 
    5201          24 :             if (!GEOSBufferParams_setSingleSided_r(hGEOSCtxt, hParams,
    5202             :                                                    bSingleSided))
    5203             :             {
    5204           0 :                 bParamsAreValid = false;
    5205           0 :                 break;
    5206             :             }
    5207             :         }
    5208             :         else
    5209             :         {
    5210           1 :             bParamsAreValid = false;
    5211           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    5212             :                      "Unsupported buffer option: %s", pszValue);
    5213             :         }
    5214             :     }
    5215             : 
    5216          35 :     if (bParamsAreValid)
    5217             :     {
    5218          26 :         GEOSGeom hGeosGeom = exportToGEOS(hGEOSCtxt);
    5219          26 :         if (hGeosGeom != nullptr)
    5220             :         {
    5221             :             GEOSGeom hGeosProduct =
    5222          26 :                 GEOSBufferWithParams_r(hGEOSCtxt, hGeosGeom, hParams, dfDist);
    5223          26 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    5224             : 
    5225          26 :             if (hGeosProduct != nullptr)
    5226             :             {
    5227          26 :                 poOGRProduct = BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct,
    5228             :                                                      this, nullptr);
    5229             :             }
    5230             :         }
    5231             :     }
    5232             : 
    5233          35 :     GEOSBufferParams_destroy_r(hGEOSCtxt, hParams);
    5234          35 :     freeGEOSContext(hGEOSCtxt);
    5235          35 :     return poOGRProduct;
    5236             : #endif
    5237             : }
    5238             : 
    5239             : /**
    5240             :  * \brief Compute buffer of geometry.
    5241             :  *
    5242             :  * Builds a new geometry containing the buffer region around the geometry
    5243             :  * on which it is invoked.  The buffer is a polygon containing the region within
    5244             :  * the buffer distance of the original geometry.
    5245             :  *
    5246             :  * This function is built on the GEOS library, check it for the definition
    5247             :  * of the geometry operation.
    5248             :  * If OGR is built without the GEOS library, this function will always fail,
    5249             :  * issuing a CPLE_NotSupported error.
    5250             :  *
    5251             :  * The following options are supported. See the GEOS library for more detailed
    5252             :  * descriptions.
    5253             :  *
    5254             :  * <ul>
    5255             :  * <li>ENDCAP_STYLE=ROUND/FLAT/SQUARE</li>
    5256             :  * <li>JOIN_STYLE=ROUND/MITRE/BEVEL</li>
    5257             :  * <li>MITRE_LIMIT=double</li>
    5258             :  * <li>QUADRANT_SEGMENTS=int</li>
    5259             :  * <li>SINGLE_SIDED=YES/NO</li>
    5260             :  * </ul>
    5261             :  *
    5262             :  * This function is the same as the C++ method OGRGeometry::BufferEx().
    5263             :  *
    5264             :  * @param hTarget the geometry.
    5265             :  * @param dfDist the buffer distance to be applied. Should be expressed into
    5266             :  *               the same unit as the coordinates of the geometry.
    5267             :  * @param papszOptions NULL terminated list of options (may be NULL)
    5268             :  *
    5269             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5270             :  * or NULL if an error occurs.
    5271             :  *
    5272             :  * @since GDAL 3.10
    5273             :  */
    5274             : 
    5275          12 : OGRGeometryH OGR_G_BufferEx(OGRGeometryH hTarget, double dfDist,
    5276             :                             CSLConstList papszOptions)
    5277             : 
    5278             : {
    5279          12 :     VALIDATE_POINTER1(hTarget, "OGR_G_BufferEx", nullptr);
    5280             : 
    5281          12 :     return OGRGeometry::ToHandle(
    5282          12 :         OGRGeometry::FromHandle(hTarget)->BufferEx(dfDist, papszOptions));
    5283             : }
    5284             : 
    5285             : /************************************************************************/
    5286             : /*                            Intersection()                            */
    5287             : /************************************************************************/
    5288             : 
    5289             : /**
    5290             :  * \brief Compute intersection.
    5291             :  *
    5292             :  * Generates a new geometry which is the region of intersection of the
    5293             :  * two geometries operated on.  The Intersects() method can be used to test if
    5294             :  * two geometries intersect.
    5295             :  *
    5296             :  * Geometry validity is not checked. In case you are unsure of the validity
    5297             :  * of the input geometries, call IsValid() before, otherwise the result might
    5298             :  * be wrong.
    5299             :  *
    5300             :  * This method is the same as the C function OGR_G_Intersection().
    5301             :  *
    5302             :  * This method is built on the GEOS library, check it for the definition
    5303             :  * of the geometry operation.
    5304             :  * If OGR is built without the GEOS library, this method will always fail,
    5305             :  * issuing a CPLE_NotSupported error.
    5306             :  *
    5307             :  * @param poOtherGeom the other geometry intersected with "this" geometry.
    5308             :  *
    5309             :  * @return a new geometry to be freed by the caller, or NULL if there is no
    5310             :  * intersection or if an error occurs.
    5311             :  *
    5312             :  */
    5313             : 
    5314             : OGRGeometry *
    5315        3093 : OGRGeometry::Intersection(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5316             : 
    5317             : {
    5318        3093 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5319             :     {
    5320             : #ifndef HAVE_SFCGAL
    5321             : 
    5322           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5323           0 :         return nullptr;
    5324             : 
    5325             : #else
    5326             : 
    5327             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5328             :         if (poThis == nullptr)
    5329             :             return nullptr;
    5330             : 
    5331             :         sfcgal_geometry_t *poOther =
    5332             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5333             :         if (poOther == nullptr)
    5334             :         {
    5335             :             sfcgal_geometry_delete(poThis);
    5336             :             return nullptr;
    5337             :         }
    5338             : 
    5339             :         sfcgal_geometry_t *poRes =
    5340             :             sfcgal_geometry_intersection_3d(poThis, poOther);
    5341             :         OGRGeometry *h_prodGeom = SFCGALexportToOGR(poRes);
    5342             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5343             :             poOtherGeom->getSpatialReference() != nullptr &&
    5344             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5345             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5346             : 
    5347             :         sfcgal_geometry_delete(poThis);
    5348             :         sfcgal_geometry_delete(poOther);
    5349             :         sfcgal_geometry_delete(poRes);
    5350             : 
    5351             :         return h_prodGeom;
    5352             : 
    5353             : #endif
    5354             :     }
    5355             : 
    5356             :     else
    5357             :     {
    5358             : #ifndef HAVE_GEOS
    5359             : 
    5360             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5361             :         return nullptr;
    5362             : 
    5363             : #else
    5364        3093 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSIntersection_r);
    5365             : #endif /* HAVE_GEOS */
    5366             :     }
    5367             : }
    5368             : 
    5369             : /************************************************************************/
    5370             : /*                         OGR_G_Intersection()                         */
    5371             : /************************************************************************/
    5372             : 
    5373             : /**
    5374             :  * \brief Compute intersection.
    5375             :  *
    5376             :  * Generates a new geometry which is the region of intersection of the
    5377             :  * two geometries operated on.  The OGR_G_Intersects() function can be used to
    5378             :  * test if two geometries intersect.
    5379             :  *
    5380             :  * Geometry validity is not checked. In case you are unsure of the validity
    5381             :  * of the input geometries, call IsValid() before, otherwise the result might
    5382             :  * be wrong.
    5383             :  *
    5384             :  * This function is the same as the C++ method OGRGeometry::Intersection().
    5385             :  *
    5386             :  * This function is built on the GEOS library, check it for the definition
    5387             :  * of the geometry operation.
    5388             :  * If OGR is built without the GEOS library, this function will always fail,
    5389             :  * issuing a CPLE_NotSupported error.
    5390             :  *
    5391             :  * @param hThis the geometry.
    5392             :  * @param hOther the other geometry.
    5393             :  *
    5394             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5395             :  * or NULL if there is not intersection of if an error occurs.
    5396             :  */
    5397             : 
    5398          12 : OGRGeometryH OGR_G_Intersection(OGRGeometryH hThis, OGRGeometryH hOther)
    5399             : 
    5400             : {
    5401          12 :     VALIDATE_POINTER1(hThis, "OGR_G_Intersection", nullptr);
    5402             : 
    5403          24 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Intersection(
    5404          24 :         OGRGeometry::FromHandle(hOther)));
    5405             : }
    5406             : 
    5407             : /************************************************************************/
    5408             : /*                               Union()                                */
    5409             : /************************************************************************/
    5410             : 
    5411             : /**
    5412             :  * \brief Compute union.
    5413             :  *
    5414             :  * Generates a new geometry which is the region of union of the
    5415             :  * two geometries operated on.
    5416             :  *
    5417             :  * Geometry validity is not checked. In case you are unsure of the validity
    5418             :  * of the input geometries, call IsValid() before, otherwise the result might
    5419             :  * be wrong.
    5420             :  *
    5421             :  * This method is the same as the C function OGR_G_Union().
    5422             :  *
    5423             :  * This method is built on the GEOS library, check it for the definition
    5424             :  * of the geometry operation.
    5425             :  * If OGR is built without the GEOS library, this method will always fail,
    5426             :  * issuing a CPLE_NotSupported error.
    5427             :  *
    5428             :  * @param poOtherGeom the other geometry unioned with "this" geometry.
    5429             :  *
    5430             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5431             :  */
    5432             : 
    5433             : OGRGeometry *
    5434          68 : OGRGeometry::Union(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5435             : 
    5436             : {
    5437          68 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5438             :     {
    5439             : #ifndef HAVE_SFCGAL
    5440             : 
    5441           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5442           0 :         return nullptr;
    5443             : 
    5444             : #else
    5445             : 
    5446             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5447             :         if (poThis == nullptr)
    5448             :             return nullptr;
    5449             : 
    5450             :         sfcgal_geometry_t *poOther =
    5451             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5452             :         if (poOther == nullptr)
    5453             :         {
    5454             :             sfcgal_geometry_delete(poThis);
    5455             :             return nullptr;
    5456             :         }
    5457             : 
    5458             :         sfcgal_geometry_t *poRes = sfcgal_geometry_union_3d(poThis, poOther);
    5459             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5460             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5461             :             poOtherGeom->getSpatialReference() != nullptr &&
    5462             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5463             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5464             : 
    5465             :         sfcgal_geometry_delete(poThis);
    5466             :         sfcgal_geometry_delete(poOther);
    5467             :         sfcgal_geometry_delete(poRes);
    5468             : 
    5469             :         return h_prodGeom;
    5470             : 
    5471             : #endif
    5472             :     }
    5473             : 
    5474             :     else
    5475             :     {
    5476             : #ifndef HAVE_GEOS
    5477             : 
    5478             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5479             :         return nullptr;
    5480             : 
    5481             : #else
    5482          68 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSUnion_r);
    5483             : #endif /* HAVE_GEOS */
    5484             :     }
    5485             : }
    5486             : 
    5487             : /************************************************************************/
    5488             : /*                            OGR_G_Union()                             */
    5489             : /************************************************************************/
    5490             : 
    5491             : /**
    5492             :  * \brief Compute union.
    5493             :  *
    5494             :  * Generates a new geometry which is the region of union of the
    5495             :  * two geometries operated on.
    5496             :  *
    5497             :  * Geometry validity is not checked. In case you are unsure of the validity
    5498             :  * of the input geometries, call IsValid() before, otherwise the result might
    5499             :  * be wrong.
    5500             :  *
    5501             :  * This function is the same as the C++ method OGRGeometry::Union().
    5502             :  *
    5503             :  * This function is built on the GEOS library, check it for the definition
    5504             :  * of the geometry operation.
    5505             :  * If OGR is built without the GEOS library, this function will always fail,
    5506             :  * issuing a CPLE_NotSupported error.
    5507             :  *
    5508             :  * @param hThis the geometry.
    5509             :  * @param hOther the other geometry.
    5510             :  *
    5511             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5512             :  * or NULL if an error occurs.
    5513             :  */
    5514             : 
    5515          10 : OGRGeometryH OGR_G_Union(OGRGeometryH hThis, OGRGeometryH hOther)
    5516             : 
    5517             : {
    5518          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Union", nullptr);
    5519             : 
    5520          20 :     return OGRGeometry::ToHandle(
    5521          20 :         OGRGeometry::FromHandle(hThis)->Union(OGRGeometry::FromHandle(hOther)));
    5522             : }
    5523             : 
    5524             : /************************************************************************/
    5525             : /*                           UnionCascaded()                            */
    5526             : /************************************************************************/
    5527             : 
    5528             : /**
    5529             :  * \brief Compute union using cascading.
    5530             :  *
    5531             :  * Geometry validity is not checked. In case you are unsure of the validity
    5532             :  * of the input geometries, call IsValid() before, otherwise the result might
    5533             :  * be wrong.
    5534             :  *
    5535             :  * The input geometry must be a MultiPolygon.
    5536             :  *
    5537             :  * This method is the same as the C function OGR_G_UnionCascaded().
    5538             :  *
    5539             :  * This method is built on the GEOS library, check it for the definition
    5540             :  * of the geometry operation.
    5541             :  * If OGR is built without the GEOS library, this method will always fail,
    5542             :  * issuing a CPLE_NotSupported error.
    5543             :  *
    5544             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5545             :  *
    5546             :  *
    5547             :  * @deprecated Use UnaryUnion() instead
    5548             :  */
    5549             : 
    5550           2 : OGRGeometry *OGRGeometry::UnionCascaded() const
    5551             : 
    5552             : {
    5553             : #ifndef HAVE_GEOS
    5554             : 
    5555             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5556             :     return nullptr;
    5557             : #else
    5558             : 
    5559             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5560             :     if (wkbFlatten(getGeometryType()) == wkbMultiPolygon && IsEmpty())
    5561             :     {
    5562             :         // GEOS < 3.11 crashes on an empty multipolygon input
    5563             :         auto poRet = new OGRGeometryCollection();
    5564             :         poRet->assignSpatialReference(getSpatialReference());
    5565             :         return poRet;
    5566             :     }
    5567             : #endif
    5568           2 :     OGRGeometry *poOGRProduct = nullptr;
    5569             : 
    5570           2 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5571           2 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5572           2 :     if (hThisGeosGeom != nullptr)
    5573             :     {
    5574           2 :         GEOSGeom hGeosProduct = GEOSUnionCascaded_r(hGEOSCtxt, hThisGeosGeom);
    5575           2 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5576             : 
    5577             :         poOGRProduct =
    5578           2 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5579             :     }
    5580           2 :     freeGEOSContext(hGEOSCtxt);
    5581             : 
    5582           2 :     return poOGRProduct;
    5583             : 
    5584             : #endif  // HAVE_GEOS
    5585             : }
    5586             : 
    5587             : /************************************************************************/
    5588             : /*                        OGR_G_UnionCascaded()                         */
    5589             : /************************************************************************/
    5590             : 
    5591             : /**
    5592             :  * \brief Compute union using cascading.
    5593             :  *
    5594             :  * Geometry validity is not checked. In case you are unsure of the validity
    5595             :  * of the input geometries, call IsValid() before, otherwise the result might
    5596             :  * be wrong.
    5597             :  *
    5598             :  * The input geometry must be a MultiPolygon.
    5599             :  *
    5600             :  * This function is the same as the C++ method OGRGeometry::UnionCascaded().
    5601             :  *
    5602             :  * This function is built on the GEOS library, check it for the definition
    5603             :  * of the geometry operation.
    5604             :  * If OGR is built without the GEOS library, this function will always fail,
    5605             :  * issuing a CPLE_NotSupported error.
    5606             :  *
    5607             :  * @param hThis the geometry.
    5608             :  *
    5609             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5610             :  * or NULL if an error occurs.
    5611             :  *
    5612             :  * @deprecated Use OGR_G_UnaryUnion() instead
    5613             :  */
    5614             : 
    5615           2 : OGRGeometryH OGR_G_UnionCascaded(OGRGeometryH hThis)
    5616             : 
    5617             : {
    5618           2 :     VALIDATE_POINTER1(hThis, "OGR_G_UnionCascaded", nullptr);
    5619             : 
    5620           2 :     return OGRGeometry::ToHandle(
    5621           2 :         OGRGeometry::FromHandle(hThis)->UnionCascaded());
    5622             : }
    5623             : 
    5624             : /************************************************************************/
    5625             : /*                             UnaryUnion()                             */
    5626             : /************************************************************************/
    5627             : 
    5628             : /**
    5629             :  * \brief Returns the union of all components of a single geometry.
    5630             :  *
    5631             :  * Usually used to convert a collection into the smallest set of polygons that
    5632             :  * cover the same area.
    5633             :  *
    5634             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5635             :  *
    5636             :  * This method is the same as the C function OGR_G_UnaryUnion().
    5637             :  *
    5638             :  * This method is built on the GEOS library, check it for the definition
    5639             :  * of the geometry operation.
    5640             :  * If OGR is built without the GEOS library, this method will always fail,
    5641             :  * issuing a CPLE_NotSupported error.
    5642             :  *
    5643             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    5644             :  *
    5645             :  * @since GDAL 3.7
    5646             :  */
    5647             : 
    5648         636 : OGRGeometry *OGRGeometry::UnaryUnion() const
    5649             : 
    5650             : {
    5651             : #ifndef HAVE_GEOS
    5652             : 
    5653             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5654             :     return nullptr;
    5655             : #else
    5656             : 
    5657             : #if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
    5658             :     if (IsEmpty())
    5659             :     {
    5660             :         // GEOS < 3.11 crashes on an empty geometry
    5661             :         auto poRet = new OGRGeometryCollection();
    5662             :         poRet->assignSpatialReference(getSpatialReference());
    5663             :         return poRet;
    5664             :     }
    5665             : #endif
    5666         636 :     OGRGeometry *poOGRProduct = nullptr;
    5667             : 
    5668         636 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    5669         636 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    5670         636 :     if (hThisGeosGeom != nullptr)
    5671             :     {
    5672         636 :         GEOSGeom hGeosProduct = GEOSUnaryUnion_r(hGEOSCtxt, hThisGeosGeom);
    5673         636 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    5674             : 
    5675             :         poOGRProduct =
    5676         636 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    5677             :     }
    5678         636 :     freeGEOSContext(hGEOSCtxt);
    5679             : 
    5680         636 :     return poOGRProduct;
    5681             : 
    5682             : #endif  // HAVE_GEOS
    5683             : }
    5684             : 
    5685             : /************************************************************************/
    5686             : /*                          OGR_G_UnaryUnion()                          */
    5687             : /************************************************************************/
    5688             : 
    5689             : /**
    5690             :  * \brief Returns the union of all components of a single geometry.
    5691             :  *
    5692             :  * Usually used to convert a collection into the smallest set of polygons that
    5693             :  * cover the same area.
    5694             :  *
    5695             :  * See https://postgis.net/docs/ST_UnaryUnion.html for more details.
    5696             :  *
    5697             :  * Geometry validity is not checked. In case you are unsure of the validity
    5698             :  * of the input geometries, call IsValid() before, otherwise the result might
    5699             :  * be wrong.
    5700             :  *
    5701             :  * This function is the same as the C++ method OGRGeometry::UnaryUnion().
    5702             :  *
    5703             :  * This function is built on the GEOS library, check it for the definition
    5704             :  * of the geometry operation.
    5705             :  * If OGR is built without the GEOS library, this function will always fail,
    5706             :  * issuing a CPLE_NotSupported error.
    5707             :  *
    5708             :  * @param hThis the geometry.
    5709             :  *
    5710             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5711             :  * or NULL if an error occurs.
    5712             :  *
    5713             :  * @since GDAL 3.7
    5714             :  */
    5715             : 
    5716           3 : OGRGeometryH OGR_G_UnaryUnion(OGRGeometryH hThis)
    5717             : 
    5718             : {
    5719           3 :     VALIDATE_POINTER1(hThis, "OGR_G_UnaryUnion", nullptr);
    5720             : 
    5721           3 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->UnaryUnion());
    5722             : }
    5723             : 
    5724             : /************************************************************************/
    5725             : /*                             Difference()                             */
    5726             : /************************************************************************/
    5727             : 
    5728             : /**
    5729             :  * \brief Compute difference.
    5730             :  *
    5731             :  * Generates a new geometry which is the region of this geometry with the
    5732             :  * region of the second geometry removed.
    5733             :  *
    5734             :  * Geometry validity is not checked. In case you are unsure of the validity
    5735             :  * of the input geometries, call IsValid() before, otherwise the result might
    5736             :  * be wrong.
    5737             :  *
    5738             :  * This method is the same as the C function OGR_G_Difference().
    5739             :  *
    5740             :  * This method is built on the GEOS library, check it for the definition
    5741             :  * of the geometry operation.
    5742             :  * If OGR is built without the GEOS library, this method will always fail,
    5743             :  * issuing a CPLE_NotSupported error.
    5744             :  *
    5745             :  * @param poOtherGeom the other geometry removed from "this" geometry.
    5746             :  *
    5747             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5748             :  * is empty or if an error occurs.
    5749             :  */
    5750             : 
    5751             : OGRGeometry *
    5752         752 : OGRGeometry::Difference(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    5753             : 
    5754             : {
    5755         752 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5756             :     {
    5757             : #ifndef HAVE_SFCGAL
    5758             : 
    5759           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5760           0 :         return nullptr;
    5761             : 
    5762             : #else
    5763             : 
    5764             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    5765             :         if (poThis == nullptr)
    5766             :             return nullptr;
    5767             : 
    5768             :         sfcgal_geometry_t *poOther =
    5769             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    5770             :         if (poOther == nullptr)
    5771             :         {
    5772             :             sfcgal_geometry_delete(poThis);
    5773             :             return nullptr;
    5774             :         }
    5775             : 
    5776             :         sfcgal_geometry_t *poRes =
    5777             :             sfcgal_geometry_difference_3d(poThis, poOther);
    5778             :         OGRGeometry *h_prodGeom = OGRGeometry::SFCGALexportToOGR(poRes);
    5779             :         if (h_prodGeom != nullptr && getSpatialReference() != nullptr &&
    5780             :             poOtherGeom->getSpatialReference() != nullptr &&
    5781             :             poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()))
    5782             :             h_prodGeom->assignSpatialReference(getSpatialReference());
    5783             : 
    5784             :         sfcgal_geometry_delete(poThis);
    5785             :         sfcgal_geometry_delete(poOther);
    5786             :         sfcgal_geometry_delete(poRes);
    5787             : 
    5788             :         return h_prodGeom;
    5789             : 
    5790             : #endif
    5791             :     }
    5792             : 
    5793             :     else
    5794             :     {
    5795             : #ifndef HAVE_GEOS
    5796             : 
    5797             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5798             :         return nullptr;
    5799             : 
    5800             : #else
    5801         752 :         return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSDifference_r);
    5802             : #endif /* HAVE_GEOS */
    5803             :     }
    5804             : }
    5805             : 
    5806             : /************************************************************************/
    5807             : /*                          OGR_G_Difference()                          */
    5808             : /************************************************************************/
    5809             : 
    5810             : /**
    5811             :  * \brief Compute difference.
    5812             :  *
    5813             :  * Generates a new geometry which is the region of this geometry with the
    5814             :  * region of the other geometry removed.
    5815             :  *
    5816             :  * Geometry validity is not checked. In case you are unsure of the validity
    5817             :  * of the input geometries, call IsValid() before, otherwise the result might
    5818             :  * be wrong.
    5819             :  *
    5820             :  * This function is the same as the C++ method OGRGeometry::Difference().
    5821             :  *
    5822             :  * This function is built on the GEOS library, check it for the definition
    5823             :  * of the geometry operation.
    5824             :  * If OGR is built without the GEOS library, this function will always fail,
    5825             :  * issuing a CPLE_NotSupported error.
    5826             :  *
    5827             :  * @param hThis the geometry.
    5828             :  * @param hOther the other geometry.
    5829             :  *
    5830             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5831             :  * or NULL if the difference is empty or if an error occurs.
    5832             :  */
    5833             : 
    5834           6 : OGRGeometryH OGR_G_Difference(OGRGeometryH hThis, OGRGeometryH hOther)
    5835             : 
    5836             : {
    5837           6 :     VALIDATE_POINTER1(hThis, "OGR_G_Difference", nullptr);
    5838             : 
    5839          12 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->Difference(
    5840          12 :         OGRGeometry::FromHandle(hOther)));
    5841             : }
    5842             : 
    5843             : /************************************************************************/
    5844             : /*                           SymDifference()                            */
    5845             : /************************************************************************/
    5846             : 
    5847             : /**
    5848             :  * \brief Compute symmetric difference.
    5849             :  *
    5850             :  * Generates a new geometry which is the symmetric difference of this
    5851             :  * geometry and the second geometry passed into the method.
    5852             :  *
    5853             :  * Geometry validity is not checked. In case you are unsure of the validity
    5854             :  * of the input geometries, call IsValid() before, otherwise the result might
    5855             :  * be wrong.
    5856             :  *
    5857             :  * This method is the same as the C function OGR_G_SymDifference().
    5858             :  *
    5859             :  * This method is built on the GEOS library, check it for the definition
    5860             :  * of the geometry operation.
    5861             :  * If OGR is built without the GEOS library, this method will always fail,
    5862             :  * issuing a CPLE_NotSupported error.
    5863             :  *
    5864             :  * @param poOtherGeom the other geometry.
    5865             :  *
    5866             :  * @return a new geometry to be freed by the caller, or NULL if the difference
    5867             :  * is empty or if an error occurs.
    5868             :  *
    5869             :  */
    5870             : 
    5871           7 : OGRGeometry *OGRGeometry::SymDifference(const OGRGeometry *poOtherGeom) const
    5872             : 
    5873             : {
    5874             :     (void)poOtherGeom;
    5875           7 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    5876             :     {
    5877             : #ifndef HAVE_SFCGAL
    5878           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    5879           0 :         return nullptr;
    5880             : #else
    5881             :         OGRGeometry *poFirstDifference = Difference(poOtherGeom);
    5882             :         if (poFirstDifference == nullptr)
    5883             :             return nullptr;
    5884             : 
    5885             :         OGRGeometry *poOtherDifference = poOtherGeom->Difference(this);
    5886             :         if (poOtherDifference == nullptr)
    5887             :         {
    5888             :             delete poFirstDifference;
    5889             :             return nullptr;
    5890             :         }
    5891             : 
    5892             :         OGRGeometry *poSymDiff = poFirstDifference->Union(poOtherDifference);
    5893             :         delete poFirstDifference;
    5894             :         delete poOtherDifference;
    5895             :         return poSymDiff;
    5896             : #endif
    5897             :     }
    5898             : 
    5899             : #ifndef HAVE_GEOS
    5900             : 
    5901             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    5902             :     return nullptr;
    5903             : 
    5904             : #else
    5905           7 :     return BuildGeometryFromTwoGeoms(this, poOtherGeom, GEOSSymDifference_r);
    5906             : #endif  // HAVE_GEOS
    5907             : }
    5908             : 
    5909             : //! @cond Doxygen_Suppress
    5910             : /**
    5911             :  * \brief Compute symmetric difference (deprecated)
    5912             :  *
    5913             :  * @deprecated
    5914             :  *
    5915             :  * @see OGRGeometry::SymDifference()
    5916             :  */
    5917             : OGRGeometry *
    5918           0 : OGRGeometry::SymmetricDifference(const OGRGeometry *poOtherGeom) const
    5919             : 
    5920             : {
    5921           0 :     return SymDifference(poOtherGeom);
    5922             : }
    5923             : 
    5924             : //! @endcond
    5925             : 
    5926             : /************************************************************************/
    5927             : /*                        OGR_G_SymDifference()                         */
    5928             : /************************************************************************/
    5929             : 
    5930             : /**
    5931             :  * \brief Compute symmetric difference.
    5932             :  *
    5933             :  * Generates a new geometry which is the symmetric difference of this
    5934             :  * geometry and the other geometry.
    5935             :  *
    5936             :  * Geometry validity is not checked. In case you are unsure of the validity
    5937             :  * of the input geometries, call IsValid() before, otherwise the result might
    5938             :  * be wrong.
    5939             :  *
    5940             :  * This function is the same as the C++ method
    5941             :  * OGRGeometry::SymmetricDifference().
    5942             :  *
    5943             :  * This function is built on the GEOS library, check it for the definition
    5944             :  * of the geometry operation.
    5945             :  * If OGR is built without the GEOS library, this function will always fail,
    5946             :  * issuing a CPLE_NotSupported error.
    5947             :  *
    5948             :  * @param hThis the geometry.
    5949             :  * @param hOther the other geometry.
    5950             :  *
    5951             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    5952             :  * or NULL if the difference is empty or if an error occurs.
    5953             :  *
    5954             :  */
    5955             : 
    5956           7 : OGRGeometryH OGR_G_SymDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5957             : 
    5958             : {
    5959           7 :     VALIDATE_POINTER1(hThis, "OGR_G_SymDifference", nullptr);
    5960             : 
    5961          14 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5962          14 :         OGRGeometry::FromHandle(hOther)));
    5963             : }
    5964             : 
    5965             : /**
    5966             :  * \brief Compute symmetric difference (deprecated)
    5967             :  *
    5968             :  * @deprecated
    5969             :  *
    5970             :  * @see OGR_G_SymmetricDifference()
    5971             :  */
    5972           0 : OGRGeometryH OGR_G_SymmetricDifference(OGRGeometryH hThis, OGRGeometryH hOther)
    5973             : 
    5974             : {
    5975           0 :     VALIDATE_POINTER1(hThis, "OGR_G_SymmetricDifference", nullptr);
    5976             : 
    5977           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hThis)->SymDifference(
    5978           0 :         OGRGeometry::FromHandle(hOther)));
    5979             : }
    5980             : 
    5981             : /************************************************************************/
    5982             : /*                              Disjoint()                              */
    5983             : /************************************************************************/
    5984             : 
    5985             : /**
    5986             :  * \brief Test for disjointness.
    5987             :  *
    5988             :  * Tests if this geometry and the other passed into the method are disjoint.
    5989             :  *
    5990             :  * Geometry validity is not checked. In case you are unsure of the validity
    5991             :  * of the input geometries, call IsValid() before, otherwise the result might
    5992             :  * be wrong.
    5993             :  *
    5994             :  * This method is the same as the C function OGR_G_Disjoint().
    5995             :  *
    5996             :  * This method is built on the GEOS library, check it for the definition
    5997             :  * of the geometry operation.
    5998             :  * If OGR is built without the GEOS library, this method will always fail,
    5999             :  * issuing a CPLE_NotSupported error.
    6000             :  *
    6001             :  * @param poOtherGeom the geometry to compare to this geometry.
    6002             :  *
    6003             :  * @return TRUE if they are disjoint, otherwise FALSE.
    6004             :  */
    6005             : 
    6006           8 : bool OGRGeometry::Disjoint(const OGRGeometry *poOtherGeom) const
    6007             : 
    6008             : {
    6009             :     (void)poOtherGeom;
    6010             : #ifndef HAVE_GEOS
    6011             : 
    6012             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6013             :     return FALSE;
    6014             : 
    6015             : #else
    6016           8 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSDisjoint_r);
    6017             : #endif  // HAVE_GEOS
    6018             : }
    6019             : 
    6020             : /************************************************************************/
    6021             : /*                           OGR_G_Disjoint()                           */
    6022             : /************************************************************************/
    6023             : 
    6024             : /**
    6025             :  * \brief Test for disjointness.
    6026             :  *
    6027             :  * Tests if this geometry and the other geometry are disjoint.
    6028             :  *
    6029             :  * Geometry validity is not checked. In case you are unsure of the validity
    6030             :  * of the input geometries, call IsValid() before, otherwise the result might
    6031             :  * be wrong.
    6032             :  *
    6033             :  * This function is the same as the C++ method OGRGeometry::Disjoint().
    6034             :  *
    6035             :  * This function is built on the GEOS library, check it for the definition
    6036             :  * of the geometry operation.
    6037             :  * If OGR is built without the GEOS library, this function will always fail,
    6038             :  * issuing a CPLE_NotSupported error.
    6039             :  *
    6040             :  * @param hThis the geometry to compare.
    6041             :  * @param hOther the other geometry to compare.
    6042             :  *
    6043             :  * @return TRUE if they are disjoint, otherwise FALSE.
    6044             :  */
    6045           8 : int OGR_G_Disjoint(OGRGeometryH hThis, OGRGeometryH hOther)
    6046             : 
    6047             : {
    6048           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Disjoint", FALSE);
    6049             : 
    6050          16 :     return OGRGeometry::FromHandle(hThis)->Disjoint(
    6051          16 :         OGRGeometry::FromHandle(hOther));
    6052             : }
    6053             : 
    6054             : /************************************************************************/
    6055             : /*                              Touches()                               */
    6056             : /************************************************************************/
    6057             : 
    6058             : /**
    6059             :  * \brief Test for touching.
    6060             :  *
    6061             :  * Tests if this geometry and the other passed into the method are touching.
    6062             :  *
    6063             :  * Geometry validity is not checked. In case you are unsure of the validity
    6064             :  * of the input geometries, call IsValid() before, otherwise the result might
    6065             :  * be wrong.
    6066             :  *
    6067             :  * This method is the same as the C function OGR_G_Touches().
    6068             :  *
    6069             :  * This method is built on the GEOS library, check it for the definition
    6070             :  * of the geometry operation.
    6071             :  * If OGR is built without the GEOS library, this method will always fail,
    6072             :  * issuing a CPLE_NotSupported error.
    6073             :  *
    6074             :  * @param poOtherGeom the geometry to compare to this geometry.
    6075             :  *
    6076             :  * @return TRUE if they are touching, otherwise FALSE.
    6077             :  */
    6078             : 
    6079          11 : bool OGRGeometry::Touches(const OGRGeometry *poOtherGeom) const
    6080             : 
    6081             : {
    6082             :     (void)poOtherGeom;
    6083             : #ifndef HAVE_GEOS
    6084             : 
    6085             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6086             :     return FALSE;
    6087             : 
    6088             : #else
    6089          11 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSTouches_r);
    6090             : #endif  // HAVE_GEOS
    6091             : }
    6092             : 
    6093             : /************************************************************************/
    6094             : /*                           OGR_G_Touches()                            */
    6095             : /************************************************************************/
    6096             : /**
    6097             :  * \brief Test for touching.
    6098             :  *
    6099             :  * Tests if this geometry and the other geometry are touching.
    6100             :  *
    6101             :  * Geometry validity is not checked. In case you are unsure of the validity
    6102             :  * of the input geometries, call IsValid() before, otherwise the result might
    6103             :  * be wrong.
    6104             :  *
    6105             :  * This function is the same as the C++ method OGRGeometry::Touches().
    6106             :  *
    6107             :  * This function is built on the GEOS library, check it for the definition
    6108             :  * of the geometry operation.
    6109             :  * If OGR is built without the GEOS library, this function will always fail,
    6110             :  * issuing a CPLE_NotSupported error.
    6111             :  *
    6112             :  * @param hThis the geometry to compare.
    6113             :  * @param hOther the other geometry to compare.
    6114             :  *
    6115             :  * @return TRUE if they are touching, otherwise FALSE.
    6116             :  */
    6117             : 
    6118           8 : int OGR_G_Touches(OGRGeometryH hThis, OGRGeometryH hOther)
    6119             : 
    6120             : {
    6121           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Touches", FALSE);
    6122             : 
    6123          16 :     return OGRGeometry::FromHandle(hThis)->Touches(
    6124          16 :         OGRGeometry::FromHandle(hOther));
    6125             : }
    6126             : 
    6127             : /************************************************************************/
    6128             : /*                              Crosses()                               */
    6129             : /************************************************************************/
    6130             : 
    6131             : /**
    6132             :  * \brief Test for crossing.
    6133             :  *
    6134             :  * Tests if this geometry and the other passed into the method are crossing.
    6135             :  *
    6136             :  * Geometry validity is not checked. In case you are unsure of the validity
    6137             :  * of the input geometries, call IsValid() before, otherwise the result might
    6138             :  * be wrong.
    6139             :  *
    6140             :  * This method is the same as the C function OGR_G_Crosses().
    6141             :  *
    6142             :  * This method is built on the GEOS library, check it for the definition
    6143             :  * of the geometry operation.
    6144             :  * If OGR is built without the GEOS library, this method will always fail,
    6145             :  * issuing a CPLE_NotSupported error.
    6146             :  *
    6147             :  * @param poOtherGeom the geometry to compare to this geometry.
    6148             :  *
    6149             :  * @return TRUE if they are crossing, otherwise FALSE.
    6150             :  */
    6151             : 
    6152           8 : bool OGRGeometry::Crosses(UNUSED_PARAMETER const OGRGeometry *poOtherGeom) const
    6153             : 
    6154             : {
    6155           8 :     if (IsSFCGALCompatible() || poOtherGeom->IsSFCGALCompatible())
    6156             :     {
    6157             : #ifndef HAVE_SFCGAL
    6158             : 
    6159           0 :         CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    6160           0 :         return FALSE;
    6161             : 
    6162             : #else
    6163             : 
    6164             :         sfcgal_geometry_t *poThis = OGRGeometry::OGRexportToSFCGAL(this);
    6165             :         if (poThis == nullptr)
    6166             :             return FALSE;
    6167             : 
    6168             :         sfcgal_geometry_t *poOther =
    6169             :             OGRGeometry::OGRexportToSFCGAL(poOtherGeom);
    6170             :         if (poOther == nullptr)
    6171             :         {
    6172             :             sfcgal_geometry_delete(poThis);
    6173             :             return FALSE;
    6174             :         }
    6175             : 
    6176             :         int res = sfcgal_geometry_intersects_3d(poThis, poOther);
    6177             : 
    6178             :         sfcgal_geometry_delete(poThis);
    6179             :         sfcgal_geometry_delete(poOther);
    6180             : 
    6181             :         return (res == 1) ? TRUE : FALSE;
    6182             : 
    6183             : #endif
    6184             :     }
    6185             : 
    6186             :     else
    6187             :     {
    6188             : 
    6189             : #ifndef HAVE_GEOS
    6190             : 
    6191             :         CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6192             :         return FALSE;
    6193             : 
    6194             : #else
    6195           8 :         return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSCrosses_r);
    6196             : #endif /* HAVE_GEOS */
    6197             :     }
    6198             : }
    6199             : 
    6200             : /************************************************************************/
    6201             : /*                           OGR_G_Crosses()                            */
    6202             : /************************************************************************/
    6203             : /**
    6204             :  * \brief Test for crossing.
    6205             :  *
    6206             :  * Tests if this geometry and the other geometry are crossing.
    6207             :  *
    6208             :  * Geometry validity is not checked. In case you are unsure of the validity
    6209             :  * of the input geometries, call IsValid() before, otherwise the result might
    6210             :  * be wrong.
    6211             :  *
    6212             :  * This function is the same as the C++ method OGRGeometry::Crosses().
    6213             :  *
    6214             :  * This function is built on the GEOS library, check it for the definition
    6215             :  * of the geometry operation.
    6216             :  * If OGR is built without the GEOS library, this function will always fail,
    6217             :  * issuing a CPLE_NotSupported error.
    6218             :  *
    6219             :  * @param hThis the geometry to compare.
    6220             :  * @param hOther the other geometry to compare.
    6221             :  *
    6222             :  * @return TRUE if they are crossing, otherwise FALSE.
    6223             :  */
    6224             : 
    6225           8 : int OGR_G_Crosses(OGRGeometryH hThis, OGRGeometryH hOther)
    6226             : 
    6227             : {
    6228           8 :     VALIDATE_POINTER1(hThis, "OGR_G_Crosses", FALSE);
    6229             : 
    6230          16 :     return OGRGeometry::FromHandle(hThis)->Crosses(
    6231          16 :         OGRGeometry::FromHandle(hOther));
    6232             : }
    6233             : 
    6234             : /************************************************************************/
    6235             : /*                               Within()                               */
    6236             : /************************************************************************/
    6237             : 
    6238             : /**
    6239             :  * \brief Test for containment.
    6240             :  *
    6241             :  * Tests if actual geometry object is within the passed geometry.
    6242             :  *
    6243             :  * Geometry validity is not checked. In case you are unsure of the validity
    6244             :  * of the input geometries, call IsValid() before, otherwise the result might
    6245             :  * be wrong.
    6246             :  *
    6247             :  * This method is the same as the C function OGR_G_Within().
    6248             :  *
    6249             :  * This method is built on the GEOS library, check it for the definition
    6250             :  * of the geometry operation.
    6251             :  * If OGR is built without the GEOS library, this method will always fail,
    6252             :  * issuing a CPLE_NotSupported error.
    6253             :  *
    6254             :  * @param poOtherGeom the geometry to compare to this geometry.
    6255             :  *
    6256             :  * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE.
    6257             :  */
    6258             : 
    6259       22417 : bool OGRGeometry::Within(const OGRGeometry *poOtherGeom) const
    6260             : 
    6261             : {
    6262             :     (void)poOtherGeom;
    6263             : #ifndef HAVE_GEOS
    6264             : 
    6265             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6266             :     return FALSE;
    6267             : 
    6268             : #else
    6269       22417 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSWithin_r);
    6270             : #endif  // HAVE_GEOS
    6271             : }
    6272             : 
    6273             : /************************************************************************/
    6274             : /*                            OGR_G_Within()                            */
    6275             : /************************************************************************/
    6276             : 
    6277             : /**
    6278             :  * \brief Test for containment.
    6279             :  *
    6280             :  * Tests if this geometry is within the other geometry.
    6281             :  *
    6282             :  * Geometry validity is not checked. In case you are unsure of the validity
    6283             :  * of the input geometries, call IsValid() before, otherwise the result might
    6284             :  * be wrong.
    6285             :  *
    6286             :  * This function is the same as the C++ method OGRGeometry::Within().
    6287             :  *
    6288             :  * This function is built on the GEOS library, check it for the definition
    6289             :  * of the geometry operation.
    6290             :  * If OGR is built without the GEOS library, this function will always fail,
    6291             :  * issuing a CPLE_NotSupported error.
    6292             :  *
    6293             :  * @param hThis the geometry to compare.
    6294             :  * @param hOther the other geometry to compare.
    6295             :  *
    6296             :  * @return TRUE if hThis is within hOther, otherwise FALSE.
    6297             :  */
    6298        7374 : int OGR_G_Within(OGRGeometryH hThis, OGRGeometryH hOther)
    6299             : 
    6300             : {
    6301        7374 :     VALIDATE_POINTER1(hThis, "OGR_G_Within", FALSE);
    6302             : 
    6303       14748 :     return OGRGeometry::FromHandle(hThis)->Within(
    6304       14748 :         OGRGeometry::FromHandle(hOther));
    6305             : }
    6306             : 
    6307             : /************************************************************************/
    6308             : /*                              Contains()                              */
    6309             : /************************************************************************/
    6310             : 
    6311             : /**
    6312             :  * \brief Test for containment.
    6313             :  *
    6314             :  * Tests if actual geometry object contains the passed geometry.
    6315             :  *
    6316             :  * Geometry validity is not checked. In case you are unsure of the validity
    6317             :  * of the input geometries, call IsValid() before, otherwise the result might
    6318             :  * be wrong.
    6319             :  *
    6320             :  * This method is the same as the C function OGR_G_Contains().
    6321             :  *
    6322             :  * This method is built on the GEOS library, check it for the definition
    6323             :  * of the geometry operation.
    6324             :  * If OGR is built without the GEOS library, this method will always fail,
    6325             :  * issuing a CPLE_NotSupported error.
    6326             :  *
    6327             :  * @param poOtherGeom the geometry to compare to this geometry.
    6328             :  *
    6329             :  * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE.
    6330             :  */
    6331             : 
    6332         322 : bool OGRGeometry::Contains(const OGRGeometry *poOtherGeom) const
    6333             : 
    6334             : {
    6335             :     (void)poOtherGeom;
    6336             : #ifndef HAVE_GEOS
    6337             : 
    6338             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6339             :     return FALSE;
    6340             : 
    6341             : #else
    6342         322 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSContains_r);
    6343             : #endif  // HAVE_GEOS
    6344             : }
    6345             : 
    6346             : /************************************************************************/
    6347             : /*                           OGR_G_Contains()                           */
    6348             : /************************************************************************/
    6349             : 
    6350             : /**
    6351             :  * \brief Test for containment.
    6352             :  *
    6353             :  * Tests if this geometry contains the other geometry.
    6354             :  *
    6355             :  * Geometry validity is not checked. In case you are unsure of the validity
    6356             :  * of the input geometries, call IsValid() before, otherwise the result might
    6357             :  * be wrong.
    6358             :  *
    6359             :  * This function is the same as the C++ method OGRGeometry::Contains().
    6360             :  *
    6361             :  * This function is built on the GEOS library, check it for the definition
    6362             :  * of the geometry operation.
    6363             :  * If OGR is built without the GEOS library, this function will always fail,
    6364             :  * issuing a CPLE_NotSupported error.
    6365             :  *
    6366             :  * @param hThis the geometry to compare.
    6367             :  * @param hOther the other geometry to compare.
    6368             :  *
    6369             :  * @return TRUE if hThis contains hOther geometry, otherwise FALSE.
    6370             :  */
    6371          10 : int OGR_G_Contains(OGRGeometryH hThis, OGRGeometryH hOther)
    6372             : 
    6373             : {
    6374          10 :     VALIDATE_POINTER1(hThis, "OGR_G_Contains", FALSE);
    6375             : 
    6376          20 :     return OGRGeometry::FromHandle(hThis)->Contains(
    6377          20 :         OGRGeometry::FromHandle(hOther));
    6378             : }
    6379             : 
    6380             : /************************************************************************/
    6381             : /*                              Overlaps()                              */
    6382             : /************************************************************************/
    6383             : 
    6384             : /**
    6385             :  * \brief Test for overlap.
    6386             :  *
    6387             :  * Tests if this geometry and the other passed into the method overlap, that is
    6388             :  * their intersection has a non-zero area.
    6389             :  *
    6390             :  * Geometry validity is not checked. In case you are unsure of the validity
    6391             :  * of the input geometries, call IsValid() before, otherwise the result might
    6392             :  * be wrong.
    6393             :  *
    6394             :  * This method is the same as the C function OGR_G_Overlaps().
    6395             :  *
    6396             :  * This method is built on the GEOS library, check it for the definition
    6397             :  * of the geometry operation.
    6398             :  * If OGR is built without the GEOS library, this method will always fail,
    6399             :  * issuing a CPLE_NotSupported error.
    6400             :  *
    6401             :  * @param poOtherGeom the geometry to compare to this geometry.
    6402             :  *
    6403             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6404             :  */
    6405             : 
    6406           7 : bool OGRGeometry::Overlaps(const OGRGeometry *poOtherGeom) const
    6407             : 
    6408             : {
    6409             :     (void)poOtherGeom;
    6410             : #ifndef HAVE_GEOS
    6411             : 
    6412             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6413             :     return FALSE;
    6414             : 
    6415             : #else
    6416           7 :     return OGRGEOSBooleanPredicate(this, poOtherGeom, GEOSOverlaps_r);
    6417             : #endif  // HAVE_GEOS
    6418             : }
    6419             : 
    6420             : /************************************************************************/
    6421             : /*                           OGR_G_Overlaps()                           */
    6422             : /************************************************************************/
    6423             : /**
    6424             :  * \brief Test for overlap.
    6425             :  *
    6426             :  * Tests if this geometry and the other geometry overlap, that is their
    6427             :  * intersection has a non-zero area.
    6428             :  *
    6429             :  * Geometry validity is not checked. In case you are unsure of the validity
    6430             :  * of the input geometries, call IsValid() before, otherwise the result might
    6431             :  * be wrong.
    6432             :  *
    6433             :  * This function is the same as the C++ method OGRGeometry::Overlaps().
    6434             :  *
    6435             :  * This function is built on the GEOS library, check it for the definition
    6436             :  * of the geometry operation.
    6437             :  * If OGR is built without the GEOS library, this function will always fail,
    6438             :  * issuing a CPLE_NotSupported error.
    6439             :  *
    6440             :  * @param hThis the geometry to compare.
    6441             :  * @param hOther the other geometry to compare.
    6442             :  *
    6443             :  * @return TRUE if they are overlapping, otherwise FALSE.
    6444             :  */
    6445             : 
    6446           7 : int OGR_G_Overlaps(OGRGeometryH hThis, OGRGeometryH hOther)
    6447             : 
    6448             : {
    6449           7 :     VALIDATE_POINTER1(hThis, "OGR_G_Overlaps", FALSE);
    6450             : 
    6451          14 :     return OGRGeometry::FromHandle(hThis)->Overlaps(
    6452          14 :         OGRGeometry::FromHandle(hOther));
    6453             : }
    6454             : 
    6455             : /************************************************************************/
    6456             : /*                             closeRings()                             */
    6457             : /************************************************************************/
    6458             : 
    6459             : /**
    6460             :  * \brief Force rings to be closed.
    6461             :  *
    6462             :  * If this geometry, or any contained geometries has polygon rings that
    6463             :  * are not closed, they will be closed by adding the starting point at
    6464             :  * the end.
    6465             :  */
    6466             : 
    6467        1264 : void OGRGeometry::closeRings()
    6468             : {
    6469        1264 : }
    6470             : 
    6471             : /************************************************************************/
    6472             : /*                          OGR_G_CloseRings()                          */
    6473             : /************************************************************************/
    6474             : 
    6475             : /**
    6476             :  * \brief Force rings to be closed.
    6477             :  *
    6478             :  * If this geometry, or any contained geometries has polygon rings that
    6479             :  * are not closed, they will be closed by adding the starting point at
    6480             :  * the end.
    6481             :  *
    6482             :  * @param hGeom handle to the geometry.
    6483             :  */
    6484             : 
    6485           6 : void OGR_G_CloseRings(OGRGeometryH hGeom)
    6486             : 
    6487             : {
    6488           6 :     VALIDATE_POINTER0(hGeom, "OGR_G_CloseRings");
    6489             : 
    6490           6 :     OGRGeometry::FromHandle(hGeom)->closeRings();
    6491             : }
    6492             : 
    6493             : /************************************************************************/
    6494             : /*                              Centroid()                              */
    6495             : /************************************************************************/
    6496             : 
    6497             : /**
    6498             :  * \brief Compute the geometry centroid.
    6499             :  *
    6500             :  * The centroid location is applied to the passed in OGRPoint object.
    6501             :  * The centroid is not necessarily within the geometry.
    6502             :  *
    6503             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6504             :  * however the current implementation based on GEOS can operate on other
    6505             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6506             :  * multipolygons.
    6507             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6508             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6509             :  * (multipolygons).
    6510             :  *
    6511             :  * This function is the same as the C function OGR_G_Centroid().
    6512             :  *
    6513             :  * This function is built on the GEOS library, check it for the definition
    6514             :  * of the geometry operation.
    6515             :  * If OGR is built without the GEOS library, this function will always fail,
    6516             :  * issuing a CPLE_NotSupported error.
    6517             :  *
    6518             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6519             :  *
    6520             :  * to OGRPolygon)
    6521             :  */
    6522             : 
    6523           5 : OGRErr OGRGeometry::Centroid(OGRPoint *poPoint) const
    6524             : 
    6525             : {
    6526           5 :     if (poPoint == nullptr)
    6527           0 :         return OGRERR_FAILURE;
    6528             : 
    6529             : #ifndef HAVE_GEOS
    6530             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6531             :     return OGRERR_FAILURE;
    6532             : 
    6533             : #else
    6534             : 
    6535           5 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6536             :     GEOSGeom hThisGeosGeom =
    6537           5 :         exportToGEOS(hGEOSCtxt, /* bRemoveEmptyParts = */ true);
    6538             : 
    6539           5 :     if (hThisGeosGeom != nullptr)
    6540             :     {
    6541           5 :         GEOSGeom hOtherGeosGeom = GEOSGetCentroid_r(hGEOSCtxt, hThisGeosGeom);
    6542           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6543             : 
    6544           5 :         if (hOtherGeosGeom == nullptr)
    6545             :         {
    6546           0 :             freeGEOSContext(hGEOSCtxt);
    6547           0 :             return OGRERR_FAILURE;
    6548             :         }
    6549             : 
    6550             :         OGRGeometry *poCentroidGeom =
    6551           5 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6552             : 
    6553           5 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6554             : 
    6555           5 :         if (poCentroidGeom == nullptr)
    6556             :         {
    6557           0 :             freeGEOSContext(hGEOSCtxt);
    6558           0 :             return OGRERR_FAILURE;
    6559             :         }
    6560           5 :         if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6561             :         {
    6562           0 :             delete poCentroidGeom;
    6563           0 :             freeGEOSContext(hGEOSCtxt);
    6564           0 :             return OGRERR_FAILURE;
    6565             :         }
    6566             : 
    6567           5 :         if (getSpatialReference() != nullptr)
    6568           0 :             poCentroidGeom->assignSpatialReference(getSpatialReference());
    6569             : 
    6570           5 :         OGRPoint *poCentroid = poCentroidGeom->toPoint();
    6571             : 
    6572           5 :         if (!poCentroid->IsEmpty())
    6573             :         {
    6574           4 :             poPoint->setX(poCentroid->getX());
    6575           4 :             poPoint->setY(poCentroid->getY());
    6576             :         }
    6577             :         else
    6578             :         {
    6579           1 :             poPoint->empty();
    6580             :         }
    6581             : 
    6582           5 :         delete poCentroidGeom;
    6583             : 
    6584           5 :         freeGEOSContext(hGEOSCtxt);
    6585           5 :         return OGRERR_NONE;
    6586             :     }
    6587             :     else
    6588             :     {
    6589           0 :         freeGEOSContext(hGEOSCtxt);
    6590           0 :         return OGRERR_FAILURE;
    6591             :     }
    6592             : 
    6593             : #endif  // HAVE_GEOS
    6594             : }
    6595             : 
    6596             : /************************************************************************/
    6597             : /*                           OGR_G_Centroid()                           */
    6598             : /************************************************************************/
    6599             : 
    6600             : /**
    6601             :  * \brief Compute the geometry centroid.
    6602             :  *
    6603             :  * The centroid location is applied to the passed in OGRPoint object.
    6604             :  * The centroid is not necessarily within the geometry.
    6605             :  *
    6606             :  * This method relates to the SFCOM ISurface::get_Centroid() method
    6607             :  * however the current implementation based on GEOS can operate on other
    6608             :  * geometry types such as multipoint, linestring, geometrycollection such as
    6609             :  * multipolygons.
    6610             :  * OGC SF SQL 1.1 defines the operation for surfaces (polygons).
    6611             :  * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces
    6612             :  * (multipolygons).
    6613             :  *
    6614             :  * This function is the same as the C++ method OGRGeometry::Centroid().
    6615             :  *
    6616             :  * This function is built on the GEOS library, check it for the definition
    6617             :  * of the geometry operation.
    6618             :  * If OGR is built without the GEOS library, this function will always fail,
    6619             :  * issuing a CPLE_NotSupported error.
    6620             :  *
    6621             :  * @return OGRERR_NONE on success or OGRERR_FAILURE on error.
    6622             :  */
    6623             : 
    6624           5 : int OGR_G_Centroid(OGRGeometryH hGeom, OGRGeometryH hCentroidPoint)
    6625             : 
    6626             : {
    6627           5 :     VALIDATE_POINTER1(hGeom, "OGR_G_Centroid", OGRERR_FAILURE);
    6628             : 
    6629           5 :     OGRGeometry *poCentroidGeom = OGRGeometry::FromHandle(hCentroidPoint);
    6630           5 :     if (poCentroidGeom == nullptr)
    6631           0 :         return OGRERR_FAILURE;
    6632           5 :     if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint)
    6633             :     {
    6634           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6635             :                  "Passed wrong geometry type as centroid argument.");
    6636           0 :         return OGRERR_FAILURE;
    6637             :     }
    6638             : 
    6639           5 :     return OGRGeometry::FromHandle(hGeom)->Centroid(poCentroidGeom->toPoint());
    6640             : }
    6641             : 
    6642             : /************************************************************************/
    6643             : /*                        OGR_G_PointOnSurface()                        */
    6644             : /************************************************************************/
    6645             : 
    6646             : /**
    6647             :  * \brief Returns a point guaranteed to lie on the surface.
    6648             :  *
    6649             :  * This method relates to the SFCOM ISurface::get_PointOnSurface() method
    6650             :  * however the current implementation based on GEOS can operate on other
    6651             :  * geometry types than the types that are supported by SQL/MM-Part 3 :
    6652             :  * surfaces (polygons) and multisurfaces (multipolygons).
    6653             :  *
    6654             :  * This method is built on the GEOS library, check it for the definition
    6655             :  * of the geometry operation.
    6656             :  * If OGR is built without the GEOS library, this method will always fail,
    6657             :  * issuing a CPLE_NotSupported error.
    6658             :  *
    6659             :  * @param hGeom the geometry to operate on.
    6660             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6661             :  * or NULL if an error occurs.
    6662             :  *
    6663             :  */
    6664             : 
    6665           4 : OGRGeometryH OGR_G_PointOnSurface(OGRGeometryH hGeom)
    6666             : 
    6667             : {
    6668           4 :     VALIDATE_POINTER1(hGeom, "OGR_G_PointOnSurface", nullptr);
    6669             : 
    6670             : #ifndef HAVE_GEOS
    6671             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6672             :     return nullptr;
    6673             : #else
    6674             : 
    6675           4 :     OGRGeometry *poThis = OGRGeometry::FromHandle(hGeom);
    6676             : 
    6677           4 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    6678           4 :     GEOSGeom hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt);
    6679             : 
    6680           4 :     if (hThisGeosGeom != nullptr)
    6681             :     {
    6682             :         GEOSGeom hOtherGeosGeom =
    6683           4 :             GEOSPointOnSurface_r(hGEOSCtxt, hThisGeosGeom);
    6684           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6685             : 
    6686           4 :         if (hOtherGeosGeom == nullptr)
    6687             :         {
    6688           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6689           0 :             return nullptr;
    6690             :         }
    6691             : 
    6692             :         OGRGeometry *poInsidePointGeom =
    6693           4 :             OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom);
    6694             : 
    6695           4 :         GEOSGeom_destroy_r(hGEOSCtxt, hOtherGeosGeom);
    6696             : 
    6697           4 :         if (poInsidePointGeom == nullptr)
    6698             :         {
    6699           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6700           0 :             return nullptr;
    6701             :         }
    6702           4 :         if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint)
    6703             :         {
    6704           0 :             delete poInsidePointGeom;
    6705           0 :             OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6706           0 :             return nullptr;
    6707             :         }
    6708             : 
    6709           4 :         if (poThis->getSpatialReference() != nullptr)
    6710           0 :             poInsidePointGeom->assignSpatialReference(
    6711           0 :                 poThis->getSpatialReference());
    6712             : 
    6713           4 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6714           4 :         return OGRGeometry::ToHandle(poInsidePointGeom);
    6715             :     }
    6716             : 
    6717           0 :     OGRGeometry::freeGEOSContext(hGEOSCtxt);
    6718           0 :     return nullptr;
    6719             : #endif
    6720             : }
    6721             : 
    6722             : /************************************************************************/
    6723             : /*                       PointOnSurfaceInternal()                       */
    6724             : /************************************************************************/
    6725             : 
    6726             : //! @cond Doxygen_Suppress
    6727           0 : OGRErr OGRGeometry::PointOnSurfaceInternal(OGRPoint *poPoint) const
    6728             : {
    6729           0 :     if (poPoint == nullptr || poPoint->IsEmpty())
    6730           0 :         return OGRERR_FAILURE;
    6731             : 
    6732           0 :     OGRGeometryH hInsidePoint = OGR_G_PointOnSurface(
    6733             :         OGRGeometry::ToHandle(const_cast<OGRGeometry *>(this)));
    6734           0 :     if (hInsidePoint == nullptr)
    6735           0 :         return OGRERR_FAILURE;
    6736             : 
    6737           0 :     OGRPoint *poInsidePoint = OGRGeometry::FromHandle(hInsidePoint)->toPoint();
    6738           0 :     if (poInsidePoint->IsEmpty())
    6739             :     {
    6740           0 :         poPoint->empty();
    6741             :     }
    6742             :     else
    6743             :     {
    6744           0 :         poPoint->setX(poInsidePoint->getX());
    6745           0 :         poPoint->setY(poInsidePoint->getY());
    6746             :     }
    6747             : 
    6748           0 :     OGR_G_DestroyGeometry(hInsidePoint);
    6749             : 
    6750           0 :     return OGRERR_NONE;
    6751             : }
    6752             : 
    6753             : //! @endcond
    6754             : 
    6755             : /************************************************************************/
    6756             : /*                              Simplify()                              */
    6757             : /************************************************************************/
    6758             : 
    6759             : /**
    6760             :  * \brief Simplify the geometry.
    6761             :  *
    6762             :  * This function is the same as the C function OGR_G_Simplify().
    6763             :  *
    6764             :  * This function is built on the GEOS library, check it for the definition
    6765             :  * of the geometry operation.
    6766             :  * If OGR is built without the GEOS library, this function will always fail,
    6767             :  * issuing a CPLE_NotSupported error.
    6768             :  *
    6769             :  * @param dTolerance the distance tolerance for the simplification.
    6770             :  *
    6771             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6772             :  *
    6773             :  */
    6774             : 
    6775          55 : OGRGeometry *OGRGeometry::Simplify(double dTolerance) const
    6776             : 
    6777             : {
    6778             :     (void)dTolerance;
    6779             : #ifndef HAVE_GEOS
    6780             : 
    6781             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6782             :     return nullptr;
    6783             : 
    6784             : #else
    6785          55 :     OGRGeometry *poOGRProduct = nullptr;
    6786             : 
    6787          55 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6788          55 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6789          55 :     if (hThisGeosGeom != nullptr)
    6790             :     {
    6791             :         GEOSGeom hGeosProduct =
    6792          55 :             GEOSSimplify_r(hGEOSCtxt, hThisGeosGeom, dTolerance);
    6793          55 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6794             :         poOGRProduct =
    6795          55 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6796             :     }
    6797          55 :     freeGEOSContext(hGEOSCtxt);
    6798          55 :     return poOGRProduct;
    6799             : 
    6800             : #endif  // HAVE_GEOS
    6801             : }
    6802             : 
    6803             : /************************************************************************/
    6804             : /*                           OGR_G_Simplify()                           */
    6805             : /************************************************************************/
    6806             : 
    6807             : /**
    6808             :  * \brief Compute a simplified geometry.
    6809             :  *
    6810             :  * This function is the same as the C++ method OGRGeometry::Simplify().
    6811             :  *
    6812             :  * This function is built on the GEOS library, check it for the definition
    6813             :  * of the geometry operation.
    6814             :  * If OGR is built without the GEOS library, this function will always fail,
    6815             :  * issuing a CPLE_NotSupported error.
    6816             :  *
    6817             :  * @param hThis the geometry.
    6818             :  * @param dTolerance the distance tolerance for the simplification.
    6819             :  *
    6820             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6821             :  * or NULL if an error occurs.
    6822             :  *
    6823             :  */
    6824             : 
    6825           1 : OGRGeometryH OGR_G_Simplify(OGRGeometryH hThis, double dTolerance)
    6826             : 
    6827             : {
    6828           1 :     VALIDATE_POINTER1(hThis, "OGR_G_Simplify", nullptr);
    6829           1 :     return OGRGeometry::ToHandle(
    6830           1 :         OGRGeometry::FromHandle(hThis)->Simplify(dTolerance));
    6831             : }
    6832             : 
    6833             : /************************************************************************/
    6834             : /*                      SimplifyPreserveTopology()                      */
    6835             : /************************************************************************/
    6836             : 
    6837             : /**
    6838             :  * \brief Simplify the geometry while preserving topology.
    6839             :  *
    6840             :  * This function is the same as the C function OGR_G_SimplifyPreserveTopology().
    6841             :  *
    6842             :  * This function is built on the GEOS library, check it for the definition
    6843             :  * of the geometry operation.
    6844             :  * If OGR is built without the GEOS library, this function will always fail,
    6845             :  * issuing a CPLE_NotSupported error.
    6846             :  *
    6847             :  * @param dTolerance the distance tolerance for the simplification.
    6848             :  *
    6849             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    6850             :  *
    6851             :  */
    6852             : 
    6853          17 : OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const
    6854             : 
    6855             : {
    6856             :     (void)dTolerance;
    6857             : #ifndef HAVE_GEOS
    6858             : 
    6859             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    6860             :     return nullptr;
    6861             : 
    6862             : #else
    6863          17 :     OGRGeometry *poOGRProduct = nullptr;
    6864             : 
    6865          17 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    6866          17 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    6867          17 :     if (hThisGeosGeom != nullptr)
    6868             :     {
    6869          17 :         GEOSGeom hGeosProduct = GEOSTopologyPreserveSimplify_r(
    6870             :             hGEOSCtxt, hThisGeosGeom, dTolerance);
    6871          17 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    6872             :         poOGRProduct =
    6873          17 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    6874             :     }
    6875          17 :     freeGEOSContext(hGEOSCtxt);
    6876          17 :     return poOGRProduct;
    6877             : 
    6878             : #endif  // HAVE_GEOS
    6879             : }
    6880             : 
    6881             : /************************************************************************/
    6882             : /*                   OGR_G_SimplifyPreserveTopology()                   */
    6883             : /************************************************************************/
    6884             : 
    6885             : /**
    6886             :  * \brief Simplify the geometry while preserving topology.
    6887             :  *
    6888             :  * This function is the same as the C++ method
    6889             :  * OGRGeometry::SimplifyPreserveTopology().
    6890             :  *
    6891             :  * This function is built on the GEOS library, check it for the definition
    6892             :  * of the geometry operation.
    6893             :  * If OGR is built without the GEOS library, this function will always fail,
    6894             :  * issuing a CPLE_NotSupported error.
    6895             :  *
    6896             :  * @param hThis the geometry.
    6897             :  * @param dTolerance the distance tolerance for the simplification.
    6898             :  *
    6899             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    6900             :  * or NULL if an error occurs.
    6901             :  *
    6902             :  */
    6903             : 
    6904           1 : OGRGeometryH OGR_G_SimplifyPreserveTopology(OGRGeometryH hThis,
    6905             :                                             double dTolerance)
    6906             : 
    6907             : {
    6908           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SimplifyPreserveTopology", nullptr);
    6909           1 :     return OGRGeometry::ToHandle(
    6910           1 :         OGRGeometry::FromHandle(hThis)->SimplifyPreserveTopology(dTolerance));
    6911             : }
    6912             : 
    6913             : /************************************************************************/
    6914             : /*                          roundCoordinates()                          */
    6915             : /************************************************************************/
    6916             : 
    6917             : /** Round coordinates of the geometry to the specified precision.
    6918             :  *
    6919             :  * Note that this is not the same as OGRGeometry::SetPrecision(). The later
    6920             :  * will return valid geometries, whereas roundCoordinates() does not make
    6921             :  * such guarantee and may return geometries with invalidities, if they are
    6922             :  * not compatible with the specified precision. roundCoordinates() supports
    6923             :  * curve geometries, whereas SetPrecision() does not currently.
    6924             :  *
    6925             :  * One use case for roundCoordinates() is to undo the effect of
    6926             :  * quantizeCoordinates().
    6927             :  *
    6928             :  * @param sPrecision Contains the precision requirements.
    6929             :  * @since GDAL 3.9
    6930             :  */
    6931          39 : void OGRGeometry::roundCoordinates(const OGRGeomCoordinatePrecision &sPrecision)
    6932             : {
    6933             :     struct Rounder : public OGRDefaultGeometryVisitor
    6934             :     {
    6935             :         const OGRGeomCoordinatePrecision &m_precision;
    6936             :         const double m_invXYResolution;
    6937             :         const double m_invZResolution;
    6938             :         const double m_invMResolution;
    6939             : 
    6940          39 :         explicit Rounder(const OGRGeomCoordinatePrecision &sPrecisionIn)
    6941          39 :             : m_precision(sPrecisionIn),
    6942          39 :               m_invXYResolution(m_precision.dfXYResolution !=
    6943             :                                         OGRGeomCoordinatePrecision::UNKNOWN
    6944          39 :                                     ? 1.0 / m_precision.dfXYResolution
    6945             :                                     : 0.0),
    6946          39 :               m_invZResolution(m_precision.dfZResolution !=
    6947             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6948          39 :                                    ? 1.0 / m_precision.dfZResolution
    6949             :                                    : 0.0),
    6950          39 :               m_invMResolution(m_precision.dfMResolution !=
    6951             :                                        OGRGeomCoordinatePrecision::UNKNOWN
    6952          39 :                                    ? 1.0 / m_precision.dfMResolution
    6953         117 :                                    : 0.0)
    6954             :         {
    6955          39 :         }
    6956             : 
    6957             :         using OGRDefaultGeometryVisitor::visit;
    6958             : 
    6959         379 :         void visit(OGRPoint *poPoint) override
    6960             :         {
    6961         379 :             if (m_precision.dfXYResolution !=
    6962             :                 OGRGeomCoordinatePrecision::UNKNOWN)
    6963             :             {
    6964         379 :                 poPoint->setX(std::round(poPoint->getX() * m_invXYResolution) *
    6965         379 :                               m_precision.dfXYResolution);
    6966         379 :                 poPoint->setY(std::round(poPoint->getY() * m_invXYResolution) *
    6967         379 :                               m_precision.dfXYResolution);
    6968             :             }
    6969         758 :             if (m_precision.dfZResolution !=
    6970         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6971           4 :                 poPoint->Is3D())
    6972             :             {
    6973           4 :                 poPoint->setZ(std::round(poPoint->getZ() * m_invZResolution) *
    6974           4 :                               m_precision.dfZResolution);
    6975             :             }
    6976         758 :             if (m_precision.dfMResolution !=
    6977         383 :                     OGRGeomCoordinatePrecision::UNKNOWN &&
    6978           4 :                 poPoint->IsMeasured())
    6979             :             {
    6980           4 :                 poPoint->setM(std::round(poPoint->getM() * m_invMResolution) *
    6981           4 :                               m_precision.dfMResolution);
    6982             :             }
    6983         379 :         }
    6984             :     };
    6985             : 
    6986          78 :     Rounder rounder(sPrecision);
    6987          39 :     accept(&rounder);
    6988          39 : }
    6989             : 
    6990             : /************************************************************************/
    6991             : /*                            SetPrecision()                            */
    6992             : /************************************************************************/
    6993             : 
    6994             : /** Set the geometry's precision, rounding all its coordinates to the precision
    6995             :  * grid, and making sure the geometry is still valid.
    6996             :  *
    6997             :  * This is a stronger version of roundCoordinates().
    6998             :  *
    6999             :  * Note that at time of writing GEOS does no supported curve geometries. So
    7000             :  * currently if this function is called on such a geometry, OGR will first call
    7001             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    7002             :  * that it is unlikely to yield to the expected result.
    7003             :  *
    7004             :  * This function is the same as the C function OGR_G_SetPrecision().
    7005             :  *
    7006             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    7007             :  * GEOS library. Check it for the definition of the geometry operation.
    7008             :  * If OGR is built without the GEOS library, this function will always fail,
    7009             :  * issuing a CPLE_NotSupported error.
    7010             :  *
    7011             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    7012             :  *                 precision.
    7013             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    7014             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    7015             :  *
    7016             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7017             :  *
    7018             :  * @since GDAL 3.9
    7019             :  */
    7020             : 
    7021           6 : OGRGeometry *OGRGeometry::SetPrecision(double dfGridSize, int nFlags) const
    7022             : {
    7023             :     (void)dfGridSize;
    7024             :     (void)nFlags;
    7025             : #ifndef HAVE_GEOS
    7026             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7027             :     return nullptr;
    7028             : 
    7029             : #else
    7030           6 :     OGRGeometry *poOGRProduct = nullptr;
    7031             : 
    7032           6 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7033           6 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7034           6 :     if (hThisGeosGeom != nullptr)
    7035             :     {
    7036           6 :         GEOSGeom hGeosProduct = GEOSGeom_setPrecision_r(
    7037             :             hGEOSCtxt, hThisGeosGeom, dfGridSize, nFlags);
    7038           6 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7039             :         poOGRProduct =
    7040           6 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7041             :     }
    7042           6 :     freeGEOSContext(hGEOSCtxt);
    7043           6 :     return poOGRProduct;
    7044             : 
    7045             : #endif  // HAVE_GEOS
    7046             : }
    7047             : 
    7048             : /************************************************************************/
    7049             : /*                         OGR_G_SetPrecision()                         */
    7050             : /************************************************************************/
    7051             : 
    7052             : /** Set the geometry's precision, rounding all its coordinates to the precision
    7053             :  * grid, and making sure the geometry is still valid.
    7054             :  *
    7055             :  * This is a stronger version of roundCoordinates().
    7056             :  *
    7057             :  * Note that at time of writing GEOS does no supported curve geometries. So
    7058             :  * currently if this function is called on such a geometry, OGR will first call
    7059             :  * getLinearGeometry() on the input and getCurveGeometry() on the output, but
    7060             :  * that it is unlikely to yield to the expected result.
    7061             :  *
    7062             :  * This function is the same as the C++ method OGRGeometry::SetPrecision().
    7063             :  *
    7064             :  * This function is built on the GEOSGeom_setPrecision_r() function of the
    7065             :  * GEOS library. Check it for the definition of the geometry operation.
    7066             :  * If OGR is built without the GEOS library, this function will always fail,
    7067             :  * issuing a CPLE_NotSupported error.
    7068             :  *
    7069             :  * @param hThis the geometry.
    7070             :  * @param dfGridSize size of the precision grid, or 0 for FLOATING
    7071             :  *                 precision.
    7072             :  * @param nFlags The bitwise OR of zero, one or several of OGR_GEOS_PREC_NO_TOPO
    7073             :  *               and OGR_GEOS_PREC_KEEP_COLLAPSED
    7074             :  *
    7075             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7076             :  * or NULL if an error occurs.
    7077             :  *
    7078             :  * @since GDAL 3.9
    7079             :  */
    7080           1 : OGRGeometryH OGR_G_SetPrecision(OGRGeometryH hThis, double dfGridSize,
    7081             :                                 int nFlags)
    7082             : {
    7083           1 :     VALIDATE_POINTER1(hThis, "OGR_G_SetPrecision", nullptr);
    7084           1 :     return OGRGeometry::ToHandle(
    7085           1 :         OGRGeometry::FromHandle(hThis)->SetPrecision(dfGridSize, nFlags));
    7086             : }
    7087             : 
    7088             : /************************************************************************/
    7089             : /*                       DelaunayTriangulation()                        */
    7090             : /************************************************************************/
    7091             : 
    7092             : /**
    7093             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    7094             :  *
    7095             :  * This function is the same as the C function OGR_G_DelaunayTriangulation().
    7096             :  *
    7097             :  * This function is built on the GEOS library, v3.4 or above.
    7098             :  * If OGR is built without the GEOS library, this function will always fail,
    7099             :  * issuing a CPLE_NotSupported error.
    7100             :  *
    7101             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    7102             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    7103             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    7104             :  *
    7105             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7106             :  */
    7107             : 
    7108             : #ifndef HAVE_GEOS
    7109             : OGRGeometry *OGRGeometry::DelaunayTriangulation(double /*dfTolerance*/,
    7110             :                                                 int /*bOnlyEdges*/) const
    7111             : {
    7112             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7113             :     return nullptr;
    7114             : }
    7115             : #else
    7116           1 : OGRGeometry *OGRGeometry::DelaunayTriangulation(double dfTolerance,
    7117             :                                                 int bOnlyEdges) const
    7118             : {
    7119           1 :     OGRGeometry *poOGRProduct = nullptr;
    7120             : 
    7121           1 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7122           1 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7123           1 :     if (hThisGeosGeom != nullptr)
    7124             :     {
    7125           1 :         GEOSGeom hGeosProduct = GEOSDelaunayTriangulation_r(
    7126             :             hGEOSCtxt, hThisGeosGeom, dfTolerance, bOnlyEdges);
    7127           1 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7128             :         poOGRProduct =
    7129           1 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7130             :     }
    7131           1 :     freeGEOSContext(hGEOSCtxt);
    7132           1 :     return poOGRProduct;
    7133             : }
    7134             : #endif
    7135             : 
    7136             : /************************************************************************/
    7137             : /*                    OGR_G_DelaunayTriangulation()                     */
    7138             : /************************************************************************/
    7139             : 
    7140             : /**
    7141             :  * \brief Return a Delaunay triangulation of the vertices of the geometry.
    7142             :  *
    7143             :  * This function is the same as the C++ method
    7144             :  * OGRGeometry::DelaunayTriangulation().
    7145             :  *
    7146             :  * This function is built on the GEOS library, v3.4 or above.
    7147             :  * If OGR is built without the GEOS library, this function will always fail,
    7148             :  * issuing a CPLE_NotSupported error.
    7149             :  *
    7150             :  * @param hThis the geometry.
    7151             :  * @param dfTolerance optional snapping tolerance to use for improved robustness
    7152             :  * @param bOnlyEdges if TRUE, will return a MULTILINESTRING, otherwise it will
    7153             :  *                   return a GEOMETRYCOLLECTION containing triangular POLYGONs.
    7154             :  *
    7155             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7156             :  * or NULL if an error occurs.
    7157             :  */
    7158             : 
    7159           1 : OGRGeometryH OGR_G_DelaunayTriangulation(OGRGeometryH hThis, double dfTolerance,
    7160             :                                          int bOnlyEdges)
    7161             : 
    7162             : {
    7163           1 :     VALIDATE_POINTER1(hThis, "OGR_G_DelaunayTriangulation", nullptr);
    7164             : 
    7165           1 :     return OGRGeometry::ToHandle(
    7166             :         OGRGeometry::FromHandle(hThis)->DelaunayTriangulation(dfTolerance,
    7167           1 :                                                               bOnlyEdges));
    7168             : }
    7169             : 
    7170             : /************************************************************************/
    7171             : /*                  ConstrainedDelaunayTriangulation()                  */
    7172             : /************************************************************************/
    7173             : 
    7174             : /**
    7175             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7176             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7177             :  * geometry collection.
    7178             :  *
    7179             :  * This function is the same as the C function
    7180             :  * OGR_G_ConstrainedDelaunayTriangulation().
    7181             :  *
    7182             :  * This function is built on the GEOS library, v3.10 or above.
    7183             :  * If OGR is built without the GEOS library, this function will always fail,
    7184             :  * issuing a CPLE_NotSupported error.
    7185             :  *
    7186             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7187             :  *
    7188             :  * @since OGR 3.12
    7189             :  */
    7190             : 
    7191           3 : OGRGeometry *OGRGeometry::ConstrainedDelaunayTriangulation() const
    7192             : {
    7193             : #ifndef HAVE_GEOS
    7194             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7195             :     return nullptr;
    7196             : #elif !(GEOS_VERSION_MAJOR > 3 ||                                              \
    7197             :         (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 10))
    7198             :     CPLError(
    7199             :         CE_Failure, CPLE_NotSupported,
    7200             :         "GEOS 3.10 or later needed for ConstrainedDelaunayTriangulation().");
    7201             :     return nullptr;
    7202             : #else
    7203             : 
    7204           3 :     OGRGeometry *poOGRProduct = nullptr;
    7205             : 
    7206           3 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7207           3 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7208           3 :     if (hThisGeosGeom != nullptr)
    7209             :     {
    7210             :         GEOSGeom hGeosProduct =
    7211           3 :             GEOSConstrainedDelaunayTriangulation_r(hGEOSCtxt, hThisGeosGeom);
    7212           3 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7213             :         poOGRProduct =
    7214           3 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosProduct, this, nullptr);
    7215             :     }
    7216           3 :     freeGEOSContext(hGEOSCtxt);
    7217           3 :     return poOGRProduct;
    7218             : #endif
    7219             : }
    7220             : 
    7221             : /************************************************************************/
    7222             : /*               OGR_G_ConstrainedDelaunayTriangulation()               */
    7223             : /************************************************************************/
    7224             : 
    7225             : /**
    7226             :  * \brief Return a constrained Delaunay triangulation of the vertices of the
    7227             :  * given polygon(s). For non-polygonal inputs, silently returns an empty
    7228             :  * geometry collection.
    7229             :  *
    7230             :  * This function is the same as the C++ method
    7231             :  * OGRGeometry::ConstrainedDelaunayTriangulation().
    7232             :  *
    7233             :  * This function is built on the GEOS library, v3.10 or above.
    7234             :  * If OGR is built without the GEOS library, this function will always fail,
    7235             :  * issuing a CPLE_NotSupported error.
    7236             :  *
    7237             :  * @param hThis the geometry.
    7238             :  *
    7239             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7240             :  * or NULL if an error occurs.
    7241             :  *
    7242             :  * @since OGR 3.12
    7243             :  */
    7244             : 
    7245           3 : OGRGeometryH OGR_G_ConstrainedDelaunayTriangulation(OGRGeometryH hThis)
    7246             : {
    7247           3 :     VALIDATE_POINTER1(hThis, "OGR_G_ConstrainedDelaunayTriangulation", nullptr);
    7248             : 
    7249           3 :     return OGRGeometry::ToHandle(
    7250           3 :         OGRGeometry::FromHandle(hThis)->ConstrainedDelaunayTriangulation());
    7251             : }
    7252             : 
    7253             : /************************************************************************/
    7254             : /*                             Polygonize()                             */
    7255             : /************************************************************************/
    7256             : /* Contributor: Alessandro Furieri, a.furieri@lqt.it                    */
    7257             : /* Developed for Faunalia (http://www.faunalia.it) with funding from    */
    7258             : /* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED        */
    7259             : /*                   AMBIENTALE                                         */
    7260             : /************************************************************************/
    7261             : 
    7262             : /**
    7263             :  * \brief Polygonizes a set of sparse edges.
    7264             :  *
    7265             :  * A new geometry object is created and returned containing a collection
    7266             :  * of reassembled Polygons: NULL will be returned if the input collection
    7267             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7268             :  * into Polygons is impossible due to topological inconsistencies.
    7269             :  *
    7270             :  * This method is the same as the C function OGR_G_Polygonize().
    7271             :  *
    7272             :  * This method is built on the GEOS library, check it for the definition
    7273             :  * of the geometry operation.
    7274             :  * If OGR is built without the GEOS library, this method will always fail,
    7275             :  * issuing a CPLE_NotSupported error.
    7276             :  *
    7277             :  * @return a new geometry to be freed by the caller, or NULL if an error occurs.
    7278             :  *
    7279             :  */
    7280             : 
    7281         117 : OGRGeometry *OGRGeometry::Polygonize() const
    7282             : 
    7283             : {
    7284             : #ifndef HAVE_GEOS
    7285             : 
    7286             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7287             :     return nullptr;
    7288             : 
    7289             : #else
    7290             : 
    7291         117 :     const OGRGeometryCollection *poColl = nullptr;
    7292         233 :     if (wkbFlatten(getGeometryType()) == wkbGeometryCollection ||
    7293         116 :         wkbFlatten(getGeometryType()) == wkbMultiLineString)
    7294         116 :         poColl = toGeometryCollection();
    7295             :     else
    7296           1 :         return nullptr;
    7297             : 
    7298         116 :     const int nCount = poColl->getNumGeometries();
    7299             : 
    7300         116 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7301         116 :     bool bError = false;
    7302             : 
    7303         116 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7304             : 
    7305         116 :     GEOSGeom *pahGeosGeomList = new GEOSGeom[nCount];
    7306         747 :     for (int ig = 0; ig < nCount; ig++)
    7307             :     {
    7308         631 :         GEOSGeom hGeosGeom = nullptr;
    7309         631 :         const OGRGeometry *poChild = poColl->getGeometryRef(ig);
    7310        1262 :         if (poChild == nullptr ||
    7311         631 :             wkbFlatten(poChild->getGeometryType()) != wkbLineString)
    7312           1 :             bError = true;
    7313             :         else
    7314             :         {
    7315         630 :             hGeosGeom = poChild->exportToGEOS(hGEOSCtxt);
    7316         630 :             if (hGeosGeom == nullptr)
    7317           0 :                 bError = true;
    7318             :         }
    7319         631 :         pahGeosGeomList[ig] = hGeosGeom;
    7320             :     }
    7321             : 
    7322         116 :     if (!bError)
    7323             :     {
    7324             :         GEOSGeom hGeosPolygs =
    7325         115 :             GEOSPolygonize_r(hGEOSCtxt, pahGeosGeomList, nCount);
    7326             : 
    7327             :         poPolygsOGRGeom =
    7328         115 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7329             :     }
    7330             : 
    7331         747 :     for (int ig = 0; ig < nCount; ig++)
    7332             :     {
    7333         631 :         GEOSGeom hGeosGeom = pahGeosGeomList[ig];
    7334         631 :         if (hGeosGeom != nullptr)
    7335         630 :             GEOSGeom_destroy_r(hGEOSCtxt, hGeosGeom);
    7336             :     }
    7337         116 :     delete[] pahGeosGeomList;
    7338         116 :     freeGEOSContext(hGEOSCtxt);
    7339             : 
    7340         116 :     return poPolygsOGRGeom;
    7341             : 
    7342             : #endif  // HAVE_GEOS
    7343             : }
    7344             : 
    7345             : /************************************************************************/
    7346             : /*                          OGR_G_Polygonize()                          */
    7347             : /************************************************************************/
    7348             : /**
    7349             :  * \brief Polygonizes a set of sparse edges.
    7350             :  *
    7351             :  * A new geometry object is created and returned containing a collection
    7352             :  * of reassembled Polygons: NULL will be returned if the input collection
    7353             :  * doesn't corresponds to a MultiLinestring, or when reassembling Edges
    7354             :  * into Polygons is impossible due to topological inconsistencies.
    7355             :  *
    7356             :  * This function is the same as the C++ method OGRGeometry::Polygonize().
    7357             :  *
    7358             :  * This function is built on the GEOS library, check it for the definition
    7359             :  * of the geometry operation.
    7360             :  * If OGR is built without the GEOS library, this function will always fail,
    7361             :  * issuing a CPLE_NotSupported error.
    7362             :  *
    7363             :  * @param hTarget The Geometry to be polygonized.
    7364             :  *
    7365             :  * @return a new geometry to be freed by the caller with OGR_G_DestroyGeometry,
    7366             :  * or NULL if an error occurs.
    7367             :  *
    7368             :  */
    7369             : 
    7370           3 : OGRGeometryH OGR_G_Polygonize(OGRGeometryH hTarget)
    7371             : 
    7372             : {
    7373           3 :     VALIDATE_POINTER1(hTarget, "OGR_G_Polygonize", nullptr);
    7374             : 
    7375           3 :     return OGRGeometry::ToHandle(
    7376           3 :         OGRGeometry::FromHandle(hTarget)->Polygonize());
    7377             : }
    7378             : 
    7379             : /************************************************************************/
    7380             : /*                             BuildArea()                              */
    7381             : /************************************************************************/
    7382             : 
    7383             : /**
    7384             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7385             :  *
    7386             :  * This method is the same as the C function OGR_G_BuildArea().
    7387             :  *
    7388             :  * Polygonization is performed similarly to OGRGeometry::Polygonize().
    7389             :  * Additionally, holes are dropped and the result is unified producing
    7390             :  * a single Polygon or a MultiPolygon.
    7391             :  *
    7392             :  * A new geometry object is created and returned: NULL on failure,
    7393             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7394             :  * Polygon or MultiPolygon on success.
    7395             :  *
    7396             :  * This method is built on the GEOSBuildArea_r() function of the GEOS
    7397             :  * library, check it for the definition of the geometry operation.
    7398             :  * If OGR is built without the GEOS library, this method will always fail,
    7399             :  * issuing a CPLE_NotSupported error.
    7400             :  *
    7401             :  * @return a newly allocated geometry now owned by the caller,
    7402             :  *         or NULL on failure.
    7403             :  *
    7404             :  * @since OGR 3.11
    7405             :  */
    7406             : 
    7407          30 : OGRGeometry *OGRGeometry::BuildArea() const
    7408             : 
    7409             : {
    7410             : #ifndef HAVE_GEOS
    7411             : 
    7412             :     CPLError(CE_Failure, CPLE_NotSupported, "GEOS support not enabled.");
    7413             :     return nullptr;
    7414             : 
    7415             : #else
    7416             : 
    7417          30 :     OGRGeometry *poPolygsOGRGeom = nullptr;
    7418             : 
    7419          30 :     GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    7420          30 :     GEOSGeom hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    7421          30 :     if (hThisGeosGeom != nullptr)
    7422             :     {
    7423          30 :         GEOSGeom hGeosPolygs = GEOSBuildArea_r(hGEOSCtxt, hThisGeosGeom);
    7424             :         poPolygsOGRGeom =
    7425          30 :             BuildGeometryFromGEOS(hGEOSCtxt, hGeosPolygs, this, nullptr);
    7426          30 :         GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    7427             :     }
    7428          30 :     freeGEOSContext(hGEOSCtxt);
    7429             : 
    7430          30 :     return poPolygsOGRGeom;
    7431             : 
    7432             : #endif  // HAVE_GEOS
    7433             : }
    7434             : 
    7435             : /************************************************************************/
    7436             : /*                          OGR_G_BuildArea()                           */
    7437             : /************************************************************************/
    7438             : 
    7439             : /**
    7440             :  * \brief Polygonize a linework assuming inner polygons are holes.
    7441             :  *
    7442             :  * This function is the same as the C++ method OGRGeometry::BuildArea().
    7443             :  *
    7444             :  * Polygonization is performed similarly to OGR_G_Polygonize().
    7445             :  * Additionally, holes are dropped and the result is unified producing
    7446             :  * a single Polygon or a MultiPolygon.
    7447             :  *
    7448             :  * A new geometry object is created and returned: NULL on failure,
    7449             :  * empty GeometryCollection if the input geometry cannot be polygonized,
    7450             :  * Polygon or MultiPolygon on success.
    7451             :  *
    7452             :  * This function is built on the GEOSBuildArea_r() function of the GEOS
    7453             :  * library, check it for the definition of the geometry operation.
    7454             :  * If OGR is built without the GEOS library, this function will always fail,
    7455             :  * issuing a CPLE_NotSupported error.
    7456             :  *
    7457             :  * @param hGeom handle on the geometry to polygonize.
    7458             :  *
    7459             :  * @return a handle on newly allocated geometry now owned by the caller,
    7460             :  *         or NULL on failure.
    7461             :  *
    7462             :  * @since OGR 3.11
    7463             :  */
    7464             : 
    7465           0 : OGRGeometryH OGR_G_BuildArea(OGRGeometryH hGeom)
    7466             : 
    7467             : {
    7468           0 :     VALIDATE_POINTER1(hGeom, "OGR_G_BuildArea", nullptr);
    7469             : 
    7470           0 :     return OGRGeometry::ToHandle(OGRGeometry::FromHandle(hGeom)->BuildArea());
    7471             : }
    7472             : 
    7473             : /************************************************************************/
    7474             : /*                               swapXY()                               */
    7475             : /************************************************************************/
    7476             : 
    7477             : /**
    7478             :  * \brief Swap x and y coordinates.
    7479             :  *
    7480             :  */
    7481             : 
    7482           0 : void OGRGeometry::swapXY()
    7483             : 
    7484             : {
    7485           0 : }
    7486             : 
    7487             : /************************************************************************/
    7488             : /*                               swapXY()                               */
    7489             : /************************************************************************/
    7490             : 
    7491             : /**
    7492             :  * \brief Swap x and y coordinates.
    7493             :  *
    7494             :  * @param hGeom geometry.
    7495             :  */
    7496             : 
    7497          32 : void OGR_G_SwapXY(OGRGeometryH hGeom)
    7498             : {
    7499          32 :     VALIDATE_POINTER0(hGeom, "OGR_G_SwapXY");
    7500             : 
    7501          32 :     OGRGeometry::FromHandle(hGeom)->swapXY();
    7502             : }
    7503             : 
    7504             : /************************************************************************/
    7505             : /*                        Prepared geometry API                         */
    7506             : /************************************************************************/
    7507             : 
    7508             : #if defined(HAVE_GEOS)
    7509             : struct _OGRPreparedGeometry
    7510             : {
    7511             :     GEOSContextHandle_t hGEOSCtxt;
    7512             :     GEOSGeom hGEOSGeom;
    7513             :     const GEOSPreparedGeometry *poPreparedGEOSGeom;
    7514             : };
    7515             : #endif
    7516             : 
    7517             : /************************************************************************/
    7518             : /*                   OGRHasPreparedGeometrySupport()                    */
    7519             : /************************************************************************/
    7520             : 
    7521             : /** Returns if GEOS has prepared geometry support.
    7522             :  * @return TRUE or FALSE
    7523             :  */
    7524           1 : int OGRHasPreparedGeometrySupport()
    7525             : {
    7526             : #if defined(HAVE_GEOS)
    7527           1 :     return TRUE;
    7528             : #else
    7529             :     return FALSE;
    7530             : #endif
    7531             : }
    7532             : 
    7533             : /************************************************************************/
    7534             : /*                     OGRCreatePreparedGeometry()                      */
    7535             : /************************************************************************/
    7536             : 
    7537             : /** Creates a prepared geometry.
    7538             :  *
    7539             :  * To free with OGRDestroyPreparedGeometry()
    7540             :  *
    7541             :  * @param hGeom input geometry to prepare.
    7542             :  * @return handle to a prepared geometry.
    7543             :  * @since GDAL 3.3
    7544             :  */
    7545       53033 : OGRPreparedGeometryH OGRCreatePreparedGeometry(OGRGeometryH hGeom)
    7546             : {
    7547             :     (void)hGeom;
    7548             : #if defined(HAVE_GEOS)
    7549       53033 :     OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom);
    7550       53033 :     GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext();
    7551       53033 :     GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt);
    7552       53033 :     if (hGEOSGeom == nullptr)
    7553             :     {
    7554           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7555           0 :         return nullptr;
    7556             :     }
    7557             :     const GEOSPreparedGeometry *poPreparedGEOSGeom =
    7558       53033 :         GEOSPrepare_r(hGEOSCtxt, hGEOSGeom);
    7559       53033 :     if (poPreparedGEOSGeom == nullptr)
    7560             :     {
    7561           0 :         GEOSGeom_destroy_r(hGEOSCtxt, hGEOSGeom);
    7562           0 :         OGRGeometry::freeGEOSContext(hGEOSCtxt);
    7563           0 :         return nullptr;
    7564             :     }
    7565             : 
    7566       53033 :     OGRPreparedGeometry *poPreparedGeom = new OGRPreparedGeometry;
    7567       53033 :     poPreparedGeom->hGEOSCtxt = hGEOSCtxt;
    7568       53033 :     poPreparedGeom->hGEOSGeom = hGEOSGeom;
    7569       53033 :     poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom;
    7570             : 
    7571       53033 :     return poPreparedGeom;
    7572             : #else
    7573             :     return nullptr;
    7574             : #endif
    7575             : }
    7576             : 
    7577             : /************************************************************************/
    7578             : /*                     OGRDestroyPreparedGeometry()                     */
    7579             : /************************************************************************/
    7580             : 
    7581             : /** Destroys a prepared geometry.
    7582             :  * @param hPreparedGeom prepared geometry.
    7583             :  * @since GDAL 3.3
    7584             :  */
    7585       53079 : void OGRDestroyPreparedGeometry(OGRPreparedGeometryH hPreparedGeom)
    7586             : {
    7587             :     (void)hPreparedGeom;
    7588             : #if defined(HAVE_GEOS)
    7589       53079 :     if (hPreparedGeom != nullptr)
    7590             :     {
    7591       53033 :         GEOSPreparedGeom_destroy_r(hPreparedGeom->hGEOSCtxt,
    7592             :                                    hPreparedGeom->poPreparedGEOSGeom);
    7593       53033 :         GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hPreparedGeom->hGEOSGeom);
    7594       53033 :         OGRGeometry::freeGEOSContext(hPreparedGeom->hGEOSCtxt);
    7595       53033 :         delete hPreparedGeom;
    7596             :     }
    7597             : #endif
    7598       53079 : }
    7599             : 
    7600             : /************************************************************************/
    7601             : /*                   OGRPreparedGeometryIntersects()                    */
    7602             : /************************************************************************/
    7603             : 
    7604             : /** Returns whether a prepared geometry intersects with a geometry.
    7605             :  * @param hPreparedGeom prepared geometry.
    7606             :  * @param hOtherGeom other geometry.
    7607             :  * @return TRUE or FALSE.
    7608             :  * @since GDAL 3.3
    7609             :  */
    7610        5587 : int OGRPreparedGeometryIntersects(const OGRPreparedGeometryH hPreparedGeom,
    7611             :                                   const OGRGeometryH hOtherGeom)
    7612             : {
    7613             :     (void)hPreparedGeom;
    7614             :     (void)hOtherGeom;
    7615             : #if defined(HAVE_GEOS)
    7616        5587 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7617        5587 :     if (hPreparedGeom == nullptr ||
    7618             :         poOtherGeom == nullptr
    7619             :         // The check for IsEmpty() is for buggy GEOS versions.
    7620             :         // See https://github.com/libgeos/geos/pull/423
    7621       11174 :         || poOtherGeom->IsEmpty())
    7622             :     {
    7623           1 :         return FALSE;
    7624             :     }
    7625             : 
    7626             :     GEOSGeom hGEOSOtherGeom =
    7627        5586 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7628        5586 :     if (hGEOSOtherGeom == nullptr)
    7629           0 :         return FALSE;
    7630             : 
    7631             :     const bool bRet =
    7632        5586 :         GEOSPreparedIntersects_r(hPreparedGeom->hGEOSCtxt,
    7633             :                                  hPreparedGeom->poPreparedGEOSGeom,
    7634        5586 :                                  hGEOSOtherGeom) == 1;
    7635        5586 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7636             : 
    7637        5586 :     return bRet;
    7638             : #else
    7639             :     return FALSE;
    7640             : #endif
    7641             : }
    7642             : 
    7643             : /** Returns whether a prepared geometry contains a geometry.
    7644             :  * @param hPreparedGeom prepared geometry.
    7645             :  * @param hOtherGeom other geometry.
    7646             :  * @return TRUE or FALSE.
    7647             :  */
    7648      120516 : int OGRPreparedGeometryContains(const OGRPreparedGeometryH hPreparedGeom,
    7649             :                                 const OGRGeometryH hOtherGeom)
    7650             : {
    7651             :     (void)hPreparedGeom;
    7652             :     (void)hOtherGeom;
    7653             : #if defined(HAVE_GEOS)
    7654      120516 :     OGRGeometry *poOtherGeom = OGRGeometry::FromHandle(hOtherGeom);
    7655      120516 :     if (hPreparedGeom == nullptr ||
    7656             :         poOtherGeom == nullptr
    7657             :         // The check for IsEmpty() is for buggy GEOS versions.
    7658             :         // See https://github.com/libgeos/geos/pull/423
    7659      241032 :         || poOtherGeom->IsEmpty())
    7660             :     {
    7661           1 :         return FALSE;
    7662             :     }
    7663             : 
    7664             :     GEOSGeom hGEOSOtherGeom =
    7665      120515 :         poOtherGeom->exportToGEOS(hPreparedGeom->hGEOSCtxt);
    7666      120515 :     if (hGEOSOtherGeom == nullptr)
    7667           0 :         return FALSE;
    7668             : 
    7669      120515 :     const bool bRet = GEOSPreparedContains_r(hPreparedGeom->hGEOSCtxt,
    7670             :                                              hPreparedGeom->poPreparedGEOSGeom,
    7671      120515 :                                              hGEOSOtherGeom) == 1;
    7672      120515 :     GEOSGeom_destroy_r(hPreparedGeom->hGEOSCtxt, hGEOSOtherGeom);
    7673             : 
    7674      120515 :     return bRet;
    7675             : #else
    7676             :     return FALSE;
    7677             : #endif
    7678             : }
    7679             : 
    7680             : /************************************************************************/
    7681             : /*                        OGRGeometryFromEWKB()                         */
    7682             : /************************************************************************/
    7683             : 
    7684        1445 : OGRGeometry *OGRGeometryFromEWKB(GByte *pabyEWKB, int nLength, int *pnSRID,
    7685             :                                  int bIsPostGIS1_EWKB)
    7686             : 
    7687             : {
    7688        1445 :     OGRGeometry *poGeometry = nullptr;
    7689             : 
    7690        1445 :     size_t nWKBSize = 0;
    7691        1445 :     const GByte *pabyWKB = WKBFromEWKB(pabyEWKB, nLength, nWKBSize, pnSRID);
    7692        1445 :     if (pabyWKB == nullptr)
    7693           0 :         return nullptr;
    7694             : 
    7695             :     /* -------------------------------------------------------------------- */
    7696             :     /*      Try to ingest the geometry.                                     */
    7697             :     /* -------------------------------------------------------------------- */
    7698        1445 :     (void)OGRGeometryFactory::createFromWkb(
    7699             :         pabyWKB, nullptr, &poGeometry, nWKBSize,
    7700             :         (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc);
    7701             : 
    7702        1445 :     return poGeometry;
    7703             : }
    7704             : 
    7705             : /************************************************************************/
    7706             : /*                       OGRGeometryFromHexEWKB()                       */
    7707             : /************************************************************************/
    7708             : 
    7709        1443 : OGRGeometry *OGRGeometryFromHexEWKB(const char *pszBytea, int *pnSRID,
    7710             :                                     int bIsPostGIS1_EWKB)
    7711             : 
    7712             : {
    7713        1443 :     if (pszBytea == nullptr)
    7714           0 :         return nullptr;
    7715             : 
    7716        1443 :     int nWKBLength = 0;
    7717        1443 :     GByte *pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength);
    7718             : 
    7719             :     OGRGeometry *poGeometry =
    7720        1443 :         OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB);
    7721             : 
    7722        1443 :     CPLFree(pabyWKB);
    7723             : 
    7724        1443 :     return poGeometry;
    7725             : }
    7726             : 
    7727             : /************************************************************************/
    7728             : /*                        OGRGeometryToHexEWKB()                        */
    7729             : /************************************************************************/
    7730             : 
    7731        1071 : char *OGRGeometryToHexEWKB(const OGRGeometry *poGeometry, int nSRSId,
    7732             :                            int nPostGISMajor, int nPostGISMinor)
    7733             : {
    7734        1071 :     const size_t nWkbSize = poGeometry->WkbSize();
    7735        1071 :     GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
    7736        1071 :     if (pabyWKB == nullptr)
    7737           0 :         return CPLStrdup("");
    7738             : 
    7739         118 :     if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
    7740        1815 :         wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
    7741         626 :         poGeometry->IsEmpty())
    7742             :     {
    7743           2 :         if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
    7744             :             OGRERR_NONE)
    7745             :         {
    7746           0 :             CPLFree(pabyWKB);
    7747           0 :             return CPLStrdup("");
    7748             :         }
    7749             :     }
    7750        1069 :     else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
    7751             :                                      (nPostGISMajor < 2)
    7752             :                                          ? wkbVariantPostGIS1
    7753        1069 :                                          : wkbVariantOldOgc) != OGRERR_NONE)
    7754             :     {
    7755           0 :         CPLFree(pabyWKB);
    7756           0 :         return CPLStrdup("");
    7757             :     }
    7758             : 
    7759             :     // When converting to hex, each byte takes 2 hex characters.  In addition
    7760             :     // we add in 8 characters to represent the SRID integer in hex, and
    7761             :     // one for a null terminator.
    7762             :     // The limit of INT_MAX = 2 GB is a bit artificial, but at time of writing
    7763             :     // (2024), PostgreSQL by default cannot handle objects larger than 1 GB:
    7764             :     // https://github.com/postgres/postgres/blob/5d39becf8ba0080c98fee4b63575552f6800b012/src/include/utils/memutils.h#L40
    7765        1071 :     if (nWkbSize >
    7766        1071 :         static_cast<size_t>(std::numeric_limits<int>::max() - 8 - 1) / 2)
    7767             :     {
    7768           0 :         CPLFree(pabyWKB);
    7769           0 :         return CPLStrdup("");
    7770             :     }
    7771        1071 :     const size_t nTextSize = nWkbSize * 2 + 8 + 1;
    7772        1071 :     char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextSize));
    7773        1071 :     if (pszTextBuf == nullptr)
    7774             :     {
    7775           0 :         CPLFree(pabyWKB);
    7776           0 :         return CPLStrdup("");
    7777             :     }
    7778        1071 :     char *pszTextBufCurrent = pszTextBuf;
    7779             : 
    7780             :     // Convert the 1st byte, which is the endianness flag, to hex.
    7781        1071 :     char *pszHex = CPLBinaryToHex(1, pabyWKB);
    7782        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7783        1071 :     CPLFree(pszHex);
    7784        1071 :     pszTextBufCurrent += 2;
    7785             : 
    7786             :     // Next, get the geom type which is bytes 2 through 5.
    7787             :     GUInt32 geomType;
    7788        1071 :     memcpy(&geomType, pabyWKB + 1, 4);
    7789             : 
    7790             :     // Now add the SRID flag if an SRID is provided.
    7791        1071 :     if (nSRSId > 0)
    7792             :     {
    7793             :         // Change the flag to wkbNDR (little) endianness.
    7794         541 :         constexpr GUInt32 WKBSRIDFLAG = 0x20000000;
    7795         541 :         GUInt32 nGSrsFlag = CPL_LSBWORD32(WKBSRIDFLAG);
    7796             :         // Apply the flag.
    7797         541 :         geomType = geomType | nGSrsFlag;
    7798             :     }
    7799             : 
    7800             :     // Now write the geom type which is 4 bytes.
    7801        1071 :     pszHex = CPLBinaryToHex(4, reinterpret_cast<const GByte *>(&geomType));
    7802        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7803        1071 :     CPLFree(pszHex);
    7804        1071 :     pszTextBufCurrent += 8;
    7805             : 
    7806             :     // Now include SRID if provided.
    7807        1071 :     if (nSRSId > 0)
    7808             :     {
    7809             :         // Force the srsid to wkbNDR (little) endianness.
    7810         541 :         const GUInt32 nGSRSId = CPL_LSBWORD32(nSRSId);
    7811         541 :         pszHex = CPLBinaryToHex(sizeof(nGSRSId),
    7812             :                                 reinterpret_cast<const GByte *>(&nGSRSId));
    7813         541 :         strcpy(pszTextBufCurrent, pszHex);
    7814         541 :         CPLFree(pszHex);
    7815         541 :         pszTextBufCurrent += 8;
    7816             :     }
    7817             : 
    7818             :     // Copy the rest of the data over - subtract
    7819             :     // 5 since we already copied 5 bytes above.
    7820        1071 :     pszHex = CPLBinaryToHex(static_cast<int>(nWkbSize - 5), pabyWKB + 5);
    7821        1071 :     CPLFree(pabyWKB);
    7822        1071 :     if (!pszHex || pszHex[0] == 0)
    7823             :     {
    7824           0 :         CPLFree(pszTextBuf);
    7825           0 :         return pszHex;
    7826             :     }
    7827        1071 :     strcpy(pszTextBufCurrent, pszHex);
    7828        1071 :     CPLFree(pszHex);
    7829             : 
    7830        1071 :     return pszTextBuf;
    7831             : }
    7832             : 
    7833             : /************************************************************************/
    7834             : /*                       importPreambleFromWkb()                        */
    7835             : /************************************************************************/
    7836             : 
    7837             : //! @cond Doxygen_Suppress
    7838      160381 : OGRErr OGRGeometry::importPreambleFromWkb(const unsigned char *pabyData,
    7839             :                                           size_t nSize,
    7840             :                                           OGRwkbByteOrder &eByteOrder,
    7841             :                                           OGRwkbVariant eWkbVariant)
    7842             : {
    7843      160381 :     if (nSize < 9 && nSize != static_cast<size_t>(-1))
    7844           0 :         return OGRERR_NOT_ENOUGH_DATA;
    7845             : 
    7846             :     /* -------------------------------------------------------------------- */
    7847             :     /*      Get the byte order byte.                                        */
    7848             :     /* -------------------------------------------------------------------- */
    7849      160381 :     int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
    7850      160381 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    7851           0 :         return OGRERR_CORRUPT_DATA;
    7852      160381 :     eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    7853             : 
    7854             :     /* -------------------------------------------------------------------- */
    7855             :     /*      Get the geometry feature type.                                  */
    7856             :     /* -------------------------------------------------------------------- */
    7857             :     OGRwkbGeometryType eGeometryType;
    7858             :     const OGRErr err =
    7859      160381 :         OGRReadWKBGeometryType(pabyData, eWkbVariant, &eGeometryType);
    7860      160381 :     if (wkbHasZ(eGeometryType))
    7861       62309 :         flags |= OGR_G_3D;
    7862      160381 :     if (wkbHasM(eGeometryType))
    7863       59692 :         flags |= OGR_G_MEASURED;
    7864             : 
    7865      160381 :     if (err != OGRERR_NONE || eGeometryType != getGeometryType())
    7866           0 :         return OGRERR_CORRUPT_DATA;
    7867             : 
    7868      160381 :     return OGRERR_NONE;
    7869             : }
    7870             : 
    7871             : /************************************************************************/
    7872             : /*                    importPreambleOfCollectionFromWkb()              */
    7873             : /*                                                                      */
    7874             : /*      Utility method for OGRSimpleCurve, OGRCompoundCurve,            */
    7875             : /*      OGRCurvePolygon and OGRGeometryCollection.                      */
    7876             : /************************************************************************/
    7877             : 
    7878       77013 : OGRErr OGRGeometry::importPreambleOfCollectionFromWkb(
    7879             :     const unsigned char *pabyData, size_t &nSize, size_t &nDataOffset,
    7880             :     OGRwkbByteOrder &eByteOrder, size_t nMinSubGeomSize, int &nGeomCount,
    7881             :     OGRwkbVariant eWkbVariant)
    7882             : {
    7883       77013 :     nGeomCount = 0;
    7884             : 
    7885             :     OGRErr eErr =
    7886       77013 :         importPreambleFromWkb(pabyData, nSize, eByteOrder, eWkbVariant);
    7887       77013 :     if (eErr != OGRERR_NONE)
    7888           0 :         return eErr;
    7889             : 
    7890             :     /* -------------------------------------------------------------------- */
    7891             :     /*      Clear existing Geoms.                                           */
    7892             :     /* -------------------------------------------------------------------- */
    7893       77013 :     int _flags = flags;  // flags set in importPreambleFromWkb
    7894       77013 :     empty();             // may reset flags etc.
    7895             : 
    7896             :     // restore
    7897       77013 :     if (_flags & OGR_G_3D)
    7898       59265 :         set3D(TRUE);
    7899       77013 :     if (_flags & OGR_G_MEASURED)
    7900       56768 :         setMeasured(TRUE);
    7901             : 
    7902             :     /* -------------------------------------------------------------------- */
    7903             :     /*      Get the sub-geometry count.                                     */
    7904             :     /* -------------------------------------------------------------------- */
    7905       77013 :     memcpy(&nGeomCount, pabyData + 5, 4);
    7906             : 
    7907       77013 :     if (OGR_SWAP(eByteOrder))
    7908         386 :         nGeomCount = CPL_SWAP32(nGeomCount);
    7909             : 
    7910      153892 :     if (nGeomCount < 0 ||
    7911       76879 :         static_cast<size_t>(nGeomCount) >
    7912       76879 :             std::numeric_limits<size_t>::max() / nMinSubGeomSize)
    7913             :     {
    7914         134 :         nGeomCount = 0;
    7915         134 :         return OGRERR_CORRUPT_DATA;
    7916             :     }
    7917       76879 :     const size_t nBufferMinSize = nGeomCount * nMinSubGeomSize;
    7918             : 
    7919             :     // Each ring has a minimum of nMinSubGeomSize bytes.
    7920       76879 :     if (nSize != static_cast<size_t>(-1) && nSize - 9 < nBufferMinSize)
    7921             :     {
    7922         910 :         CPLError(CE_Failure, CPLE_AppDefined,
    7923             :                  "Length of input WKB is too small");
    7924         910 :         nGeomCount = 0;
    7925         910 :         return OGRERR_NOT_ENOUGH_DATA;
    7926             :     }
    7927             : 
    7928       75969 :     nDataOffset = 9;
    7929       75969 :     if (nSize != static_cast<size_t>(-1))
    7930             :     {
    7931       75949 :         CPLAssert(nSize >= nDataOffset);
    7932       75949 :         nSize -= nDataOffset;
    7933             :     }
    7934             : 
    7935       75969 :     return OGRERR_NONE;
    7936             : }
    7937             : 
    7938             : /************************************************************************/
    7939             : /*                      importCurveCollectionFromWkt()                  */
    7940             : /*                                                                      */
    7941             : /*      Utility method for OGRCompoundCurve, OGRCurvePolygon and        */
    7942             : /*      OGRMultiCurve.                                                  */
    7943             : /************************************************************************/
    7944             : 
    7945        1450 : OGRErr OGRGeometry::importCurveCollectionFromWkt(
    7946             :     const char **ppszInput, int bAllowEmptyComponent, int bAllowLineString,
    7947             :     int bAllowCurve, int bAllowCompoundCurve,
    7948             :     OGRErr (*pfnAddCurveDirectly)(OGRGeometry *poSelf, OGRCurve *poCurve))
    7949             : 
    7950             : {
    7951        1450 :     int bHasZ = FALSE;
    7952        1450 :     int bHasM = FALSE;
    7953        1450 :     bool bIsEmpty = false;
    7954        1450 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
    7955        1450 :     flags = 0;
    7956        1450 :     if (eErr != OGRERR_NONE)
    7957          14 :         return eErr;
    7958        1436 :     if (bHasZ)
    7959         206 :         flags |= OGR_G_3D;
    7960        1436 :     if (bHasM)
    7961         132 :         flags |= OGR_G_MEASURED;
    7962        1436 :     if (bIsEmpty)
    7963         111 :         return OGRERR_NONE;
    7964             : 
    7965             :     char szToken[OGR_WKT_TOKEN_MAX];
    7966        1325 :     const char *pszInput = *ppszInput;
    7967        1325 :     eErr = OGRERR_NONE;
    7968             : 
    7969             :     // Skip first '('.
    7970        1325 :     pszInput = OGRWktReadToken(pszInput, szToken);
    7971             : 
    7972             :     /* ==================================================================== */
    7973             :     /*      Read each curve in turn.  Note that we try to reuse the same    */
    7974             :     /*      point list buffer from curve to curve to cut down on            */
    7975             :     /*      allocate/deallocate overhead.                                   */
    7976             :     /* ==================================================================== */
    7977        1325 :     OGRRawPoint *paoPoints = nullptr;
    7978        1325 :     int nMaxPoints = 0;
    7979        1325 :     double *padfZ = nullptr;
    7980             : 
    7981         656 :     do
    7982             :     {
    7983             : 
    7984             :         /* --------------------------------------------------------------------
    7985             :          */
    7986             :         /*      Get the first token, which should be the geometry type. */
    7987             :         /* --------------------------------------------------------------------
    7988             :          */
    7989        1981 :         const char *pszInputBefore = pszInput;
    7990        1981 :         pszInput = OGRWktReadToken(pszInput, szToken);
    7991             : 
    7992             :         /* --------------------------------------------------------------------
    7993             :          */
    7994             :         /*      Do the import. */
    7995             :         /* --------------------------------------------------------------------
    7996             :          */
    7997        1981 :         OGRCurve *poCurve = nullptr;
    7998        1981 :         if (EQUAL(szToken, "("))
    7999             :         {
    8000        1427 :             OGRLineString *poLine = new OGRLineString();
    8001        1427 :             poCurve = poLine;
    8002        1427 :             pszInput = pszInputBefore;
    8003        1427 :             eErr = poLine->importFromWKTListOnly(&pszInput, bHasZ, bHasM,
    8004             :                                                  paoPoints, nMaxPoints, padfZ);
    8005             :         }
    8006         554 :         else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY"))
    8007             :         {
    8008          16 :             poCurve = new OGRLineString();
    8009             :         }
    8010             :         // Accept LINESTRING(), but this is an extension to the BNF, also
    8011             :         // accepted by PostGIS.
    8012         538 :         else if ((bAllowLineString && STARTS_WITH_CI(szToken, "LINESTRING")) ||
    8013         523 :                  (bAllowCurve && !STARTS_WITH_CI(szToken, "LINESTRING") &&
    8014         523 :                   !STARTS_WITH_CI(szToken, "COMPOUNDCURVE") &&
    8015        1235 :                   OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) ||
    8016         159 :                  (bAllowCompoundCurve &&
    8017         159 :                   STARTS_WITH_CI(szToken, "COMPOUNDCURVE")))
    8018             :         {
    8019         500 :             OGRGeometry *poGeom = nullptr;
    8020         500 :             pszInput = pszInputBefore;
    8021             :             eErr =
    8022         500 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
    8023         500 :             if (poGeom == nullptr)
    8024             :             {
    8025           1 :                 eErr = OGRERR_CORRUPT_DATA;
    8026             :             }
    8027             :             else
    8028             :             {
    8029         499 :                 poCurve = poGeom->toCurve();
    8030             :             }
    8031             :         }
    8032             :         else
    8033             :         {
    8034          38 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s",
    8035             :                      szToken);
    8036          38 :             eErr = OGRERR_CORRUPT_DATA;
    8037             :         }
    8038             : 
    8039             :         // If this has M it is an error if poGeom does not have M.
    8040        1981 :         if (poCurve && !Is3D() && IsMeasured() && !poCurve->IsMeasured())
    8041           0 :             eErr = OGRERR_CORRUPT_DATA;
    8042             : 
    8043        1981 :         if (eErr == OGRERR_NONE)
    8044        1936 :             eErr = pfnAddCurveDirectly(this, poCurve);
    8045        1981 :         if (eErr != OGRERR_NONE)
    8046             :         {
    8047          55 :             delete poCurve;
    8048          55 :             break;
    8049             :         }
    8050             : 
    8051             :         /* --------------------------------------------------------------------
    8052             :          */
    8053             :         /*      Read the delimiter following the surface. */
    8054             :         /* --------------------------------------------------------------------
    8055             :          */
    8056        1926 :         pszInput = OGRWktReadToken(pszInput, szToken);
    8057        1926 :     } while (szToken[0] == ',' && eErr == OGRERR_NONE);
    8058             : 
    8059        1325 :     CPLFree(paoPoints);
    8060        1325 :     CPLFree(padfZ);
    8061             : 
    8062             :     /* -------------------------------------------------------------------- */
    8063             :     /*      freak if we don't get a closing bracket.                        */
    8064             :     /* -------------------------------------------------------------------- */
    8065             : 
    8066        1325 :     if (eErr != OGRERR_NONE)
    8067          55 :         return eErr;
    8068             : 
    8069        1270 :     if (szToken[0] != ')')
    8070           9 :         return OGRERR_CORRUPT_DATA;
    8071             : 
    8072        1261 :     *ppszInput = pszInput;
    8073        1261 :     return OGRERR_NONE;
    8074             : }
    8075             : 
    8076             : //! @endcond
    8077             : 
    8078             : /************************************************************************/
    8079             : /*                           OGR_GT_Flatten()                           */
    8080             : /************************************************************************/
    8081             : /**
    8082             :  * \brief Returns the 2D geometry type corresponding to the passed geometry
    8083             :  * type.
    8084             :  *
    8085             :  * This function is intended to work with geometry types as old-style 99-402
    8086             :  * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and
    8087             :  * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types.
    8088             :  *
    8089             :  * @param eType Input geometry type
    8090             :  *
    8091             :  * @return 2D geometry type corresponding to the passed geometry type.
    8092             :  *
    8093             :  */
    8094             : 
    8095     7889970 : OGRwkbGeometryType OGR_GT_Flatten(OGRwkbGeometryType eType)
    8096             : {
    8097     7889970 :     eType = static_cast<OGRwkbGeometryType>(eType & (~wkb25DBitInternalUse));
    8098     7889970 :     if (eType >= 1000 && eType < 2000)  // ISO Z.
    8099     2776760 :         return static_cast<OGRwkbGeometryType>(eType - 1000);
    8100     5113210 :     if (eType >= 2000 && eType < 3000)  // ISO M.
    8101        6059 :         return static_cast<OGRwkbGeometryType>(eType - 2000);
    8102     5107150 :     if (eType >= 3000 && eType < 4000)  // ISO ZM.
    8103      136249 :         return static_cast<OGRwkbGeometryType>(eType - 3000);
    8104     4970900 :     return eType;
    8105             : }
    8106             : 
    8107             : /************************************************************************/
    8108             : /*                            OGR_GT_HasZ()                             */
    8109             : /************************************************************************/
    8110             : /**
    8111             :  * \brief Return if the geometry type is a 3D geometry type.
    8112             :  *
    8113             :  * @param eType Input geometry type
    8114             :  *
    8115             :  * @return TRUE if the geometry type is a 3D geometry type.
    8116             :  *
    8117             :  */
    8118             : 
    8119     2083450 : int OGR_GT_HasZ(OGRwkbGeometryType eType)
    8120             : {
    8121     2083450 :     if (eType & wkb25DBitInternalUse)
    8122      157130 :         return TRUE;
    8123     1926320 :     if (eType >= 1000 && eType < 2000)  // Accept 1000 for wkbUnknownZ.
    8124         264 :         return TRUE;
    8125     1926060 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8126      121539 :         return TRUE;
    8127     1804520 :     return FALSE;
    8128             : }
    8129             : 
    8130             : /************************************************************************/
    8131             : /*                            OGR_GT_HasM()                             */
    8132             : /************************************************************************/
    8133             : /**
    8134             :  * \brief Return if the geometry type is a measured type.
    8135             :  *
    8136             :  * @param eType Input geometry type
    8137             :  *
    8138             :  * @return TRUE if the geometry type is a measured type.
    8139             :  *
    8140             :  */
    8141             : 
    8142     2144850 : int OGR_GT_HasM(OGRwkbGeometryType eType)
    8143             : {
    8144     2144850 :     if (eType >= 2000 && eType < 3000)  // Accept 2000 for wkbUnknownM.
    8145        2593 :         return TRUE;
    8146     2142250 :     if (eType >= 3000 && eType < 4000)  // Accept 3000 for wkbUnknownZM.
    8147      121195 :         return TRUE;
    8148     2021060 :     return FALSE;
    8149             : }
    8150             : 
    8151             : /************************************************************************/
    8152             : /*                            OGR_GT_SetZ()                             */
    8153             : /************************************************************************/
    8154             : /**
    8155             :  * \brief Returns the 3D geometry type corresponding to the passed geometry
    8156             :  * type.
    8157             :  *
    8158             :  * @param eType Input geometry type
    8159             :  *
    8160             :  * @return 3D geometry type corresponding to the passed geometry type.
    8161             :  *
    8162             :  */
    8163             : 
    8164        5748 : OGRwkbGeometryType OGR_GT_SetZ(OGRwkbGeometryType eType)
    8165             : {
    8166        5748 :     if (OGR_GT_HasZ(eType) || eType == wkbNone)
    8167         498 :         return eType;
    8168        5250 :     if (eType <= wkbGeometryCollection)
    8169        5148 :         return static_cast<OGRwkbGeometryType>(eType | wkb25DBitInternalUse);
    8170             :     else
    8171         102 :         return static_cast<OGRwkbGeometryType>(eType + 1000);
    8172             : }
    8173             : 
    8174             : /************************************************************************/
    8175             : /*                            OGR_GT_SetM()                             */
    8176             : /************************************************************************/
    8177             : /**
    8178             :  * \brief Returns the measured geometry type corresponding to the passed
    8179             :  * geometry type.
    8180             :  *
    8181             :  * @param eType Input geometry type
    8182             :  *
    8183             :  * @return measured geometry type corresponding to the passed geometry type.
    8184             :  *
    8185             :  */
    8186             : 
    8187        2013 : OGRwkbGeometryType OGR_GT_SetM(OGRwkbGeometryType eType)
    8188             : {
    8189        2013 :     if (OGR_GT_HasM(eType) || eType == wkbNone)
    8190         262 :         return eType;
    8191        1751 :     if (eType & wkb25DBitInternalUse)
    8192             :     {
    8193         717 :         eType = static_cast<OGRwkbGeometryType>(eType & ~wkb25DBitInternalUse);
    8194         717 :         eType = static_cast<OGRwkbGeometryType>(eType + 1000);
    8195             :     }
    8196        1751 :     return static_cast<OGRwkbGeometryType>(eType + 2000);
    8197             : }
    8198             : 
    8199             : /************************************************************************/
    8200             : /*                         OGR_GT_SetModifier()                         */
    8201             : /************************************************************************/
    8202             : /**
    8203             :  * \brief Returns a XY, XYZ, XYM or XYZM geometry type depending on parameter.
    8204             :  *
    8205             :  * @param eType Input geometry type
    8206             :  * @param bHasZ TRUE if the output geometry type must be 3D.
    8207             :  * @param bHasM TRUE if the output geometry type must be measured.
    8208             :  *
    8209             :  * @return Output geometry type.
    8210             :  *
    8211             :  */
    8212             : 
    8213        5578 : OGRwkbGeometryType OGR_GT_SetModifier(OGRwkbGeometryType eType, int bHasZ,
    8214             :                                       int bHasM)
    8215             : {
    8216        5578 :     if (bHasZ && bHasM)
    8217         342 :         return OGR_GT_SetM(OGR_GT_SetZ(eType));
    8218        5236 :     else if (bHasM)
    8219         333 :         return OGR_GT_SetM(wkbFlatten(eType));
    8220        4903 :     else if (bHasZ)
    8221        2110 :         return OGR_GT_SetZ(wkbFlatten(eType));
    8222             :     else
    8223        2793 :         return wkbFlatten(eType);
    8224             : }
    8225             : 
    8226             : /************************************************************************/
    8227             : /*                         OGR_GT_IsSubClassOf)                         */
    8228             : /************************************************************************/
    8229             : /**
    8230             :  * \brief Returns if a type is a subclass of another one
    8231             :  *
    8232             :  * @param eType Type.
    8233             :  * @param eSuperType Super type
    8234             :  *
    8235             :  * @return TRUE if eType is a subclass of eSuperType.
    8236             :  *
    8237             :  */
    8238             : 
    8239      132549 : int OGR_GT_IsSubClassOf(OGRwkbGeometryType eType, OGRwkbGeometryType eSuperType)
    8240             : {
    8241      132549 :     eSuperType = wkbFlatten(eSuperType);
    8242      132549 :     eType = wkbFlatten(eType);
    8243             : 
    8244      132549 :     if (eSuperType == eType || eSuperType == wkbUnknown)
    8245       20031 :         return TRUE;
    8246             : 
    8247      112518 :     if (eSuperType == wkbGeometryCollection)
    8248       31327 :         return eType == wkbMultiPoint || eType == wkbMultiLineString ||
    8249       64654 :                eType == wkbMultiPolygon || eType == wkbMultiCurve ||
    8250       33327 :                eType == wkbMultiSurface;
    8251             : 
    8252       79191 :     if (eSuperType == wkbCurvePolygon)
    8253       21967 :         return eType == wkbPolygon || eType == wkbTriangle;
    8254             : 
    8255       57224 :     if (eSuperType == wkbMultiCurve)
    8256         249 :         return eType == wkbMultiLineString;
    8257             : 
    8258       56975 :     if (eSuperType == wkbMultiSurface)
    8259         288 :         return eType == wkbMultiPolygon;
    8260             : 
    8261       56687 :     if (eSuperType == wkbCurve)
    8262       23399 :         return eType == wkbLineString || eType == wkbCircularString ||
    8263       23399 :                eType == wkbCompoundCurve;
    8264             : 
    8265       33288 :     if (eSuperType == wkbSurface)
    8266        3521 :         return eType == wkbCurvePolygon || eType == wkbPolygon ||
    8267        7190 :                eType == wkbTriangle || eType == wkbPolyhedralSurface ||
    8268        3669 :                eType == wkbTIN;
    8269             : 
    8270       29619 :     if (eSuperType == wkbPolygon)
    8271         221 :         return eType == wkbTriangle;
    8272             : 
    8273       29398 :     if (eSuperType == wkbPolyhedralSurface)
    8274       14654 :         return eType == wkbTIN;
    8275             : 
    8276       14744 :     return FALSE;
    8277             : }
    8278             : 
    8279             : /************************************************************************/
    8280             : /*                        OGR_GT_GetCollection()                        */
    8281             : /************************************************************************/
    8282             : /**
    8283             :  * \brief Returns the collection type that can contain the passed geometry type
    8284             :  *
    8285             :  * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint,
    8286             :  * wkbLineString->wkbMultiLineString,
    8287             :  * wkbPolygon/wkbTriangle/wkbPolyhedralSurface/wkbTIN->wkbMultiPolygon,
    8288             :  * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve,
    8289             :  * wkbCurvePolygon->wkbMultiSurface.
    8290             :  * In other cases, wkbUnknown is returned
    8291             :  *
    8292             :  * Passed Z, M, ZM flag is preserved.
    8293             :  *
    8294             :  *
    8295             :  * @param eType Input geometry type
    8296             :  *
    8297             :  * @return the collection type that can contain the passed geometry type or
    8298             :  * wkbUnknown
    8299             :  *
    8300             :  */
    8301             : 
    8302        2709 : OGRwkbGeometryType OGR_GT_GetCollection(OGRwkbGeometryType eType)
    8303             : {
    8304        2709 :     const bool bHasZ = wkbHasZ(eType);
    8305        2709 :     const bool bHasM = wkbHasM(eType);
    8306        2709 :     if (eType == wkbNone)
    8307           1 :         return wkbNone;
    8308        2708 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8309        2708 :     if (eFGType == wkbPoint)
    8310          67 :         eType = wkbMultiPoint;
    8311             : 
    8312        2641 :     else if (eFGType == wkbLineString)
    8313         187 :         eType = wkbMultiLineString;
    8314             : 
    8315        2454 :     else if (eFGType == wkbPolygon)
    8316        1004 :         eType = wkbMultiPolygon;
    8317             : 
    8318        1450 :     else if (eFGType == wkbTriangle)
    8319           7 :         eType = wkbTIN;
    8320             : 
    8321        1443 :     else if (OGR_GT_IsCurve(eFGType))
    8322         189 :         eType = wkbMultiCurve;
    8323             : 
    8324        1254 :     else if (OGR_GT_IsSurface(eFGType))
    8325         952 :         eType = wkbMultiSurface;
    8326             : 
    8327             :     else
    8328         302 :         return wkbUnknown;
    8329             : 
    8330        2406 :     if (bHasZ)
    8331           3 :         eType = wkbSetZ(eType);
    8332        2406 :     if (bHasM)
    8333           3 :         eType = wkbSetM(eType);
    8334             : 
    8335        2406 :     return eType;
    8336             : }
    8337             : 
    8338             : /************************************************************************/
    8339             : /*                          OGR_GT_GetSingle()                          */
    8340             : /************************************************************************/
    8341             : /**
    8342             :  * \brief Returns the non-collection type that be contained in the passed
    8343             :  * geometry type.
    8344             :  *
    8345             :  * Handled conversions are : wkbNone->wkbNone, wkbMultiPoint -> wkbPoint,
    8346             :  * wkbMultiLineString -> wkbLineString, wkbMultiPolygon -> wkbPolygon,
    8347             :  * wkbMultiCurve -> wkbCompoundCurve, wkbMultiSurface -> wkbCurvePolygon,
    8348             :  * wkbGeometryCollection -> wkbUnknown
    8349             :  * In other cases, the original geometry is returned.
    8350             :  *
    8351             :  * Passed Z, M, ZM flag is preserved.
    8352             :  *
    8353             :  *
    8354             :  * @param eType Input geometry type
    8355             :  *
    8356             :  * @return the the non-collection type that be contained in the passed geometry
    8357             :  * type or wkbUnknown
    8358             :  *
    8359             :  * @since GDAL 3.11
    8360             :  */
    8361             : 
    8362          43 : OGRwkbGeometryType OGR_GT_GetSingle(OGRwkbGeometryType eType)
    8363             : {
    8364          43 :     const bool bHasZ = wkbHasZ(eType);
    8365          43 :     const bool bHasM = wkbHasM(eType);
    8366          43 :     if (eType == wkbNone)
    8367           1 :         return wkbNone;
    8368          42 :     const OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8369          42 :     if (eFGType == wkbMultiPoint)
    8370           8 :         eType = wkbPoint;
    8371             : 
    8372          34 :     else if (eFGType == wkbMultiLineString)
    8373           4 :         eType = wkbLineString;
    8374             : 
    8375          30 :     else if (eFGType == wkbMultiPolygon)
    8376           2 :         eType = wkbPolygon;
    8377             : 
    8378          28 :     else if (eFGType == wkbMultiCurve)
    8379           2 :         eType = wkbCompoundCurve;
    8380             : 
    8381          26 :     else if (eFGType == wkbMultiSurface)
    8382           2 :         eType = wkbCurvePolygon;
    8383             : 
    8384          24 :     else if (eFGType == wkbGeometryCollection)
    8385           1 :         return wkbUnknown;
    8386             : 
    8387          41 :     if (bHasZ)
    8388           3 :         eType = wkbSetZ(eType);
    8389          41 :     if (bHasM)
    8390           2 :         eType = wkbSetM(eType);
    8391             : 
    8392          41 :     return eType;
    8393             : }
    8394             : 
    8395             : /************************************************************************/
    8396             : /*                          OGR_GT_GetCurve()                           */
    8397             : /************************************************************************/
    8398             : /**
    8399             :  * \brief Returns the curve geometry type that can contain the passed geometry
    8400             :  * type
    8401             :  *
    8402             :  * Handled conversions are : wkbPolygon -> wkbCurvePolygon,
    8403             :  * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface
    8404             :  * and wkbMultiLineString->wkbMultiCurve.
    8405             :  * In other cases, the passed geometry is returned.
    8406             :  *
    8407             :  * Passed Z, M, ZM flag is preserved.
    8408             :  *
    8409             :  * @param eType Input geometry type
    8410             :  *
    8411             :  * @return the curve type that can contain the passed geometry type
    8412             :  *
    8413             :  */
    8414             : 
    8415          35 : OGRwkbGeometryType OGR_GT_GetCurve(OGRwkbGeometryType eType)
    8416             : {
    8417          35 :     const bool bHasZ = wkbHasZ(eType);
    8418          35 :     const bool bHasM = wkbHasM(eType);
    8419          35 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8420             : 
    8421          35 :     if (eFGType == wkbLineString)
    8422           3 :         eType = wkbCompoundCurve;
    8423             : 
    8424          32 :     else if (eFGType == wkbPolygon)
    8425           1 :         eType = wkbCurvePolygon;
    8426             : 
    8427          31 :     else if (eFGType == wkbTriangle)
    8428           0 :         eType = wkbCurvePolygon;
    8429             : 
    8430          31 :     else if (eFGType == wkbMultiLineString)
    8431           6 :         eType = wkbMultiCurve;
    8432             : 
    8433          25 :     else if (eFGType == wkbMultiPolygon)
    8434           4 :         eType = wkbMultiSurface;
    8435             : 
    8436          35 :     if (bHasZ)
    8437           4 :         eType = wkbSetZ(eType);
    8438          35 :     if (bHasM)
    8439           4 :         eType = wkbSetM(eType);
    8440             : 
    8441          35 :     return eType;
    8442             : }
    8443             : 
    8444             : /************************************************************************/
    8445             : /*                          OGR_GT_GetLinear()                          */
    8446             : /************************************************************************/
    8447             : /**
    8448             :  * \brief Returns the non-curve geometry type that can contain the passed
    8449             :  * geometry type
    8450             :  *
    8451             :  * Handled conversions are : wkbCurvePolygon -> wkbPolygon,
    8452             :  * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString,
    8453             :  * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString.
    8454             :  * In other cases, the passed geometry is returned.
    8455             :  *
    8456             :  * Passed Z, M, ZM flag is preserved.
    8457             :  *
    8458             :  * @param eType Input geometry type
    8459             :  *
    8460             :  * @return the non-curve type that can contain the passed geometry type
    8461             :  *
    8462             :  */
    8463             : 
    8464         785 : OGRwkbGeometryType OGR_GT_GetLinear(OGRwkbGeometryType eType)
    8465             : {
    8466         785 :     const bool bHasZ = wkbHasZ(eType);
    8467         785 :     const bool bHasM = wkbHasM(eType);
    8468         785 :     OGRwkbGeometryType eFGType = wkbFlatten(eType);
    8469             : 
    8470         785 :     if (OGR_GT_IsCurve(eFGType))
    8471          56 :         eType = wkbLineString;
    8472             : 
    8473         729 :     else if (OGR_GT_IsSurface(eFGType))
    8474          43 :         eType = wkbPolygon;
    8475             : 
    8476         686 :     else if (eFGType == wkbMultiCurve)
    8477         186 :         eType = wkbMultiLineString;
    8478             : 
    8479         500 :     else if (eFGType == wkbMultiSurface)
    8480         166 :         eType = wkbMultiPolygon;
    8481             : 
    8482         785 :     if (bHasZ)
    8483         154 :         eType = wkbSetZ(eType);
    8484         785 :     if (bHasM)
    8485         101 :         eType = wkbSetM(eType);
    8486             : 
    8487         785 :     return eType;
    8488             : }
    8489             : 
    8490             : /************************************************************************/
    8491             : /*                           OGR_GT_IsCurve()                           */
    8492             : /************************************************************************/
    8493             : 
    8494             : /**
    8495             :  * \brief Return if a geometry type is an instance of Curve
    8496             :  *
    8497             :  * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve
    8498             :  * and their Z/M/ZM variant.
    8499             :  *
    8500             :  * @param eGeomType the geometry type
    8501             :  * @return TRUE if the geometry type is an instance of Curve
    8502             :  *
    8503             :  */
    8504             : 
    8505       23390 : int OGR_GT_IsCurve(OGRwkbGeometryType eGeomType)
    8506             : {
    8507       23390 :     return OGR_GT_IsSubClassOf(eGeomType, wkbCurve);
    8508             : }
    8509             : 
    8510             : /************************************************************************/
    8511             : /*                          OGR_GT_IsSurface()                          */
    8512             : /************************************************************************/
    8513             : 
    8514             : /**
    8515             :  * \brief Return if a geometry type is an instance of Surface
    8516             :  *
    8517             :  * Such geometry type are wkbCurvePolygon and wkbPolygon
    8518             :  * and their Z/M/ZM variant.
    8519             :  *
    8520             :  * @param eGeomType the geometry type
    8521             :  * @return TRUE if the geometry type is an instance of Surface
    8522             :  *
    8523             :  */
    8524             : 
    8525        3663 : int OGR_GT_IsSurface(OGRwkbGeometryType eGeomType)
    8526             : {
    8527        3663 :     return OGR_GT_IsSubClassOf(eGeomType, wkbSurface);
    8528             : }
    8529             : 
    8530             : /************************************************************************/
    8531             : /*                         OGR_GT_IsNonLinear()                         */
    8532             : /************************************************************************/
    8533             : 
    8534             : /**
    8535             :  * \brief Return if a geometry type is a non-linear geometry type.
    8536             :  *
    8537             :  * Such geometry type are wkbCurve, wkbCircularString, wkbCompoundCurve,
    8538             :  * wkbSurface, wkbCurvePolygon, wkbMultiCurve, wkbMultiSurface and their
    8539             :  * Z/M variants.
    8540             :  *
    8541             :  * @param eGeomType the geometry type
    8542             :  * @return TRUE if the geometry type is a non-linear geometry type.
    8543             :  *
    8544             :  */
    8545             : 
    8546      118469 : int OGR_GT_IsNonLinear(OGRwkbGeometryType eGeomType)
    8547             : {
    8548      118469 :     OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType);
    8549      118461 :     return eFGeomType == wkbCurve || eFGeomType == wkbSurface ||
    8550      118388 :            eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve ||
    8551      236930 :            eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve ||
    8552      118469 :            eFGeomType == wkbMultiSurface;
    8553             : }
    8554             : 
    8555             : /************************************************************************/
    8556             : /*                            CastToError()                             */
    8557             : /************************************************************************/
    8558             : 
    8559             : //! @cond Doxygen_Suppress
    8560           0 : OGRGeometry *OGRGeometry::CastToError(OGRGeometry *poGeom)
    8561             : {
    8562           0 :     CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible",
    8563           0 :              poGeom->getGeometryName());
    8564           0 :     delete poGeom;
    8565           0 :     return nullptr;
    8566             : }
    8567             : 
    8568             : //! @endcond
    8569             : 
    8570             : /************************************************************************/
    8571             : /*                         OGRexportToSFCGAL()                          */
    8572             : /************************************************************************/
    8573             : 
    8574             : //! @cond Doxygen_Suppress
    8575             : sfcgal_geometry_t *
    8576           0 : OGRGeometry::OGRexportToSFCGAL(UNUSED_IF_NO_SFCGAL const OGRGeometry *poGeom)
    8577             : {
    8578             : #ifdef HAVE_SFCGAL
    8579             : 
    8580             :     sfcgal_init();
    8581             : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8582             : 
    8583             :     const auto exportToSFCGALViaWKB =
    8584             :         [](const OGRGeometry *geom) -> sfcgal_geometry_t *
    8585             :     {
    8586             :         if (!geom)
    8587             :             return nullptr;
    8588             : 
    8589             :         // Get WKB size and allocate buffer
    8590             :         size_t nSize = geom->WkbSize();
    8591             :         unsigned char *pabyWkb = static_cast<unsigned char *>(CPLMalloc(nSize));
    8592             : 
    8593             :         // Set export options with NDR byte order
    8594             :         OGRwkbExportOptions oOptions;
    8595             :         oOptions.eByteOrder = wkbNDR;
    8596             :         // and ISO to avoid wkb25DBit for Z geometries
    8597             :         oOptions.eWkbVariant = wkbVariantIso;
    8598             : 
    8599             :         // Export to WKB
    8600             :         sfcgal_geometry_t *sfcgalGeom = nullptr;
    8601             :         if (geom->exportToWkb(pabyWkb, &oOptions) == OGRERR_NONE)
    8602             :         {
    8603             :             sfcgalGeom = sfcgal_io_read_wkb(
    8604             :                 reinterpret_cast<const char *>(pabyWkb), nSize);
    8605             :         }
    8606             : 
    8607             :         CPLFree(pabyWkb);
    8608             :         return sfcgalGeom;
    8609             :     };
    8610             : 
    8611             :     // Handle special cases
    8612             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8613             :     {
    8614             :         std::unique_ptr<OGRLineString> poLS(
    8615             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8616             :         return exportToSFCGALViaWKB(poLS.get());
    8617             :     }
    8618             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8619             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8620             :     {
    8621             :         std::unique_ptr<OGRLineString> poLS(
    8622             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8623             :                 ->toLineString());
    8624             :         return exportToSFCGALViaWKB(poLS.get());
    8625             :     }
    8626             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8627             :     {
    8628             :         std::unique_ptr<OGRPolygon> poPolygon(
    8629             :             OGRGeometryFactory::forceToPolygon(
    8630             :                 poGeom->clone()->toCurvePolygon())
    8631             :                 ->toPolygon());
    8632             :         return exportToSFCGALViaWKB(poPolygon.get());
    8633             :     }
    8634             :     else
    8635             :     {
    8636             :         // Default case - direct export
    8637             :         return exportToSFCGALViaWKB(poGeom);
    8638             :     }
    8639             : #else
    8640             :     char *buffer = nullptr;
    8641             : 
    8642             :     // special cases - LinearRing, Circular String, Compound Curve, Curve
    8643             :     // Polygon
    8644             : 
    8645             :     if (EQUAL(poGeom->getGeometryName(), "LINEARRING"))
    8646             :     {
    8647             :         // cast it to LineString and get the WKT
    8648             :         std::unique_ptr<OGRLineString> poLS(
    8649             :             OGRCurve::CastToLineString(poGeom->clone()->toCurve()));
    8650             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8651             :         {
    8652             :             sfcgal_geometry_t *_geometry =
    8653             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8654             :             CPLFree(buffer);
    8655             :             return _geometry;
    8656             :         }
    8657             :         else
    8658             :         {
    8659             :             CPLFree(buffer);
    8660             :             return nullptr;
    8661             :         }
    8662             :     }
    8663             :     else if (EQUAL(poGeom->getGeometryName(), "CIRCULARSTRING") ||
    8664             :              EQUAL(poGeom->getGeometryName(), "COMPOUNDCURVE"))
    8665             :     {
    8666             :         // convert it to LineString and get the WKT
    8667             :         std::unique_ptr<OGRLineString> poLS(
    8668             :             OGRGeometryFactory::forceToLineString(poGeom->clone())
    8669             :                 ->toLineString());
    8670             :         if (poLS->exportToWkt(&buffer) == OGRERR_NONE)
    8671             :         {
    8672             :             sfcgal_geometry_t *_geometry =
    8673             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8674             :             CPLFree(buffer);
    8675             :             return _geometry;
    8676             :         }
    8677             :         else
    8678             :         {
    8679             :             CPLFree(buffer);
    8680             :             return nullptr;
    8681             :         }
    8682             :     }
    8683             :     else if (EQUAL(poGeom->getGeometryName(), "CURVEPOLYGON"))
    8684             :     {
    8685             :         // convert it to Polygon and get the WKT
    8686             :         std::unique_ptr<OGRPolygon> poPolygon(
    8687             :             OGRGeometryFactory::forceToPolygon(
    8688             :                 poGeom->clone()->toCurvePolygon())
    8689             :                 ->toPolygon());
    8690             :         if (poPolygon->exportToWkt(&buffer) == OGRERR_NONE)
    8691             :         {
    8692             :             sfcgal_geometry_t *_geometry =
    8693             :                 sfcgal_io_read_wkt(buffer, strlen(buffer));
    8694             :             CPLFree(buffer);
    8695             :             return _geometry;
    8696             :         }
    8697             :         else
    8698             :         {
    8699             :             CPLFree(buffer);
    8700             :             return nullptr;
    8701             :         }
    8702             :     }
    8703             :     else if (poGeom->exportToWkt(&buffer) == OGRERR_NONE)
    8704             :     {
    8705             :         sfcgal_geometry_t *_geometry =
    8706             :             sfcgal_io_read_wkt(buffer, strlen(buffer));
    8707             :         CPLFree(buffer);
    8708             :         return _geometry;
    8709             :     }
    8710             :     else
    8711             :     {
    8712             :         CPLFree(buffer);
    8713             :         return nullptr;
    8714             :     }
    8715             : #endif
    8716             : #else
    8717           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8718           0 :     return nullptr;
    8719             : #endif
    8720             : }
    8721             : 
    8722             : //! @endcond
    8723             : 
    8724             : /************************************************************************/
    8725             : /*                         SFCGALexportToOGR()                          */
    8726             : /************************************************************************/
    8727             : 
    8728             : //! @cond Doxygen_Suppress
    8729           0 : OGRGeometry *OGRGeometry::SFCGALexportToOGR(
    8730             :     UNUSED_IF_NO_SFCGAL const sfcgal_geometry_t *geometry)
    8731             : {
    8732             : #ifdef HAVE_SFCGAL
    8733             :     if (geometry == nullptr)
    8734             :         return nullptr;
    8735             : 
    8736             :     sfcgal_init();
    8737             :     char *pabySFCGAL = nullptr;
    8738             :     size_t nLength = 0;
    8739             : #if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION(1, 5, 2)
    8740             : 
    8741             :     sfcgal_geometry_as_wkb(geometry, &pabySFCGAL, &nLength);
    8742             : 
    8743             :     if (pabySFCGAL == nullptr || nLength == 0)
    8744             :         return nullptr;
    8745             : 
    8746             :     OGRGeometry *poGeom = nullptr;
    8747             :     OGRErr eErr = OGRGeometryFactory::createFromWkb(
    8748             :         reinterpret_cast<unsigned char *>(pabySFCGAL), nullptr, &poGeom,
    8749             :         nLength);
    8750             : 
    8751             :     free(pabySFCGAL);
    8752             : 
    8753             :     if (eErr == OGRERR_NONE)
    8754             :     {
    8755             :         return poGeom;
    8756             :     }
    8757             :     else
    8758             :     {
    8759             :         return nullptr;
    8760             :     }
    8761             : #else
    8762             :     sfcgal_geometry_as_text_decim(geometry, 19, &pabySFCGAL, &nLength);
    8763             :     char *pszWKT = static_cast<char *>(CPLMalloc(nLength + 1));
    8764             :     memcpy(pszWKT, pabySFCGAL, nLength);
    8765             :     pszWKT[nLength] = 0;
    8766             :     free(pabySFCGAL);
    8767             : 
    8768             :     sfcgal_geometry_type_t geom_type = sfcgal_geometry_type_id(geometry);
    8769             : 
    8770             :     OGRGeometry *poGeom = nullptr;
    8771             :     if (geom_type == SFCGAL_TYPE_POINT)
    8772             :     {
    8773             :         poGeom = new OGRPoint();
    8774             :     }
    8775             :     else if (geom_type == SFCGAL_TYPE_LINESTRING)
    8776             :     {
    8777             :         poGeom = new OGRLineString();
    8778             :     }
    8779             :     else if (geom_type == SFCGAL_TYPE_POLYGON)
    8780             :     {
    8781             :         poGeom = new OGRPolygon();
    8782             :     }
    8783             :     else if (geom_type == SFCGAL_TYPE_MULTIPOINT)
    8784             :     {
    8785             :         poGeom = new OGRMultiPoint();
    8786             :     }
    8787             :     else if (geom_type == SFCGAL_TYPE_MULTILINESTRING)
    8788             :     {
    8789             :         poGeom = new OGRMultiLineString();
    8790             :     }
    8791             :     else if (geom_type == SFCGAL_TYPE_MULTIPOLYGON)
    8792             :     {
    8793             :         poGeom = new OGRMultiPolygon();
    8794             :     }
    8795             :     else if (geom_type == SFCGAL_TYPE_GEOMETRYCOLLECTION)
    8796             :     {
    8797             :         poGeom = new OGRGeometryCollection();
    8798             :     }
    8799             :     else if (geom_type == SFCGAL_TYPE_TRIANGLE)
    8800             :     {
    8801             :         poGeom = new OGRTriangle();
    8802             :     }
    8803             :     else if (geom_type == SFCGAL_TYPE_POLYHEDRALSURFACE)
    8804             :     {
    8805             :         poGeom = new OGRPolyhedralSurface();
    8806             :     }
    8807             :     else if (geom_type == SFCGAL_TYPE_TRIANGULATEDSURFACE)
    8808             :     {
    8809             :         poGeom = new OGRTriangulatedSurface();
    8810             :     }
    8811             :     else
    8812             :     {
    8813             :         CPLFree(pszWKT);
    8814             :         return nullptr;
    8815             :     }
    8816             : 
    8817             :     const char *pszWKTTmp = pszWKT;
    8818             :     if (poGeom->importFromWkt(&pszWKTTmp) == OGRERR_NONE)
    8819             :     {
    8820             :         CPLFree(pszWKT);
    8821             :         return poGeom;
    8822             :     }
    8823             :     else
    8824             :     {
    8825             :         delete poGeom;
    8826             :         CPLFree(pszWKT);
    8827             :         return nullptr;
    8828             :     }
    8829             : #endif
    8830             : #else
    8831           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SFCGAL support not enabled.");
    8832           0 :     return nullptr;
    8833             : #endif
    8834             : }
    8835             : 
    8836             : //! @endcond
    8837             : 
    8838             : //! @cond Doxygen_Suppress
    8839       11036 : bool OGRGeometry::IsSFCGALCompatible() const
    8840             : {
    8841       11036 :     const OGRwkbGeometryType eGType = wkbFlatten(getGeometryType());
    8842       11036 :     if (eGType == wkbTriangle || eGType == wkbPolyhedralSurface ||
    8843             :         eGType == wkbTIN)
    8844             :     {
    8845           3 :         return TRUE;
    8846             :     }
    8847       11033 :     if (eGType == wkbGeometryCollection || eGType == wkbMultiSurface)
    8848             :     {
    8849          13 :         const OGRGeometryCollection *poGC = toGeometryCollection();
    8850          13 :         bool bIsSFCGALCompatible = false;
    8851          13 :         for (auto &&poSubGeom : *poGC)
    8852             :         {
    8853             :             OGRwkbGeometryType eSubGeomType =
    8854          13 :                 wkbFlatten(poSubGeom->getGeometryType());
    8855          13 :             if (eSubGeomType == wkbTIN || eSubGeomType == wkbPolyhedralSurface)
    8856             :             {
    8857           0 :                 bIsSFCGALCompatible = true;
    8858             :             }
    8859          13 :             else if (eSubGeomType != wkbMultiPolygon)
    8860             :             {
    8861          13 :                 bIsSFCGALCompatible = false;
    8862          13 :                 break;
    8863             :             }
    8864             :         }
    8865          13 :         return bIsSFCGALCompatible;
    8866             :     }
    8867       11020 :     return FALSE;
    8868             : }
    8869             : 
    8870             : //! @endcond
    8871             : 
    8872             : /************************************************************************/
    8873             : /*                      roundCoordinatesIEEE754()                       */
    8874             : /************************************************************************/
    8875             : 
    8876             : /** Round coordinates of a geometry, exploiting characteristics of the IEEE-754
    8877             :  * double-precision binary representation.
    8878             :  *
    8879             :  * Determines the number of bits (N) required to represent a coordinate value
    8880             :  * with a specified number of digits after the decimal point, and then sets all
    8881             :  * but the N most significant bits to zero. The resulting coordinate value will
    8882             :  * still round to the original value (e.g. after roundCoordinates()), but will
    8883             :  * have improved compressiblity.
    8884             :  *
    8885             :  * @param options Contains the precision requirements.
    8886             :  * @since GDAL 3.9
    8887             :  */
    8888           1 : void OGRGeometry::roundCoordinatesIEEE754(
    8889             :     const OGRGeomCoordinateBinaryPrecision &options)
    8890             : {
    8891             :     struct Quantizer : public OGRDefaultGeometryVisitor
    8892             :     {
    8893             :         const OGRGeomCoordinateBinaryPrecision &m_options;
    8894             : 
    8895           1 :         explicit Quantizer(const OGRGeomCoordinateBinaryPrecision &optionsIn)
    8896           1 :             : m_options(optionsIn)
    8897             :         {
    8898           1 :         }
    8899             : 
    8900             :         using OGRDefaultGeometryVisitor::visit;
    8901             : 
    8902           3 :         void visit(OGRPoint *poPoint) override
    8903             :         {
    8904           3 :             if (m_options.nXYBitPrecision != INT_MIN)
    8905             :             {
    8906             :                 uint64_t i;
    8907             :                 double d;
    8908           3 :                 d = poPoint->getX();
    8909           3 :                 memcpy(&i, &d, sizeof(i));
    8910           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8911           3 :                 memcpy(&d, &i, sizeof(i));
    8912           3 :                 poPoint->setX(d);
    8913           3 :                 d = poPoint->getY();
    8914           3 :                 memcpy(&i, &d, sizeof(i));
    8915           3 :                 i = OGRRoundValueIEEE754(i, m_options.nXYBitPrecision);
    8916           3 :                 memcpy(&d, &i, sizeof(i));
    8917           3 :                 poPoint->setY(d);
    8918             :             }
    8919           3 :             if (m_options.nZBitPrecision != INT_MIN && poPoint->Is3D())
    8920             :             {
    8921             :                 uint64_t i;
    8922             :                 double d;
    8923           3 :                 d = poPoint->getZ();
    8924           3 :                 memcpy(&i, &d, sizeof(i));
    8925           3 :                 i = OGRRoundValueIEEE754(i, m_options.nZBitPrecision);
    8926           3 :                 memcpy(&d, &i, sizeof(i));
    8927           3 :                 poPoint->setZ(d);
    8928             :             }
    8929           3 :             if (m_options.nMBitPrecision != INT_MIN && poPoint->IsMeasured())
    8930             :             {
    8931             :                 uint64_t i;
    8932             :                 double d;
    8933           3 :                 d = poPoint->getM();
    8934           3 :                 memcpy(&i, &d, sizeof(i));
    8935           3 :                 i = OGRRoundValueIEEE754(i, m_options.nMBitPrecision);
    8936           3 :                 memcpy(&d, &i, sizeof(i));
    8937           3 :                 poPoint->setM(d);
    8938             :             }
    8939           3 :         }
    8940             :     };
    8941             : 
    8942           2 :     Quantizer quantizer(options);
    8943           1 :     accept(&quantizer);
    8944           1 : }
    8945             : 
    8946             : /************************************************************************/
    8947             : /*                               visit()                                */
    8948             : /************************************************************************/
    8949             : 
    8950         105 : void OGRDefaultGeometryVisitor::_visit(OGRSimpleCurve *poGeom)
    8951             : {
    8952        1248 :     for (auto &&oPoint : *poGeom)
    8953             :     {
    8954        1143 :         oPoint.accept(this);
    8955             :     }
    8956         105 : }
    8957             : 
    8958         104 : void OGRDefaultGeometryVisitor::visit(OGRLineString *poGeom)
    8959             : {
    8960         104 :     _visit(poGeom);
    8961         104 : }
    8962             : 
    8963          80 : void OGRDefaultGeometryVisitor::visit(OGRLinearRing *poGeom)
    8964             : {
    8965          80 :     visit(poGeom->toUpperClass());
    8966          80 : }
    8967             : 
    8968           1 : void OGRDefaultGeometryVisitor::visit(OGRCircularString *poGeom)
    8969             : {
    8970           1 :     _visit(poGeom);
    8971           1 : }
    8972             : 
    8973          78 : void OGRDefaultGeometryVisitor::visit(OGRCurvePolygon *poGeom)
    8974             : {
    8975         159 :     for (auto &&poSubGeom : *poGeom)
    8976          81 :         poSubGeom->accept(this);
    8977          78 : }
    8978             : 
    8979          77 : void OGRDefaultGeometryVisitor::visit(OGRPolygon *poGeom)
    8980             : {
    8981          77 :     visit(poGeom->toUpperClass());
    8982          77 : }
    8983             : 
    8984           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiPoint *poGeom)
    8985             : {
    8986           1 :     visit(poGeom->toUpperClass());
    8987           1 : }
    8988             : 
    8989           8 : void OGRDefaultGeometryVisitor::visit(OGRMultiLineString *poGeom)
    8990             : {
    8991           8 :     visit(poGeom->toUpperClass());
    8992           8 : }
    8993             : 
    8994          14 : void OGRDefaultGeometryVisitor::visit(OGRMultiPolygon *poGeom)
    8995             : {
    8996          14 :     visit(poGeom->toUpperClass());
    8997          14 : }
    8998             : 
    8999          26 : void OGRDefaultGeometryVisitor::visit(OGRGeometryCollection *poGeom)
    9000             : {
    9001          75 :     for (auto &&poSubGeom : *poGeom)
    9002          49 :         poSubGeom->accept(this);
    9003          26 : }
    9004             : 
    9005           1 : void OGRDefaultGeometryVisitor::visit(OGRCompoundCurve *poGeom)
    9006             : {
    9007           2 :     for (auto &&poSubGeom : *poGeom)
    9008           1 :         poSubGeom->accept(this);
    9009           1 : }
    9010             : 
    9011           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiCurve *poGeom)
    9012             : {
    9013           1 :     visit(poGeom->toUpperClass());
    9014           1 : }
    9015             : 
    9016           1 : void OGRDefaultGeometryVisitor::visit(OGRMultiSurface *poGeom)
    9017             : {
    9018           1 :     visit(poGeom->toUpperClass());
    9019           1 : }
    9020             : 
    9021           2 : void OGRDefaultGeometryVisitor::visit(OGRTriangle *poGeom)
    9022             : {
    9023           2 :     visit(poGeom->toUpperClass());
    9024           2 : }
    9025             : 
    9026           2 : void OGRDefaultGeometryVisitor::visit(OGRPolyhedralSurface *poGeom)
    9027             : {
    9028           4 :     for (auto &&poSubGeom : *poGeom)
    9029           2 :         poSubGeom->accept(this);
    9030           2 : }
    9031             : 
    9032           1 : void OGRDefaultGeometryVisitor::visit(OGRTriangulatedSurface *poGeom)
    9033             : {
    9034           1 :     visit(poGeom->toUpperClass());
    9035           1 : }
    9036             : 
    9037         127 : void OGRDefaultConstGeometryVisitor::_visit(const OGRSimpleCurve *poGeom)
    9038             : {
    9039        2988 :     for (auto &&oPoint : *poGeom)
    9040             :     {
    9041        2861 :         oPoint.accept(this);
    9042             :     }
    9043         127 : }
    9044             : 
    9045         121 : void OGRDefaultConstGeometryVisitor::visit(const OGRLineString *poGeom)
    9046             : {
    9047         121 :     _visit(poGeom);
    9048         121 : }
    9049             : 
    9050         110 : void OGRDefaultConstGeometryVisitor::visit(const OGRLinearRing *poGeom)
    9051             : {
    9052         110 :     visit(poGeom->toUpperClass());
    9053         110 : }
    9054             : 
    9055           6 : void OGRDefaultConstGeometryVisitor::visit(const OGRCircularString *poGeom)
    9056             : {
    9057           6 :     _visit(poGeom);
    9058           6 : }
    9059             : 
    9060         112 : void OGRDefaultConstGeometryVisitor::visit(const OGRCurvePolygon *poGeom)
    9061             : {
    9062         225 :     for (auto &&poSubGeom : *poGeom)
    9063         113 :         poSubGeom->accept(this);
    9064         112 : }
    9065             : 
    9066         109 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolygon *poGeom)
    9067             : {
    9068         109 :     visit(poGeom->toUpperClass());
    9069         109 : }
    9070             : 
    9071          64 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPoint *poGeom)
    9072             : {
    9073          64 :     visit(poGeom->toUpperClass());
    9074          64 : }
    9075             : 
    9076           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiLineString *poGeom)
    9077             : {
    9078           1 :     visit(poGeom->toUpperClass());
    9079           1 : }
    9080             : 
    9081           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiPolygon *poGeom)
    9082             : {
    9083           1 :     visit(poGeom->toUpperClass());
    9084           1 : }
    9085             : 
    9086          69 : void OGRDefaultConstGeometryVisitor::visit(const OGRGeometryCollection *poGeom)
    9087             : {
    9088         325 :     for (auto &&poSubGeom : *poGeom)
    9089         256 :         poSubGeom->accept(this);
    9090          69 : }
    9091             : 
    9092           3 : void OGRDefaultConstGeometryVisitor::visit(const OGRCompoundCurve *poGeom)
    9093             : {
    9094          14 :     for (auto &&poSubGeom : *poGeom)
    9095          11 :         poSubGeom->accept(this);
    9096           3 : }
    9097             : 
    9098           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiCurve *poGeom)
    9099             : {
    9100           1 :     visit(poGeom->toUpperClass());
    9101           1 : }
    9102             : 
    9103           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRMultiSurface *poGeom)
    9104             : {
    9105           1 :     visit(poGeom->toUpperClass());
    9106           1 : }
    9107             : 
    9108           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangle *poGeom)
    9109             : {
    9110           2 :     visit(poGeom->toUpperClass());
    9111           2 : }
    9112             : 
    9113           2 : void OGRDefaultConstGeometryVisitor::visit(const OGRPolyhedralSurface *poGeom)
    9114             : {
    9115           4 :     for (auto &&poSubGeom : *poGeom)
    9116           2 :         poSubGeom->accept(this);
    9117           2 : }
    9118             : 
    9119           1 : void OGRDefaultConstGeometryVisitor::visit(const OGRTriangulatedSurface *poGeom)
    9120             : {
    9121           1 :     visit(poGeom->toUpperClass());
    9122           1 : }
    9123             : 
    9124             : /************************************************************************/
    9125             : /*                     OGRGeometryUniquePtrDeleter                      */
    9126             : /************************************************************************/
    9127             : 
    9128             : //! @cond Doxygen_Suppress
    9129        1333 : void OGRGeometryUniquePtrDeleter::operator()(OGRGeometry *poGeom) const
    9130             : {
    9131        1333 :     delete poGeom;
    9132        1333 : }
    9133             : 
    9134             : //! @endcond
    9135             : 
    9136             : /************************************************************************/
    9137             : /*                 OGRPreparedGeometryUniquePtrDeleter                  */
    9138             : /************************************************************************/
    9139             : 
    9140             : //! @cond Doxygen_Suppress
    9141         144 : void OGRPreparedGeometryUniquePtrDeleter::operator()(
    9142             :     OGRPreparedGeometry *poPreparedGeom) const
    9143             : {
    9144         144 :     OGRDestroyPreparedGeometry(poPreparedGeom);
    9145         144 : }
    9146             : 
    9147             : //! @endcond
    9148             : 
    9149             : /************************************************************************/
    9150             : /*                    HomogenizeDimensionalityWith()                    */
    9151             : /************************************************************************/
    9152             : 
    9153             : //! @cond Doxygen_Suppress
    9154     3302940 : void OGRGeometry::HomogenizeDimensionalityWith(OGRGeometry *poOtherGeom)
    9155             : {
    9156     3302940 :     if (poOtherGeom->Is3D() && !Is3D())
    9157     1328980 :         set3D(TRUE);
    9158             : 
    9159     3302940 :     if (poOtherGeom->IsMeasured() && !IsMeasured())
    9160         851 :         setMeasured(TRUE);
    9161             : 
    9162     3302940 :     if (!poOtherGeom->Is3D() && Is3D())
    9163         298 :         poOtherGeom->set3D(TRUE);
    9164             : 
    9165     3302940 :     if (!poOtherGeom->IsMeasured() && IsMeasured())
    9166          41 :         poOtherGeom->setMeasured(TRUE);
    9167     3302940 : }
    9168             : 
    9169             : //! @endcond
    9170             : 
    9171             : /************************************************************************/
    9172             : /*             OGRGeomCoordinateBinaryPrecision::SetFrom()              */
    9173             : /************************************************************************/
    9174             : 
    9175             : /** Set binary precision options from resolution.
    9176             :  *
    9177             :  * @since GDAL 3.9
    9178             :  */
    9179          16 : void OGRGeomCoordinateBinaryPrecision::SetFrom(
    9180             :     const OGRGeomCoordinatePrecision &prec)
    9181             : {
    9182          16 :     if (prec.dfXYResolution != 0)
    9183             :     {
    9184          16 :         nXYBitPrecision =
    9185          16 :             static_cast<int>(ceil(log2(1. / prec.dfXYResolution)));
    9186             :     }
    9187          16 :     if (prec.dfZResolution != 0)
    9188             :     {
    9189          12 :         nZBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfZResolution)));
    9190             :     }
    9191          16 :     if (prec.dfMResolution != 0)
    9192             :     {
    9193          12 :         nMBitPrecision = static_cast<int>(ceil(log2(1. / prec.dfMResolution)));
    9194             :     }
    9195          16 : }
    9196             : 
    9197             : /************************************************************************/
    9198             : /*                     OGRwkbExportOptionsCreate()                      */
    9199             : /************************************************************************/
    9200             : 
    9201             : /**
    9202             :  * \brief Create geometry WKB export options.
    9203             :  *
    9204             :  * The default is Intel order, old-OGC wkb variant and 0 discarded lsb bits.
    9205             :  *
    9206             :  * @return object to be freed with OGRwkbExportOptionsDestroy().
    9207             :  * @since GDAL 3.9
    9208             :  */
    9209           2 : OGRwkbExportOptions *OGRwkbExportOptionsCreate()
    9210             : {
    9211           2 :     return new OGRwkbExportOptions;
    9212             : }
    9213             : 
    9214             : /************************************************************************/
    9215             : /*                     OGRwkbExportOptionsDestroy()                     */
    9216             : /************************************************************************/
    9217             : 
    9218             : /**
    9219             :  * \brief Destroy object returned by OGRwkbExportOptionsCreate()
    9220             :  *
    9221             :  * @param psOptions WKB export options
    9222             :  * @since GDAL 3.9
    9223             :  */
    9224             : 
    9225           2 : void OGRwkbExportOptionsDestroy(OGRwkbExportOptions *psOptions)
    9226             : {
    9227           2 :     delete psOptions;
    9228           2 : }
    9229             : 
    9230             : /************************************************************************/
    9231             : /*                  OGRwkbExportOptionsSetByteOrder()                   */
    9232             : /************************************************************************/
    9233             : 
    9234             : /**
    9235             :  * \brief Set the WKB byte order.
    9236             :  *
    9237             :  * @param psOptions WKB export options
    9238             :  * @param eByteOrder Byte order: wkbXDR (big-endian) or wkbNDR (little-endian,
    9239             :  * Intel)
    9240             :  * @since GDAL 3.9
    9241             :  */
    9242             : 
    9243           1 : void OGRwkbExportOptionsSetByteOrder(OGRwkbExportOptions *psOptions,
    9244             :                                      OGRwkbByteOrder eByteOrder)
    9245             : {
    9246           1 :     psOptions->eByteOrder = eByteOrder;
    9247           1 : }
    9248             : 
    9249             : /************************************************************************/
    9250             : /*                   OGRwkbExportOptionsSetVariant()                    */
    9251             : /************************************************************************/
    9252             : 
    9253             : /**
    9254             :  * \brief Set the WKB variant
    9255             :  *
    9256             :  * @param psOptions WKB export options
    9257             :  * @param eWkbVariant variant: wkbVariantOldOgc, wkbVariantIso,
    9258             :  * wkbVariantPostGIS1
    9259             :  * @since GDAL 3.9
    9260             :  */
    9261             : 
    9262           1 : void OGRwkbExportOptionsSetVariant(OGRwkbExportOptions *psOptions,
    9263             :                                    OGRwkbVariant eWkbVariant)
    9264             : {
    9265           1 :     psOptions->eWkbVariant = eWkbVariant;
    9266           1 : }
    9267             : 
    9268             : /************************************************************************/
    9269             : /*                  OGRwkbExportOptionsSetPrecision()                   */
    9270             : /************************************************************************/
    9271             : 
    9272             : /**
    9273             :  * \brief Set precision options
    9274             :  *
    9275             :  * @param psOptions WKB export options
    9276             :  * @param hPrecisionOptions Precision options (might be null to reset them)
    9277             :  * @since GDAL 3.9
    9278             :  */
    9279             : 
    9280           1 : void OGRwkbExportOptionsSetPrecision(
    9281             :     OGRwkbExportOptions *psOptions,
    9282             :     OGRGeomCoordinatePrecisionH hPrecisionOptions)
    9283             : {
    9284           1 :     psOptions->sPrecision = OGRGeomCoordinateBinaryPrecision();
    9285           1 :     if (hPrecisionOptions)
    9286           1 :         psOptions->sPrecision.SetFrom(*hPrecisionOptions);
    9287           1 : }
    9288             : 
    9289             : /************************************************************************/
    9290             : /*                            IsRectangle()                             */
    9291             : /************************************************************************/
    9292             : 
    9293             : /**
    9294             :  * \brief Returns whether the geometry is a polygon with 4 corners forming
    9295             :  * a rectangle.
    9296             :  *
    9297             :  * @since GDAL 3.10
    9298             :  */
    9299       53044 : bool OGRGeometry::IsRectangle() const
    9300             : {
    9301       53044 :     if (wkbFlatten(getGeometryType()) != wkbPolygon)
    9302         352 :         return false;
    9303             : 
    9304       52692 :     const OGRPolygon *poPoly = toPolygon();
    9305             : 
    9306       52692 :     if (poPoly->getNumInteriorRings() != 0)
    9307          27 :         return false;
    9308             : 
    9309       52665 :     const OGRLinearRing *poRing = poPoly->getExteriorRing();
    9310       52665 :     if (!poRing)
    9311           4 :         return false;
    9312             : 
    9313       52661 :     if (poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4)
    9314         213 :         return false;
    9315             : 
    9316             :     // If the ring has 5 points, the last should be the first.
    9317      104839 :     if (poRing->getNumPoints() == 5 && (poRing->getX(0) != poRing->getX(4) ||
    9318       52391 :                                         poRing->getY(0) != poRing->getY(4)))
    9319           1 :         return false;
    9320             : 
    9321             :     // Polygon with first segment in "y" direction.
    9322      104170 :     if (poRing->getX(0) == poRing->getX(1) &&
    9323      103445 :         poRing->getY(1) == poRing->getY(2) &&
    9324      155892 :         poRing->getX(2) == poRing->getX(3) &&
    9325       51722 :         poRing->getY(3) == poRing->getY(0))
    9326       51722 :         return true;
    9327             : 
    9328             :     // Polygon with first segment in "x" direction.
    9329        1369 :     if (poRing->getY(0) == poRing->getY(1) &&
    9330        1288 :         poRing->getX(1) == poRing->getX(2) &&
    9331        2013 :         poRing->getY(2) == poRing->getY(3) &&
    9332         644 :         poRing->getX(3) == poRing->getX(0))
    9333         644 :         return true;
    9334             : 
    9335          81 :     return false;
    9336             : }
    9337             : 
    9338             : /************************************************************************/
    9339             : /*                           hasEmptyParts()                            */
    9340             : /************************************************************************/
    9341             : 
    9342             : /**
    9343             :  * \brief Returns whether a geometry has empty parts/rings.
    9344             :  *
    9345             :  * Returns true if removeEmptyParts() will modify the geometry.
    9346             :  *
    9347             :  * This is different from IsEmpty().
    9348             :  *
    9349             :  * @since GDAL 3.10
    9350             :  */
    9351         103 : bool OGRGeometry::hasEmptyParts() const
    9352             : {
    9353         103 :     return false;
    9354             : }
    9355             : 
    9356             : /************************************************************************/
    9357             : /*                          removeEmptyParts()                          */
    9358             : /************************************************************************/
    9359             : 
    9360             : /**
    9361             :  * \brief Remove empty parts/rings from this geometry.
    9362             :  *
    9363             :  * @since GDAL 3.10
    9364             :  */
    9365          17 : void OGRGeometry::removeEmptyParts()
    9366             : {
    9367          17 : }
    9368             : 
    9369             : /************************************************************************/
    9370             : /*                        ~IOGRGeometryVisitor()                        */
    9371             : /************************************************************************/
    9372             : 
    9373             : IOGRGeometryVisitor::~IOGRGeometryVisitor() = default;
    9374             : 
    9375             : /************************************************************************/
    9376             : /*                     ~IOGRConstGeometryVisitor()                      */
    9377             : /************************************************************************/
    9378             : 
    9379             : IOGRConstGeometryVisitor::~IOGRConstGeometryVisitor() = default;

Generated by: LCOV version 1.14