LCOV - code coverage report
Current view: top level - ogr - ogrgeometrycollection.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 508 556 91.4 %
Date: 2025-10-21 22:35:35 Functions: 52 52 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  The OGRGeometryCollection class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_geometry.h"
      16             : 
      17             : #include <cstddef>
      18             : #include <cstring>
      19             : #include <limits>
      20             : #include <new>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_error.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi.h"
      26             : #include "ogr_api.h"
      27             : #include "ogr_core.h"
      28             : #include "ogr_p.h"
      29             : #include "ogr_spatialref.h"
      30             : 
      31             : /************************************************************************/
      32             : /*         OGRGeometryCollection( const OGRGeometryCollection& )        */
      33             : /************************************************************************/
      34             : 
      35             : /**
      36             :  * \brief Copy constructor.
      37             :  */
      38             : 
      39       34648 : OGRGeometryCollection::OGRGeometryCollection(const OGRGeometryCollection &other)
      40       34648 :     : OGRGeometry(other)
      41             : {
      42             :     // Do not use addGeometry() as it is virtual.
      43       34648 :     papoGeoms = static_cast<OGRGeometry **>(
      44       34648 :         VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), other.nGeomCount));
      45       34648 :     if (papoGeoms)
      46             :     {
      47       34648 :         nGeomCount = other.nGeomCount;
      48      137218 :         for (int i = 0; i < other.nGeomCount; i++)
      49             :         {
      50      102570 :             papoGeoms[i] = other.papoGeoms[i]->clone();
      51             :         }
      52             :     }
      53       34648 : }
      54             : 
      55             : /************************************************************************/
      56             : /*            OGRGeometryCollection( OGRGeometryCollection&& )          */
      57             : /************************************************************************/
      58             : 
      59             : /**
      60             :  * \brief Move constructor.
      61             :  *
      62             :  * @since GDAL 3.11
      63             :  */
      64             : 
      65             : // cppcheck-suppress-begin accessMoved
      66           8 : OGRGeometryCollection::OGRGeometryCollection(OGRGeometryCollection &&other)
      67          16 :     : OGRGeometry(std::move(other)), nGeomCount(other.nGeomCount),
      68           8 :       papoGeoms(other.papoGeoms)
      69             : {
      70           8 :     other.nGeomCount = 0;
      71           8 :     other.papoGeoms = nullptr;
      72           8 : }
      73             : 
      74             : // cppcheck-suppress-end accessMoved
      75             : 
      76             : /************************************************************************/
      77             : /*                       ~OGRGeometryCollection()                       */
      78             : /************************************************************************/
      79             : 
      80      159571 : OGRGeometryCollection::~OGRGeometryCollection()
      81             : 
      82             : {
      83      153822 :     OGRGeometryCollection::empty();
      84      159574 : }
      85             : 
      86             : /************************************************************************/
      87             : /*               operator=( const OGRGeometryCollection&)               */
      88             : /************************************************************************/
      89             : 
      90             : /**
      91             :  * \brief Assignment operator.
      92             :  */
      93             : 
      94             : OGRGeometryCollection &
      95          30 : OGRGeometryCollection::operator=(const OGRGeometryCollection &other)
      96             : {
      97          30 :     if (this != &other)
      98             :     {
      99          29 :         OGRGeometry::operator=(other);
     100             : 
     101          51 :         for (const auto *poOtherSubGeom : other)
     102             :         {
     103          23 :             if (!isCompatibleSubType(poOtherSubGeom->getGeometryType()))
     104             :             {
     105           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     106             :                          "Illegal use of OGRGeometryCollection::operator=(): "
     107             :                          "trying to assign an incompatible sub-geometry");
     108           1 :                 return *this;
     109             :             }
     110             :         }
     111             : 
     112          28 :         papoGeoms = static_cast<OGRGeometry **>(
     113          28 :             VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), other.nGeomCount));
     114          28 :         if (papoGeoms)
     115             :         {
     116          28 :             nGeomCount = other.nGeomCount;
     117          50 :             for (int i = 0; i < other.nGeomCount; i++)
     118             :             {
     119          22 :                 papoGeoms[i] = other.papoGeoms[i]->clone();
     120             :             }
     121             :         }
     122             :     }
     123          29 :     return *this;
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                  operator=( OGRGeometryCollection&&)                 */
     128             : /************************************************************************/
     129             : 
     130             : /**
     131             :  * \brief Move assignment operator.
     132             :  *
     133             :  * @since GDAL 3.11
     134             :  */
     135             : 
     136             : OGRGeometryCollection &
     137          16 : OGRGeometryCollection::operator=(OGRGeometryCollection &&other)
     138             : {
     139          16 :     if (this != &other)
     140             :     {
     141          16 :         empty();
     142             : 
     143          16 :         OGRGeometry::operator=(std::move(other));
     144          16 :         std::swap(nGeomCount, other.nGeomCount);
     145          16 :         std::swap(papoGeoms, other.papoGeoms);
     146             :     }
     147          16 :     return *this;
     148             : }
     149             : 
     150             : /************************************************************************/
     151             : /*                               empty()                                */
     152             : /************************************************************************/
     153             : 
     154      165756 : void OGRGeometryCollection::empty()
     155             : 
     156             : {
     157      165756 :     if (papoGeoms != nullptr)
     158             :     {
     159     1799470 :         for (auto &poSubGeom : *this)
     160             :         {
     161     1652830 :             delete poSubGeom;
     162             :         }
     163      146631 :         CPLFree(papoGeoms);
     164             :     }
     165             : 
     166      165756 :     nGeomCount = 0;
     167      165756 :     papoGeoms = nullptr;
     168      165756 : }
     169             : 
     170             : /************************************************************************/
     171             : /*                               clone()                                */
     172             : /************************************************************************/
     173             : 
     174         545 : OGRGeometryCollection *OGRGeometryCollection::clone() const
     175             : 
     176             : {
     177         545 :     auto ret = new (std::nothrow) OGRGeometryCollection(*this);
     178         545 :     if (ret)
     179             :     {
     180         545 :         if (ret->WkbSize() != WkbSize())
     181             :         {
     182           0 :             delete ret;
     183           0 :             ret = nullptr;
     184             :         }
     185             :     }
     186         545 :     return ret;
     187             : }
     188             : 
     189             : /************************************************************************/
     190             : /*                          getGeometryType()                           */
     191             : /************************************************************************/
     192             : 
     193       10071 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
     194             : 
     195             : {
     196       10071 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     197        2967 :         return wkbGeometryCollectionZM;
     198        7104 :     else if (flags & OGR_G_MEASURED)
     199         247 :         return wkbGeometryCollectionM;
     200        6857 :     else if (flags & OGR_G_3D)
     201        3844 :         return wkbGeometryCollection25D;
     202             :     else
     203        3013 :         return wkbGeometryCollection;
     204             : }
     205             : 
     206             : /************************************************************************/
     207             : /*                            getDimension()                            */
     208             : /************************************************************************/
     209             : 
     210           6 : int OGRGeometryCollection::getDimension() const
     211             : 
     212             : {
     213           6 :     int nDimension = 0;
     214             :     // FIXME? Not sure if it is really appropriate to take the max in case
     215             :     // of geometries of different dimension.
     216          12 :     for (const auto &poSubGeom : *this)
     217             :     {
     218           9 :         int nSubGeomDimension = poSubGeom->getDimension();
     219           9 :         if (nSubGeomDimension > nDimension)
     220             :         {
     221           5 :             nDimension = nSubGeomDimension;
     222           5 :             if (nDimension == 2)
     223           3 :                 break;
     224             :         }
     225             :     }
     226           6 :     return nDimension;
     227             : }
     228             : 
     229             : /************************************************************************/
     230             : /*                            flattenTo2D()                             */
     231             : /************************************************************************/
     232             : 
     233         101 : void OGRGeometryCollection::flattenTo2D()
     234             : 
     235             : {
     236         301 :     for (auto &poSubGeom : *this)
     237             :     {
     238         200 :         poSubGeom->flattenTo2D();
     239             :     }
     240             : 
     241         101 :     flags &= ~OGR_G_3D;
     242         101 :     flags &= ~OGR_G_MEASURED;
     243         101 : }
     244             : 
     245             : /************************************************************************/
     246             : /*                          getGeometryName()                           */
     247             : /************************************************************************/
     248             : 
     249        1048 : const char *OGRGeometryCollection::getGeometryName() const
     250             : 
     251             : {
     252        1048 :     return "GEOMETRYCOLLECTION";
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                          getNumGeometries()                          */
     257             : /************************************************************************/
     258             : 
     259             : /**
     260             :  * \brief Fetch number of geometries in container.
     261             :  *
     262             :  * This method relates to the SFCOM IGeometryCollect::get_NumGeometries()
     263             :  * method.
     264             :  *
     265             :  * @return count of children geometries.  May be zero.
     266             :  */
     267             : 
     268      122735 : int OGRGeometryCollection::getNumGeometries() const
     269             : 
     270             : {
     271      122735 :     return nGeomCount;
     272             : }
     273             : 
     274             : /************************************************************************/
     275             : /*                           getGeometryRef()                           */
     276             : /************************************************************************/
     277             : 
     278             : /**
     279             :  * \brief Fetch geometry from container.
     280             :  *
     281             :  * This method returns a pointer to a geometry within the container.  The
     282             :  * returned geometry remains owned by the container, and should not be
     283             :  * modified.  The pointer is only valid until the next change to the
     284             :  * geometry container.  Use IGeometry::clone() to make a copy.
     285             :  *
     286             :  * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
     287             :  *
     288             :  * @param i the index of the geometry to fetch, between 0 and
     289             :  *          getNumGeometries() - 1.
     290             :  * @return pointer to requested geometry.
     291             :  */
     292             : 
     293       34424 : OGRGeometry *OGRGeometryCollection::getGeometryRef(int i)
     294             : 
     295             : {
     296       34424 :     if (i < 0 || i >= nGeomCount)
     297           0 :         return nullptr;
     298             : 
     299       34424 :     return papoGeoms[i];
     300             : }
     301             : 
     302             : /**
     303             :  * \brief Fetch geometry from container.
     304             :  *
     305             :  * This method returns a pointer to a geometry within the container.  The
     306             :  * returned geometry remains owned by the container, and should not be
     307             :  * modified.  The pointer is only valid until the next change to the
     308             :  * geometry container.  Use IGeometry::clone() to make a copy.
     309             :  *
     310             :  * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
     311             :  *
     312             :  * @param i the index of the geometry to fetch, between 0 and
     313             :  *          getNumGeometries() - 1.
     314             :  * @return pointer to requested geometry.
     315             :  */
     316             : 
     317      175718 : const OGRGeometry *OGRGeometryCollection::getGeometryRef(int i) const
     318             : 
     319             : {
     320      175718 :     if (i < 0 || i >= nGeomCount)
     321           0 :         return nullptr;
     322             : 
     323      175718 :     return papoGeoms[i];
     324             : }
     325             : 
     326             : /************************************************************************/
     327             : /*                            addGeometry()                             */
     328             : /*                                                                      */
     329             : /*      Add a new geometry to a collection.  Subclasses should          */
     330             : /*      override this to verify the type of the new geometry, and       */
     331             : /*      then call this method to actually add it.                       */
     332             : /************************************************************************/
     333             : 
     334             : /**
     335             :  * \brief Add a geometry to the container.
     336             :  *
     337             :  * Some subclasses of OGRGeometryCollection restrict the types of geometry
     338             :  * that can be added, and may return an error.  The passed geometry is cloned
     339             :  * to make an internal copy.
     340             :  *
     341             :  * There is no SFCOM analog to this method.
     342             :  *
     343             :  * This method is the same as the C function OGR_G_AddGeometry().
     344             :  *
     345             :  * @param poNewGeom geometry to add to the container.
     346             :  *
     347             :  * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
     348             :  * the geometry type is illegal for the type of geometry container.
     349             :  */
     350             : 
     351        1892 : OGRErr OGRGeometryCollection::addGeometry(const OGRGeometry *poNewGeom)
     352             : 
     353             : {
     354        1892 :     OGRGeometry *poClone = poNewGeom->clone();
     355        1892 :     if (poClone == nullptr)
     356           0 :         return OGRERR_FAILURE;
     357             : 
     358        1892 :     const OGRErr eErr = addGeometryDirectly(poClone);
     359        1892 :     if (eErr != OGRERR_NONE)
     360           2 :         delete poClone;
     361             : 
     362        1892 :     return eErr;
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                        addGeometryDirectly()                         */
     367             : /*                                                                      */
     368             : /*      Add a new geometry to a collection.  Subclasses should          */
     369             : /*      override this to verify the type of the new geometry, and       */
     370             : /*      then call this method to actually add it.                       */
     371             : /************************************************************************/
     372             : 
     373             : /**
     374             :  * \brief Add a geometry directly to the container.
     375             :  *
     376             :  * Some subclasses of OGRGeometryCollection restrict the types of geometry
     377             :  * that can be added, and may return an error.  Ownership of the passed
     378             :  * geometry is taken by the container rather than cloning as addGeometry()
     379             :  * does, but only if the method is successful. If the method fails, ownership
     380             :  * still belongs to the caller.
     381             :  *
     382             :  * This method is the same as the C function OGR_G_AddGeometryDirectly().
     383             :  *
     384             :  * There is no SFCOM analog to this method.
     385             :  *
     386             :  * @param poNewGeom geometry to add to the container.
     387             :  *
     388             :  * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
     389             :  * the geometry type is illegal for the type of geometry container.
     390             :  */
     391             : 
     392      186689 : OGRErr OGRGeometryCollection::addGeometryDirectly(OGRGeometry *poNewGeom)
     393             : 
     394             : {
     395      186689 :     if (!isCompatibleSubType(poNewGeom->getGeometryType()))
     396           6 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
     397             : 
     398             : #if SIZEOF_VOIDP < 8
     399             :     if (nGeomCount == std::numeric_limits<int>::max() /
     400             :                           static_cast<int>(sizeof(OGRGeometry *)))
     401             :     {
     402             :         CPLError(CE_Failure, CPLE_OutOfMemory, "Too many subgeometries");
     403             :         return OGRERR_FAILURE;
     404             :     }
     405             : #else
     406      186678 :     if (nGeomCount == std::numeric_limits<int>::max())
     407             :     {
     408           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too many subgeometries");
     409           0 :         return OGRERR_FAILURE;
     410             :     }
     411             : #endif
     412             : 
     413      186680 :     HomogenizeDimensionalityWith(poNewGeom);
     414             : 
     415             :     OGRGeometry **papoNewGeoms =
     416      186678 :         static_cast<OGRGeometry **>(VSI_REALLOC_VERBOSE(
     417             :             papoGeoms, sizeof(OGRGeometry *) * (nGeomCount + 1)));
     418      186680 :     if (papoNewGeoms == nullptr)
     419           0 :         return OGRERR_FAILURE;
     420             : 
     421      186680 :     papoGeoms = papoNewGeoms;
     422      186680 :     papoGeoms[nGeomCount] = poNewGeom;
     423             : 
     424      186680 :     nGeomCount++;
     425             : 
     426      186680 :     return OGRERR_NONE;
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                            addGeometry()                             */
     431             : /************************************************************************/
     432             : 
     433             : /**
     434             :  * \brief Add a geometry directly to the container.
     435             :  *
     436             :  * Some subclasses of OGRGeometryCollection restrict the types of geometry
     437             :  * that can be added, and may return an error.
     438             :  *
     439             :  * There is no SFCOM analog to this method.
     440             :  *
     441             :  * @param geom geometry to add to the container.
     442             :  *
     443             :  * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
     444             :  * the geometry type is illegal for the type of geometry container.
     445             :  */
     446             : 
     447        2354 : OGRErr OGRGeometryCollection::addGeometry(std::unique_ptr<OGRGeometry> geom)
     448             : {
     449        2354 :     OGRGeometry *poGeom = geom.release();
     450        2354 :     OGRErr eErr = addGeometryDirectly(poGeom);
     451        2354 :     if (eErr != OGRERR_NONE)
     452           2 :         delete poGeom;
     453        2354 :     return eErr;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                           removeGeometry()                           */
     458             : /************************************************************************/
     459             : 
     460             : /**
     461             :  * \brief Remove a geometry from the container.
     462             :  *
     463             :  * Removing a geometry will cause the geometry count to drop by one, and all
     464             :  * "higher" geometries will shuffle down one in index.
     465             :  *
     466             :  * There is no SFCOM analog to this method.
     467             :  *
     468             :  * This method is the same as the C function OGR_G_RemoveGeometry().
     469             :  *
     470             :  * @param iGeom the index of the geometry to delete.  A value of -1 is a
     471             :  * special flag meaning that all geometries should be removed.
     472             :  *
     473             :  * @param bDelete if TRUE the geometry will be deallocated, otherwise it will
     474             :  * not.  The default is TRUE as the container is considered to own the
     475             :  * geometries in it. Note: using stealGeometry() might be a better alternative
     476             :  * to using bDelete = false.
     477             :  *
     478             :  * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
     479             :  * out of range.
     480             :  */
     481             : 
     482        5405 : OGRErr OGRGeometryCollection::removeGeometry(int iGeom, int bDelete)
     483             : 
     484             : {
     485        5405 :     if (iGeom < -1 || iGeom >= nGeomCount)
     486           4 :         return OGRERR_FAILURE;
     487             : 
     488             :     // Special case.
     489        5401 :     if (iGeom == -1)
     490             :     {
     491           7 :         while (nGeomCount > 0)
     492           5 :             removeGeometry(nGeomCount - 1, bDelete);
     493           2 :         return OGRERR_NONE;
     494             :     }
     495             : 
     496        5399 :     if (bDelete)
     497         103 :         delete papoGeoms[iGeom];
     498             : 
     499        5399 :     memmove(papoGeoms + iGeom, papoGeoms + iGeom + 1,
     500        5399 :             sizeof(OGRGeometry *) * (nGeomCount - iGeom - 1));
     501             : 
     502        5399 :     nGeomCount--;
     503             : 
     504        5399 :     return OGRERR_NONE;
     505             : }
     506             : 
     507             : /************************************************************************/
     508             : /*                           stealGeometry()                            */
     509             : /************************************************************************/
     510             : 
     511             : /**
     512             :  * \brief Remove a geometry from the container and return it to the caller
     513             :  *
     514             :  * Removing a geometry will cause the geometry count to drop by one, and all
     515             :  * "higher" geometries will shuffle down one in index.
     516             :  *
     517             :  * There is no SFCOM analog to this method.
     518             :  *
     519             :  * @param iGeom the index of the geometry to delete.
     520             :  *
     521             :  * @return the sub-geometry, or nullptr in case of error.
     522             :  * @since 3.10
     523             :  */
     524             : 
     525          13 : std::unique_ptr<OGRGeometry> OGRGeometryCollection::stealGeometry(int iGeom)
     526             : {
     527          13 :     if (iGeom < 0 || iGeom >= nGeomCount)
     528           0 :         return nullptr;
     529             : 
     530          26 :     auto poSubGeom = std::unique_ptr<OGRGeometry>(papoGeoms[iGeom]);
     531          13 :     papoGeoms[iGeom] = nullptr;
     532          13 :     removeGeometry(iGeom);
     533          13 :     return poSubGeom;
     534             : }
     535             : 
     536             : /************************************************************************/
     537             : /*                           hasEmptyParts()                            */
     538             : /************************************************************************/
     539             : 
     540           9 : bool OGRGeometryCollection::hasEmptyParts() const
     541             : {
     542          17 :     for (const auto &poSubGeom : *this)
     543             :     {
     544          11 :         if (poSubGeom->IsEmpty() || poSubGeom->hasEmptyParts())
     545           3 :             return true;
     546             :     }
     547           6 :     return false;
     548             : }
     549             : 
     550             : /************************************************************************/
     551             : /*                          removeEmptyParts()                          */
     552             : /************************************************************************/
     553             : 
     554           5 : void OGRGeometryCollection::removeEmptyParts()
     555             : {
     556          13 :     for (int i = nGeomCount - 1; i >= 0; --i)
     557             :     {
     558           8 :         papoGeoms[i]->removeEmptyParts();
     559           8 :         if (papoGeoms[i]->IsEmpty())
     560           3 :             removeGeometry(i, true);
     561             :     }
     562           5 : }
     563             : 
     564             : /************************************************************************/
     565             : /*                              WkbSize()                               */
     566             : /*                                                                      */
     567             : /*      Return the size of this object in well known binary             */
     568             : /*      representation including the byte order, and type information.  */
     569             : /************************************************************************/
     570             : 
     571       79688 : size_t OGRGeometryCollection::WkbSize() const
     572             : 
     573             : {
     574       79688 :     size_t nSize = 9;
     575             : 
     576      349251 :     for (const auto &poGeom : *this)
     577             :     {
     578      269569 :         nSize += poGeom->WkbSize();
     579             :     }
     580             : 
     581       79680 :     return nSize;
     582             : }
     583             : 
     584             : /************************************************************************/
     585             : /*                       importFromWkbInternal()                        */
     586             : /************************************************************************/
     587             : 
     588             : //! @cond Doxygen_Suppress
     589        8784 : OGRErr OGRGeometryCollection::importFromWkbInternal(
     590             :     const unsigned char *pabyData, size_t nSize, int nRecLevel,
     591             :     OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut)
     592             : 
     593             : {
     594        8784 :     nBytesConsumedOut = 0;
     595             :     // Arbitrary value, but certainly large enough for reasonable use cases.
     596        8784 :     if (nRecLevel == 32)
     597             :     {
     598           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     599             :                  "Too many recursion levels (%d) while parsing WKB geometry.",
     600             :                  nRecLevel);
     601           1 :         return OGRERR_CORRUPT_DATA;
     602             :     }
     603             : 
     604        8783 :     OGRwkbByteOrder eByteOrder = wkbXDR;
     605        8783 :     size_t nDataOffset = 0;
     606        8783 :     int nGeomCountNew = 0;
     607        8783 :     OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
     608             :                                                     nDataOffset, eByteOrder, 9,
     609             :                                                     nGeomCountNew, eWkbVariant);
     610             : 
     611        8783 :     if (eErr != OGRERR_NONE)
     612         324 :         return eErr;
     613             : 
     614        8459 :     CPLAssert(nGeomCount == 0);
     615        8459 :     nGeomCount = nGeomCountNew;
     616             : 
     617             :     // coverity[tainted_data]
     618        8458 :     papoGeoms = static_cast<OGRGeometry **>(
     619        8459 :         VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), nGeomCount));
     620        8458 :     if (nGeomCount != 0 && papoGeoms == nullptr)
     621             :     {
     622           0 :         nGeomCount = 0;
     623           0 :         return OGRERR_NOT_ENOUGH_MEMORY;
     624             :     }
     625             : 
     626             :     /* -------------------------------------------------------------------- */
     627             :     /*      Get the Geoms.                                                  */
     628             :     /* -------------------------------------------------------------------- */
     629       19559 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
     630             :     {
     631             :         // Parses sub-geometry.
     632       11991 :         const unsigned char *pabySubData = pabyData + nDataOffset;
     633       11991 :         if (nSize < 9 && nSize != static_cast<size_t>(-1))
     634         891 :             return OGRERR_NOT_ENOUGH_DATA;
     635             : 
     636       11979 :         OGRwkbGeometryType eSubGeomType = wkbUnknown;
     637       11979 :         eErr = OGRReadWKBGeometryType(pabySubData, eWkbVariant, &eSubGeomType);
     638       11980 :         if (eErr != OGRERR_NONE)
     639         372 :             return eErr;
     640             : 
     641       11608 :         if (!isCompatibleSubType(eSubGeomType))
     642             :         {
     643           1 :             nGeomCount = iGeom;
     644           1 :             CPLDebug(
     645             :                 "OGR",
     646             :                 "Cannot add geometry of type (%d) to geometry of type (%d)",
     647           1 :                 eSubGeomType, getGeometryType());
     648           1 :             return OGRERR_CORRUPT_DATA;
     649             :         }
     650             : 
     651       11607 :         OGRGeometry *poSubGeom = nullptr;
     652       11607 :         size_t nSubGeomBytesConsumed = 0;
     653       11607 :         if (OGR_GT_IsSubClassOf(eSubGeomType, wkbGeometryCollection))
     654             :         {
     655         161 :             poSubGeom = OGRGeometryFactory::createGeometry(eSubGeomType);
     656         161 :             if (poSubGeom == nullptr)
     657           0 :                 eErr = OGRERR_FAILURE;
     658             :             else
     659         161 :                 eErr = poSubGeom->toGeometryCollection()->importFromWkbInternal(
     660             :                     pabySubData, nSize, nRecLevel + 1, eWkbVariant,
     661             :                     nSubGeomBytesConsumed);
     662             :         }
     663             :         else
     664             :         {
     665       11446 :             eErr = OGRGeometryFactory::createFromWkb(
     666             :                 pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant,
     667             :                 nSubGeomBytesConsumed);
     668             : 
     669       11446 :             if (eErr == OGRERR_NONE)
     670             :             {
     671             :                 // if this is a Z or M geom make sure the sub geoms are as well
     672       10972 :                 if (Is3D() && !poSubGeom->Is3D())
     673             :                 {
     674           0 :                     CPLDebug("OGR", "Promoting sub-geometry to 3D");
     675           0 :                     poSubGeom->set3D(TRUE);
     676             :                 }
     677             : 
     678       10972 :                 if (IsMeasured() && !poSubGeom->IsMeasured())
     679             :                 {
     680           0 :                     CPLDebug("OGR", "Promoting sub-geometry to Measured");
     681           0 :                     poSubGeom->setMeasured(TRUE);
     682             :                 }
     683             :             }
     684             :         }
     685             : 
     686       11607 :         if (eErr != OGRERR_NONE)
     687             :         {
     688         506 :             nGeomCount = iGeom;
     689         506 :             delete poSubGeom;
     690         506 :             return eErr;
     691             :         }
     692             : 
     693       11101 :         papoGeoms[iGeom] = poSubGeom;
     694             : 
     695       11101 :         if (papoGeoms[iGeom]->Is3D())
     696        5778 :             flags |= OGR_G_3D;
     697       11101 :         if (papoGeoms[iGeom]->IsMeasured())
     698        5678 :             flags |= OGR_G_MEASURED;
     699             : 
     700       11101 :         CPLAssert(nSubGeomBytesConsumed > 0);
     701       11101 :         if (nSize != static_cast<size_t>(-1))
     702             :         {
     703       11096 :             CPLAssert(nSize >= nSubGeomBytesConsumed);
     704       11096 :             nSize -= nSubGeomBytesConsumed;
     705             :         }
     706             : 
     707       11101 :         nDataOffset += nSubGeomBytesConsumed;
     708             :     }
     709        7568 :     nBytesConsumedOut = nDataOffset;
     710             : 
     711        7568 :     return OGRERR_NONE;
     712             : }
     713             : 
     714             : //! @endcond
     715             : 
     716             : /************************************************************************/
     717             : /*                           importFromWkb()                            */
     718             : /*                                                                      */
     719             : /*      Initialize from serialized stream in well known binary          */
     720             : /*      format.                                                         */
     721             : /************************************************************************/
     722             : 
     723        4993 : OGRErr OGRGeometryCollection::importFromWkb(const unsigned char *pabyData,
     724             :                                             size_t nSize,
     725             :                                             OGRwkbVariant eWkbVariant,
     726             :                                             size_t &nBytesConsumedOut)
     727             : 
     728             : {
     729        4993 :     return importFromWkbInternal(pabyData, nSize, 0, eWkbVariant,
     730        4993 :                                  nBytesConsumedOut);
     731             : }
     732             : 
     733             : /************************************************************************/
     734             : /*                            exportToWkb()                             */
     735             : /*                                                                      */
     736             : /*      Build a well known binary representation of this object.        */
     737             : /************************************************************************/
     738             : 
     739             : OGRErr
     740       11308 : OGRGeometryCollection::exportToWkb(unsigned char *pabyData,
     741             :                                    const OGRwkbExportOptions *psOptions) const
     742             : 
     743             : {
     744       11308 :     if (psOptions == nullptr)
     745             :     {
     746             :         static const OGRwkbExportOptions defaultOptions;
     747           0 :         psOptions = &defaultOptions;
     748             :     }
     749             : 
     750       11308 :     OGRwkbExportOptions sOptions(*psOptions);
     751             : 
     752       11432 :     if (sOptions.eWkbVariant == wkbVariantOldOgc &&
     753         124 :         (wkbFlatten(getGeometryType()) == wkbMultiCurve ||
     754         114 :          wkbFlatten(getGeometryType()) == wkbMultiSurface))
     755             :     {
     756             :         // Does not make sense for new geometries, so patch it.
     757          19 :         sOptions.eWkbVariant = wkbVariantIso;
     758             :     }
     759             : 
     760             :     /* -------------------------------------------------------------------- */
     761             :     /*      Set the byte order.                                             */
     762             :     /* -------------------------------------------------------------------- */
     763       11308 :     pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
     764             :         static_cast<unsigned char>(sOptions.eByteOrder));
     765             : 
     766             :     /* -------------------------------------------------------------------- */
     767             :     /*      Set the geometry feature type, ensuring that 3D flag is         */
     768             :     /*      preserved.                                                      */
     769             :     /* -------------------------------------------------------------------- */
     770       11308 :     GUInt32 nGType = getGeometryType();
     771             : 
     772       11304 :     if (sOptions.eWkbVariant == wkbVariantIso)
     773       11196 :         nGType = getIsoGeometryType();
     774         108 :     else if (sOptions.eWkbVariant == wkbVariantPostGIS1)
     775             :     {
     776           4 :         const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType));
     777           4 :         nGType = wkbFlatten(nGType);
     778           4 :         if (nGType == wkbMultiCurve)
     779           0 :             nGType = POSTGIS15_MULTICURVE;
     780           4 :         else if (nGType == wkbMultiSurface)
     781           0 :             nGType = POSTGIS15_MULTISURFACE;
     782           4 :         if (bIs3D)
     783             :             // Yes, explicitly set wkb25DBit.
     784           1 :             nGType =
     785           1 :                 static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
     786             :     }
     787             : 
     788       11304 :     if (OGR_SWAP(sOptions.eByteOrder))
     789             :     {
     790           7 :         nGType = CPL_SWAP32(nGType);
     791             :     }
     792             : 
     793       11304 :     memcpy(pabyData + 1, &nGType, 4);
     794             : 
     795             :     /* -------------------------------------------------------------------- */
     796             :     /*      Copy in the raw data.                                           */
     797             :     /* -------------------------------------------------------------------- */
     798       11304 :     if (OGR_SWAP(sOptions.eByteOrder))
     799             :     {
     800           7 :         int nCount = CPL_SWAP32(nGeomCount);
     801           7 :         memcpy(pabyData + 5, &nCount, 4);
     802             :     }
     803             :     else
     804             :     {
     805       11297 :         memcpy(pabyData + 5, &nGeomCount, 4);
     806             :     }
     807             : 
     808       11304 :     size_t nOffset = 9;
     809             : 
     810             :     /* ==================================================================== */
     811             :     /*      Serialize each of the Geoms.                                    */
     812             :     /* ==================================================================== */
     813       11304 :     int iGeom = 0;
     814       78464 :     for (auto &&poSubGeom : *this)
     815             :     {
     816       67161 :         poSubGeom->exportToWkb(pabyData + nOffset, &sOptions);
     817             :         // Should normally not happen if everyone else does its job,
     818             :         // but has happened sometimes. (#6332)
     819       67161 :         if (poSubGeom->getCoordinateDimension() != getCoordinateDimension())
     820             :         {
     821           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     822             :                      "Sub-geometry %d has coordinate dimension %d, "
     823             :                      "but container has %d",
     824           0 :                      iGeom, poSubGeom->getCoordinateDimension(),
     825           0 :                      getCoordinateDimension());
     826             :         }
     827             : 
     828       67163 :         nOffset += poSubGeom->WkbSize();
     829       67160 :         iGeom++;
     830             :     }
     831             : 
     832       11303 :     return OGRERR_NONE;
     833             : }
     834             : 
     835             : /************************************************************************/
     836             : /*                       importFromWktInternal()                        */
     837             : /************************************************************************/
     838             : 
     839         632 : OGRErr OGRGeometryCollection::importFromWktInternal(const char **ppszInput,
     840             :                                                     int nRecLevel)
     841             : 
     842             : {
     843             :     // Arbitrary value, but certainly large enough for reasonable usages.
     844         632 :     if (nRecLevel == 32)
     845             :     {
     846           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     847             :                  "Too many recursion levels (%d) while parsing WKT geometry.",
     848             :                  nRecLevel);
     849           1 :         return OGRERR_CORRUPT_DATA;
     850             :     }
     851             : 
     852         631 :     int bHasZ = FALSE;
     853         631 :     int bHasM = FALSE;
     854         631 :     bool bIsEmpty = false;
     855         631 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
     856         631 :     if (eErr != OGRERR_NONE)
     857           4 :         return eErr;
     858         627 :     if (bHasZ)
     859          96 :         flags |= OGR_G_3D;
     860         627 :     if (bHasM)
     861          61 :         flags |= OGR_G_MEASURED;
     862         627 :     if (bIsEmpty)
     863          43 :         return OGRERR_NONE;
     864             : 
     865         584 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
     866         584 :     const char *pszInput = *ppszInput;
     867             : 
     868             :     // Skip first '('.
     869         584 :     pszInput = OGRWktReadToken(pszInput, szToken);
     870             : 
     871             :     /* ==================================================================== */
     872             :     /*      Read each subgeometry in turn.                                  */
     873             :     /* ==================================================================== */
     874         644 :     do
     875             :     {
     876        1228 :         OGRGeometry *poGeom = nullptr;
     877             : 
     878             :         /* --------------------------------------------------------------------
     879             :          */
     880             :         /*      Get the first token, which should be the geometry type. */
     881             :         /* --------------------------------------------------------------------
     882             :          */
     883        1228 :         OGRWktReadToken(pszInput, szToken);
     884             : 
     885             :         /* --------------------------------------------------------------------
     886             :          */
     887             :         /*      Do the import. */
     888             :         /* --------------------------------------------------------------------
     889             :          */
     890        1228 :         if (STARTS_WITH_CI(szToken, "GEOMETRYCOLLECTION"))
     891             :         {
     892         110 :             OGRGeometryCollection *poGC = new OGRGeometryCollection();
     893         110 :             poGeom = poGC;
     894         110 :             eErr = poGC->importFromWktInternal(&pszInput, nRecLevel + 1);
     895             :         }
     896             :         else
     897             :             eErr =
     898        1118 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
     899             : 
     900        1228 :         if (eErr == OGRERR_NONE)
     901             :         {
     902             :             // If this has M, but not Z, it is an error if poGeom does
     903             :             // not have M.
     904        1184 :             if (!Is3D() && IsMeasured() && !poGeom->IsMeasured())
     905           0 :                 eErr = OGRERR_CORRUPT_DATA;
     906             :             else
     907        1184 :                 eErr = addGeometryDirectly(poGeom);
     908             :         }
     909        1228 :         if (eErr != OGRERR_NONE)
     910             :         {
     911          44 :             delete poGeom;
     912          44 :             return eErr;
     913             :         }
     914             : 
     915             :         /* --------------------------------------------------------------------
     916             :          */
     917             :         /*      Read the delimiter following the ring. */
     918             :         /* --------------------------------------------------------------------
     919             :          */
     920             : 
     921        1184 :         pszInput = OGRWktReadToken(pszInput, szToken);
     922        1184 :     } while (szToken[0] == ',');
     923             : 
     924             :     /* -------------------------------------------------------------------- */
     925             :     /*      freak if we don't get a closing bracket.                        */
     926             :     /* -------------------------------------------------------------------- */
     927         540 :     if (szToken[0] != ')')
     928           2 :         return OGRERR_CORRUPT_DATA;
     929             : 
     930         538 :     *ppszInput = pszInput;
     931             : 
     932         538 :     return OGRERR_NONE;
     933             : }
     934             : 
     935             : /************************************************************************/
     936             : /*                           importFromWkt()                            */
     937             : /************************************************************************/
     938             : 
     939         522 : OGRErr OGRGeometryCollection::importFromWkt(const char **ppszInput)
     940             : 
     941             : {
     942         522 :     return importFromWktInternal(ppszInput, 0);
     943             : }
     944             : 
     945             : /************************************************************************/
     946             : /*                            exportToWkt()                             */
     947             : /*                                                                      */
     948             : /*      Translate this structure into its well known text format        */
     949             : /*      equivalent.                                                     */
     950             : /************************************************************************/
     951             : 
     952         299 : std::string OGRGeometryCollection::exportToWkt(const OGRWktOptions &opts,
     953             :                                                OGRErr *err) const
     954             : {
     955         299 :     return exportToWktInternal(opts, err);
     956             : }
     957             : 
     958             : //! @cond Doxygen_Suppress
     959        1246 : std::string OGRGeometryCollection::exportToWktInternal(
     960             :     const OGRWktOptions &opts, OGRErr *err, const std::string &exclude) const
     961             : {
     962        1246 :     bool first = true;
     963        1246 :     const size_t excludeSize = exclude.size();
     964        2492 :     std::string wkt(getGeometryName());
     965        1246 :     wkt += wktTypeString(opts.variant);
     966             : 
     967             :     try
     968             :     {
     969        2951 :         for (const auto &poSubGeom : *this)
     970             :         {
     971        1705 :             OGRErr subgeomErr = OGRERR_NONE;
     972        1705 :             std::string tempWkt = poSubGeom->exportToWkt(opts, &subgeomErr);
     973        1705 :             if (subgeomErr != OGRERR_NONE)
     974             :             {
     975           0 :                 if (err)
     976           0 :                     *err = subgeomErr;
     977           0 :                 return std::string();
     978             :             }
     979             : 
     980             :             // For some strange reason we exclude the typename leader when using
     981             :             // some geometries as part of a collection.
     982        1705 :             if (excludeSize && (tempWkt.compare(0, excludeSize, exclude) == 0))
     983             :             {
     984        1128 :                 auto pos = tempWkt.find('(');
     985             :                 // We won't have an opening paren if the geom is empty.
     986        1128 :                 if (pos == std::string::npos)
     987          31 :                     continue;
     988        1097 :                 tempWkt = tempWkt.substr(pos);
     989             :             }
     990             : 
     991             :             // Also strange, we allow the inclusion of ISO-only geometries (see
     992             :             // OGRPolyhedralSurface) in a non-iso geometry collection.  In order
     993             :             // to facilitate this, we need to rip the ISO bit from the string.
     994        1674 :             if (opts.variant != wkbVariantIso)
     995             :             {
     996             :                 std::string::size_type pos;
     997         653 :                 if ((pos = tempWkt.find(" Z ")) != std::string::npos)
     998          11 :                     tempWkt.erase(pos + 1, 2);
     999         642 :                 else if ((pos = tempWkt.find(" M ")) != std::string::npos)
    1000           0 :                     tempWkt.erase(pos + 1, 2);
    1001         642 :                 else if ((pos = tempWkt.find(" ZM ")) != std::string::npos)
    1002           0 :                     tempWkt.erase(pos + 1, 3);
    1003             :             }
    1004             : 
    1005        1674 :             if (first)
    1006        1086 :                 wkt += '(';
    1007             :             else
    1008         588 :                 wkt += ',';
    1009        1674 :             first = false;
    1010        1674 :             wkt += tempWkt;
    1011             :         }
    1012             : 
    1013        1246 :         if (err)
    1014        1129 :             *err = OGRERR_NONE;
    1015        1246 :         if (first)
    1016         160 :             wkt += "EMPTY";
    1017             :         else
    1018        1086 :             wkt += ')';
    1019        1246 :         return wkt;
    1020             :     }
    1021           0 :     catch (const std::bad_alloc &e)
    1022             :     {
    1023           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1024           0 :         if (err)
    1025           0 :             *err = OGRERR_FAILURE;
    1026           0 :         return std::string();
    1027             :     }
    1028             : }
    1029             : 
    1030             : //! @endcond
    1031             : 
    1032             : /************************************************************************/
    1033             : /*                            getEnvelope()                             */
    1034             : /************************************************************************/
    1035             : 
    1036       15637 : void OGRGeometryCollection::getEnvelope(OGREnvelope *psEnvelope) const
    1037             : 
    1038             : {
    1039       15637 :     OGREnvelope3D oEnv3D;
    1040       15637 :     getEnvelope(&oEnv3D);
    1041       15637 :     psEnvelope->MinX = oEnv3D.MinX;
    1042       15637 :     psEnvelope->MinY = oEnv3D.MinY;
    1043       15637 :     psEnvelope->MaxX = oEnv3D.MaxX;
    1044       15637 :     psEnvelope->MaxY = oEnv3D.MaxY;
    1045       15637 : }
    1046             : 
    1047             : /************************************************************************/
    1048             : /*                            getEnvelope()                             */
    1049             : /************************************************************************/
    1050             : 
    1051       16441 : void OGRGeometryCollection::getEnvelope(OGREnvelope3D *psEnvelope) const
    1052             : 
    1053             : {
    1054       16441 :     OGREnvelope3D oGeomEnv;
    1055       16441 :     bool bExtentSet = false;
    1056             : 
    1057       16441 :     *psEnvelope = OGREnvelope3D();
    1058      401776 :     for (const auto &poSubGeom : *this)
    1059             :     {
    1060      385335 :         if (!poSubGeom->IsEmpty())
    1061             :         {
    1062      385329 :             bExtentSet = true;
    1063      385329 :             poSubGeom->getEnvelope(&oGeomEnv);
    1064      385329 :             psEnvelope->Merge(oGeomEnv);
    1065             :         }
    1066             :     }
    1067             : 
    1068       16441 :     if (!bExtentSet)
    1069             :     {
    1070             :         // To be backward compatible when called on empty geom
    1071          13 :         psEnvelope->MinX = 0.0;
    1072          13 :         psEnvelope->MinY = 0.0;
    1073          13 :         psEnvelope->MinZ = 0.0;
    1074          13 :         psEnvelope->MaxX = 0.0;
    1075          13 :         psEnvelope->MaxY = 0.0;
    1076          13 :         psEnvelope->MaxZ = 0.0;
    1077             :     }
    1078       16441 : }
    1079             : 
    1080             : /************************************************************************/
    1081             : /*                               Equals()                               */
    1082             : /************************************************************************/
    1083             : 
    1084        3854 : OGRBoolean OGRGeometryCollection::Equals(const OGRGeometry *poOther) const
    1085             : 
    1086             : {
    1087        3854 :     if (poOther == this)
    1088           2 :         return TRUE;
    1089             : 
    1090        3852 :     if (poOther->getGeometryType() != getGeometryType())
    1091           0 :         return FALSE;
    1092             : 
    1093        3852 :     if (IsEmpty() && poOther->IsEmpty())
    1094          12 :         return TRUE;
    1095             : 
    1096        3840 :     auto poOGC = poOther->toGeometryCollection();
    1097        3840 :     if (getNumGeometries() != poOGC->getNumGeometries())
    1098           2 :         return FALSE;
    1099             : 
    1100             :     // TODO(schwehr): Should test the SRS.
    1101             : 
    1102        8425 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
    1103             :     {
    1104        4588 :         if (!getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)))
    1105           1 :             return FALSE;
    1106             :     }
    1107             : 
    1108        3837 :     return TRUE;
    1109             : }
    1110             : 
    1111             : /************************************************************************/
    1112             : /*                             transform()                              */
    1113             : /************************************************************************/
    1114             : 
    1115         180 : OGRErr OGRGeometryCollection::transform(OGRCoordinateTransformation *poCT)
    1116             : 
    1117             : {
    1118         180 :     int iGeom = 0;
    1119         405 :     for (auto &poSubGeom : *this)
    1120             :     {
    1121         225 :         const OGRErr eErr = poSubGeom->transform(poCT);
    1122         225 :         if (eErr != OGRERR_NONE)
    1123             :         {
    1124           0 :             if (iGeom != 0)
    1125             :             {
    1126           0 :                 CPLDebug("OGR",
    1127             :                          "OGRGeometryCollection::transform() failed for a "
    1128             :                          "geometry other than the first, meaning some "
    1129             :                          "geometries are transformed and some are not.");
    1130             : 
    1131           0 :                 return OGRERR_FAILURE;
    1132             :             }
    1133             : 
    1134           0 :             return eErr;
    1135             :         }
    1136         225 :         iGeom++;
    1137             :     }
    1138             : 
    1139         180 :     assignSpatialReference(poCT->GetTargetCS());
    1140             : 
    1141         180 :     return OGRERR_NONE;
    1142             : }
    1143             : 
    1144             : /************************************************************************/
    1145             : /*                             closeRings()                             */
    1146             : /************************************************************************/
    1147             : 
    1148         129 : void OGRGeometryCollection::closeRings()
    1149             : 
    1150             : {
    1151         412 :     for (auto &poSubGeom : *this)
    1152             :     {
    1153         283 :         if (OGR_GT_IsSubClassOf(wkbFlatten(poSubGeom->getGeometryType()),
    1154         283 :                                 wkbCurvePolygon))
    1155             :         {
    1156          83 :             OGRCurvePolygon *poPoly = poSubGeom->toCurvePolygon();
    1157          83 :             poPoly->closeRings();
    1158             :         }
    1159             :     }
    1160         129 : }
    1161             : 
    1162             : /************************************************************************/
    1163             : /*                       setCoordinateDimension()                       */
    1164             : /************************************************************************/
    1165             : 
    1166         479 : bool OGRGeometryCollection::setCoordinateDimension(int nNewDimension)
    1167             : 
    1168             : {
    1169        1409 :     for (auto &poSubGeom : *this)
    1170             :     {
    1171         930 :         if (!poSubGeom->setCoordinateDimension(nNewDimension))
    1172           0 :             return false;
    1173             :     }
    1174             : 
    1175         479 :     return OGRGeometry::setCoordinateDimension(nNewDimension);
    1176             : }
    1177             : 
    1178       88577 : bool OGRGeometryCollection::set3D(OGRBoolean bIs3D)
    1179             : {
    1180      195356 :     for (auto &poSubGeom : *this)
    1181             :     {
    1182      106779 :         if (!poSubGeom->set3D(bIs3D))
    1183           0 :             return false;
    1184             :     }
    1185             : 
    1186       88577 :     return OGRGeometry::set3D(bIs3D);
    1187             : }
    1188             : 
    1189       46187 : bool OGRGeometryCollection::setMeasured(OGRBoolean bIsMeasured)
    1190             : {
    1191      153517 :     for (auto &poSubGeom : *this)
    1192             :     {
    1193      107330 :         if (!poSubGeom->setMeasured(bIsMeasured))
    1194           0 :             return false;
    1195             :     }
    1196             : 
    1197       46187 :     return OGRGeometry::setMeasured(bIsMeasured);
    1198             : }
    1199             : 
    1200             : /************************************************************************/
    1201             : /*                              get_Length()                            */
    1202             : /************************************************************************/
    1203             : 
    1204             : /**
    1205             :  * \brief Compute the length of a multicurve.
    1206             :  *
    1207             :  * The length is computed as the sum of the length of all members
    1208             :  * in this collection.
    1209             :  *
    1210             :  * @note No warning will be issued if a member of the collection does not
    1211             :  *       support the get_Length method.
    1212             :  *
    1213             :  * @return computed length.
    1214             :  */
    1215             : 
    1216           9 : double OGRGeometryCollection::get_Length() const
    1217             : {
    1218           9 :     double dfLength = 0.0;
    1219          26 :     for (const auto &poSubGeom : *this)
    1220             :     {
    1221             :         const OGRwkbGeometryType eType =
    1222          17 :             wkbFlatten(poSubGeom->getGeometryType());
    1223          17 :         if (OGR_GT_IsCurve(eType))
    1224             :         {
    1225          13 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1226          13 :             dfLength += poCurve->get_Length();
    1227             :         }
    1228           4 :         else if (OGR_GT_IsSurface(eType))
    1229             :         {
    1230           1 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1231           1 :             dfLength += poSurface->get_Length();
    1232             :         }
    1233           3 :         else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    1234             :         {
    1235             :             const OGRGeometryCollection *poColl =
    1236           2 :                 poSubGeom->toGeometryCollection();
    1237           2 :             dfLength += poColl->get_Length();
    1238             :         }
    1239             :     }
    1240             : 
    1241           9 :     return dfLength;
    1242             : }
    1243             : 
    1244             : /************************************************************************/
    1245             : /*                              get_Area()                              */
    1246             : /************************************************************************/
    1247             : 
    1248             : /**
    1249             :  * \brief Compute area of geometry collection.
    1250             :  *
    1251             :  * The area is computed as the sum of the areas of all members
    1252             :  * in this collection.
    1253             :  *
    1254             :  * @note No warning will be issued if a member of the collection does not
    1255             :  *       support the get_Area method.
    1256             :  *
    1257             :  * @return computed area.
    1258             :  */
    1259             : 
    1260          12 : double OGRGeometryCollection::get_Area() const
    1261             : {
    1262          12 :     double dfArea = 0.0;
    1263          33 :     for (const auto &poSubGeom : *this)
    1264             :     {
    1265          21 :         OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
    1266          21 :         if (OGR_GT_IsSurface(eType))
    1267             :         {
    1268          16 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1269          16 :             dfArea += poSurface->get_Area();
    1270             :         }
    1271           5 :         else if (OGR_GT_IsCurve(eType))
    1272             :         {
    1273           1 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1274           1 :             dfArea += poCurve->get_Area();
    1275             :         }
    1276           4 :         else if (OGR_GT_IsSubClassOf(eType, wkbMultiSurface) ||
    1277             :                  eType == wkbGeometryCollection)
    1278             :         {
    1279           2 :             dfArea += poSubGeom->toGeometryCollection()->get_Area();
    1280             :         }
    1281             :     }
    1282             : 
    1283          12 :     return dfArea;
    1284             : }
    1285             : 
    1286             : /************************************************************************/
    1287             : /*                        get_GeodesicArea()                            */
    1288             : /************************************************************************/
    1289             : 
    1290             : /**
    1291             :  * \brief Compute area of geometry collection, considered as a surface on
    1292             :  * the underlying ellipsoid of the SRS attached to the geometry.
    1293             :  *
    1294             :  * The returned area will always be in square meters, and assumes that
    1295             :  * polygon edges describe geodesic lines on the ellipsoid.
    1296             :  *
    1297             :  * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
    1298             :  * follow the shortest route on the surface of the ellipsoid.
    1299             :  *
    1300             :  * If the geometry' SRS is not a geographic one, geometries are reprojected to
    1301             :  * the underlying geographic SRS of the geometry' SRS.
    1302             :  * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
    1303             :  *
    1304             :  * The area is computed as the sum of the areas of all members
    1305             :  * in this collection.
    1306             :  *
    1307             :  * @note No warning will be issued if a member of the collection does not
    1308             :  *       support the get_GeodesicArea method.
    1309             :  *
    1310             :  * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
    1311             :  * @return the area of the geometry in square meters, or a negative value in case
    1312             :  * of error.
    1313             :  *
    1314             :  * @see get_Area() for an alternative method returning areas computed in
    1315             :  * 2D Cartesian space.
    1316             :  *
    1317             :  * @since GDAL 3.9
    1318             :  */
    1319           6 : double OGRGeometryCollection::get_GeodesicArea(
    1320             :     const OGRSpatialReference *poSRSOverride) const
    1321             : {
    1322           6 :     double dfArea = 0.0;
    1323          11 :     for (const auto &poSubGeom : *this)
    1324             :     {
    1325           8 :         OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
    1326           8 :         if (OGR_GT_IsSurface(eType))
    1327             :         {
    1328           4 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1329             :             const double dfLocalArea =
    1330           4 :                 poSurface->get_GeodesicArea(poSRSOverride);
    1331           4 :             if (dfLocalArea < 0)
    1332           1 :                 return dfLocalArea;
    1333           3 :             dfArea += dfLocalArea;
    1334             :         }
    1335           4 :         else if (OGR_GT_IsCurve(eType))
    1336             :         {
    1337           2 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1338           2 :             const double dfLocalArea = poCurve->get_GeodesicArea(poSRSOverride);
    1339           2 :             if (dfLocalArea < 0)
    1340           1 :                 return dfLocalArea;
    1341           1 :             dfArea += dfLocalArea;
    1342             :         }
    1343           2 :         else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    1344             :         {
    1345             :             const double dfLocalArea =
    1346           2 :                 poSubGeom->toGeometryCollection()->get_GeodesicArea(
    1347           2 :                     poSRSOverride);
    1348           2 :             if (dfLocalArea < 0)
    1349           1 :                 return dfLocalArea;
    1350           1 :             dfArea += dfLocalArea;
    1351             :         }
    1352             :     }
    1353             : 
    1354           3 :     return dfArea;
    1355             : }
    1356             : 
    1357             : /************************************************************************/
    1358             : /*                        get_GeodesicLength()                          */
    1359             : /************************************************************************/
    1360             : 
    1361             : /**
    1362             :  * \brief Get the length of the collection,where curve edges are geodesic lines
    1363             :  * on the underlying ellipsoid of the SRS attached to the geometry.
    1364             :  *
    1365             :  * The returned length will always be in meters.
    1366             :  *
    1367             :  * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
    1368             :  * follow the shortest route on the surface of the ellipsoid.
    1369             :  *
    1370             :  * If the geometry' SRS is not a geographic one, geometries are reprojected to
    1371             :  * the underlying geographic SRS of the geometry' SRS.
    1372             :  * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
    1373             :  *
    1374             :  * Note that geometries with circular arcs will be linearized in their original
    1375             :  * coordinate space first, so the resulting geodesic length will be an
    1376             :  * approximation.
    1377             :  *
    1378             :  * The length is computed as the sum of the lengths of all members
    1379             :  * in this collection.
    1380             :  *
    1381             :  * @note No warning will be issued if a member of the collection does not
    1382             :  *       support the get_GeodesicLength method.
    1383             :  *
    1384             :  * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
    1385             :  * @return the length of the geometry in meters, or a negative value in case
    1386             :  * of error.
    1387             :  *
    1388             :  * @see get_Length() for an alternative method returning areas computed in
    1389             :  * 2D Cartesian space.
    1390             :  *
    1391             :  * @since GDAL 3.10
    1392             :  */
    1393           5 : double OGRGeometryCollection::get_GeodesicLength(
    1394             :     const OGRSpatialReference *poSRSOverride) const
    1395             : {
    1396           5 :     double dfLength = 0.0;
    1397          10 :     for (const auto &poSubGeom : *this)
    1398             :     {
    1399             :         const OGRwkbGeometryType eType =
    1400           7 :             wkbFlatten(poSubGeom->getGeometryType());
    1401           7 :         if (OGR_GT_IsSurface(eType))
    1402             :         {
    1403           4 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1404             :             const double dfLocalLength =
    1405           4 :                 poSurface->get_GeodesicLength(poSRSOverride);
    1406           4 :             if (dfLocalLength < 0)
    1407           1 :                 return dfLocalLength;
    1408           3 :             dfLength += dfLocalLength;
    1409             :         }
    1410           3 :         else if (OGR_GT_IsCurve(eType))
    1411             :         {
    1412           1 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1413             :             const double dfLocalLength =
    1414           1 :                 poCurve->get_GeodesicLength(poSRSOverride);
    1415           1 :             if (dfLocalLength < 0)
    1416           0 :                 return dfLocalLength;
    1417           1 :             dfLength += dfLocalLength;
    1418             :         }
    1419           2 :         else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    1420             :         {
    1421             :             const double dfLocalLength =
    1422           2 :                 poSubGeom->toGeometryCollection()->get_GeodesicLength(
    1423           2 :                     poSRSOverride);
    1424           2 :             if (dfLocalLength < 0)
    1425           1 :                 return dfLocalLength;
    1426           1 :             dfLength += dfLocalLength;
    1427             :         }
    1428             :     }
    1429             : 
    1430           3 :     return dfLength;
    1431             : }
    1432             : 
    1433             : /************************************************************************/
    1434             : /*                               IsEmpty()                              */
    1435             : /************************************************************************/
    1436             : 
    1437       32466 : OGRBoolean OGRGeometryCollection::IsEmpty() const
    1438             : {
    1439       32605 :     for (const auto &poSubGeom : *this)
    1440             :     {
    1441       31303 :         if (poSubGeom->IsEmpty() == FALSE)
    1442       31164 :             return FALSE;
    1443             :     }
    1444        1302 :     return TRUE;
    1445             : }
    1446             : 
    1447             : /************************************************************************/
    1448             : /*                       assignSpatialReference()                       */
    1449             : /************************************************************************/
    1450             : 
    1451      112641 : void OGRGeometryCollection::assignSpatialReference(
    1452             :     const OGRSpatialReference *poSR)
    1453             : {
    1454      112641 :     OGRGeometry::assignSpatialReference(poSR);
    1455     1524110 :     for (auto &poSubGeom : *this)
    1456             :     {
    1457     1411460 :         poSubGeom->assignSpatialReference(poSR);
    1458             :     }
    1459      112641 : }
    1460             : 
    1461             : /************************************************************************/
    1462             : /*              OGRGeometryCollection::segmentize()                     */
    1463             : /************************************************************************/
    1464             : 
    1465          13 : bool OGRGeometryCollection::segmentize(double dfMaxLength)
    1466             : {
    1467          26 :     for (auto &poSubGeom : *this)
    1468             :     {
    1469          13 :         if (!poSubGeom->segmentize(dfMaxLength))
    1470           0 :             return false;
    1471             :     }
    1472          13 :     return true;
    1473             : }
    1474             : 
    1475             : /************************************************************************/
    1476             : /*                               swapXY()                               */
    1477             : /************************************************************************/
    1478             : 
    1479          30 : void OGRGeometryCollection::swapXY()
    1480             : {
    1481          76 :     for (auto &poSubGeom : *this)
    1482             :     {
    1483          46 :         poSubGeom->swapXY();
    1484             :     }
    1485          30 : }
    1486             : 
    1487             : /************************************************************************/
    1488             : /*                          isCompatibleSubType()                       */
    1489             : /************************************************************************/
    1490             : 
    1491             : /** Returns whether a geometry of the specified geometry type can be a
    1492             :  * member of this collection.
    1493             :  *
    1494             :  * @param eSubType type of the potential member
    1495             :  * @return TRUE or FALSE
    1496             :  */
    1497             : 
    1498        9253 : OGRBoolean OGRGeometryCollection::isCompatibleSubType(
    1499             :     CPL_UNUSED OGRwkbGeometryType eSubType) const
    1500             : {
    1501             :     // Accept all geometries as sub-geometries.
    1502        9253 :     return TRUE;
    1503             : }
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                         hasCurveGeometry()                           */
    1507             : /************************************************************************/
    1508             : 
    1509        2492 : OGRBoolean OGRGeometryCollection::hasCurveGeometry(int bLookForNonLinear) const
    1510             : {
    1511        6316 :     for (const auto &poSubGeom : *this)
    1512             :     {
    1513        3907 :         if (poSubGeom->hasCurveGeometry(bLookForNonLinear))
    1514          83 :             return TRUE;
    1515             :     }
    1516        2409 :     return FALSE;
    1517             : }
    1518             : 
    1519             : /************************************************************************/
    1520             : /*                         getLinearGeometry()                        */
    1521             : /************************************************************************/
    1522             : 
    1523             : OGRGeometry *
    1524         331 : OGRGeometryCollection::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
    1525             :                                          const char *const *papszOptions) const
    1526             : {
    1527             :     auto poGC = std::unique_ptr<OGRGeometryCollection>(
    1528         331 :         OGRGeometryFactory::createGeometry(OGR_GT_GetLinear(getGeometryType()))
    1529         662 :             ->toGeometryCollection());
    1530         331 :     if (!poGC)
    1531           0 :         return nullptr;
    1532         331 :     poGC->assignSpatialReference(getSpatialReference());
    1533         704 :     for (const auto &poSubGeom : *this)
    1534             :     {
    1535         746 :         OGRGeometry *poSubGeomNew = poSubGeom->getLinearGeometry(
    1536         373 :             dfMaxAngleStepSizeDegrees, papszOptions);
    1537         373 :         if (poGC->addGeometryDirectly(poSubGeomNew) != OGRERR_NONE)
    1538           0 :             return nullptr;
    1539             :     }
    1540         331 :     return poGC.release();
    1541             : }
    1542             : 
    1543             : /************************************************************************/
    1544             : /*                             getCurveGeometry()                       */
    1545             : /************************************************************************/
    1546             : 
    1547             : OGRGeometry *
    1548          15 : OGRGeometryCollection::getCurveGeometry(const char *const *papszOptions) const
    1549             : {
    1550             :     auto poGC = std::unique_ptr<OGRGeometryCollection>(
    1551          15 :         OGRGeometryFactory::createGeometry(OGR_GT_GetCurve(getGeometryType()))
    1552          30 :             ->toGeometryCollection());
    1553          15 :     if (!poGC)
    1554           0 :         return nullptr;
    1555          15 :     poGC->assignSpatialReference(getSpatialReference());
    1556          15 :     bool bHasCurveGeometry = false;
    1557         162 :     for (const auto &poSubGeom : *this)
    1558             :     {
    1559         147 :         OGRGeometry *poSubGeomNew = poSubGeom->getCurveGeometry(papszOptions);
    1560         147 :         if (poSubGeomNew->hasCurveGeometry())
    1561           7 :             bHasCurveGeometry = true;
    1562         147 :         if (poGC->addGeometryDirectly(poSubGeomNew) != OGRERR_NONE)
    1563           0 :             return nullptr;
    1564             :     }
    1565          15 :     if (!bHasCurveGeometry)
    1566             :     {
    1567          10 :         return clone();
    1568             :     }
    1569           5 :     return poGC.release();
    1570             : }
    1571             : 
    1572             : /************************************************************************/
    1573             : /*                      TransferMembersAndDestroy()                     */
    1574             : /************************************************************************/
    1575             : 
    1576             : //! @cond Doxygen_Suppress
    1577             : OGRGeometryCollection *
    1578        1049 : OGRGeometryCollection::TransferMembersAndDestroy(OGRGeometryCollection *poSrc,
    1579             :                                                  OGRGeometryCollection *poDst)
    1580             : {
    1581        1049 :     poDst->assignSpatialReference(poSrc->getSpatialReference());
    1582        1049 :     poDst->set3D(poSrc->Is3D());
    1583        1049 :     poDst->setMeasured(poSrc->IsMeasured());
    1584        1049 :     poDst->nGeomCount = poSrc->nGeomCount;
    1585        1049 :     poDst->papoGeoms = poSrc->papoGeoms;
    1586        1049 :     poSrc->nGeomCount = 0;
    1587        1049 :     poSrc->papoGeoms = nullptr;
    1588        1049 :     delete poSrc;
    1589        1049 :     return poDst;
    1590             : }
    1591             : 
    1592             : //! @endcond
    1593             : 
    1594             : /************************************************************************/
    1595             : /*                        CastToGeometryCollection()                    */
    1596             : /************************************************************************/
    1597             : 
    1598             : /**
    1599             :  * \brief Cast to geometry collection.
    1600             :  *
    1601             :  * This methods cast a derived class of geometry collection to a plain
    1602             :  * geometry collection.
    1603             :  *
    1604             :  * The passed in geometry is consumed and a new one returned (or NULL in case
    1605             :  * of failure).
    1606             :  *
    1607             :  * @param poSrc the input geometry - ownership is passed to the method.
    1608             :  * @return new geometry.
    1609             :  */
    1610             : 
    1611             : OGRGeometryCollection *
    1612         920 : OGRGeometryCollection::CastToGeometryCollection(OGRGeometryCollection *poSrc)
    1613             : {
    1614         920 :     if (wkbFlatten(poSrc->getGeometryType()) == wkbGeometryCollection)
    1615           0 :         return poSrc;
    1616         920 :     return TransferMembersAndDestroy(poSrc, new OGRGeometryCollection());
    1617             : }

Generated by: LCOV version 1.14