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

Generated by: LCOV version 1.14