LCOV - code coverage report
Current view: top level - ogr - ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1859 2182 85.2 %
Date: 2026-02-23 15:56:29 Functions: 220 244 90.2 %

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

Generated by: LCOV version 1.14