LCOV - code coverage report
Current view: top level - ogr - ogrgeometry.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1645 1940 84.8 %
Date: 2024-05-03 15:49:35 Functions: 205 228 89.9 %

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

Generated by: LCOV version 1.14