LCOV - code coverage report
Current view: top level - ogr - ogrgeometrycollection.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 522 573 91.1 %
Date: 2026-03-17 00:06:02 Functions: 53 53 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        5491 : OGRGeometryCollection::OGRGeometryCollection(const OGRGeometryCollection &other)
      40        5491 :     : OGRGeometry(other)
      41             : {
      42             :     // Do not use addGeometry() as it is virtual.
      43        5491 :     papoGeoms = static_cast<OGRGeometry **>(
      44        5491 :         VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), other.nGeomCount));
      45        5491 :     if (papoGeoms)
      46             :     {
      47        5491 :         nGeomCount = other.nGeomCount;
      48       14426 :         for (int i = 0; i < other.nGeomCount; i++)
      49             :         {
      50        8935 :             papoGeoms[i] = other.papoGeoms[i]->clone();
      51             :         }
      52             :     }
      53        5491 : }
      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      131466 : OGRGeometryCollection::~OGRGeometryCollection()
      81             : 
      82             : {
      83      125660 :     OGRGeometryCollection::empty();
      84      131466 : }
      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      137727 : void OGRGeometryCollection::empty()
     155             : 
     156             : {
     157      137727 :     if (papoGeoms != nullptr)
     158             :     {
     159     1679100 :         for (auto &poSubGeom : *this)
     160             :         {
     161     1560790 :             delete poSubGeom;
     162             :         }
     163      118302 :         CPLFree(papoGeoms);
     164             :     }
     165             : 
     166      137727 :     nGeomCount = 0;
     167      137727 :     papoGeoms = nullptr;
     168      137727 : }
     169             : 
     170             : /************************************************************************/
     171             : /*                               clone()                                */
     172             : /************************************************************************/
     173             : 
     174         555 : OGRGeometryCollection *OGRGeometryCollection::clone() const
     175             : 
     176             : {
     177         555 :     auto ret = new (std::nothrow) OGRGeometryCollection(*this);
     178         555 :     if (ret)
     179             :     {
     180         555 :         if (ret->WkbSize() != WkbSize())
     181             :         {
     182           0 :             delete ret;
     183           0 :             ret = nullptr;
     184             :         }
     185             :     }
     186         555 :     return ret;
     187             : }
     188             : 
     189             : /************************************************************************/
     190             : /*                          getGeometryType()                           */
     191             : /************************************************************************/
     192             : 
     193       10174 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
     194             : 
     195             : {
     196       10174 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     197        2969 :         return wkbGeometryCollectionZM;
     198        7205 :     else if (flags & OGR_G_MEASURED)
     199         248 :         return wkbGeometryCollectionM;
     200        6957 :     else if (flags & OGR_G_3D)
     201        3827 :         return wkbGeometryCollection25D;
     202             :     else
     203        3130 :         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        1064 : const char *OGRGeometryCollection::getGeometryName() const
     250             : 
     251             : {
     252        1064 :     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      123284 : int OGRGeometryCollection::getNumGeometries() const
     269             : 
     270             : {
     271      123284 :     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       31639 : OGRGeometry *OGRGeometryCollection::getGeometryRef(int i)
     294             : 
     295             : {
     296       31639 :     if (i < 0 || i >= nGeomCount)
     297           0 :         return nullptr;
     298             : 
     299       31639 :     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      178613 : const OGRGeometry *OGRGeometryCollection::getGeometryRef(int i) const
     318             : 
     319             : {
     320      178613 :     if (i < 0 || i >= nGeomCount)
     321           0 :         return nullptr;
     322             : 
     323      178613 :     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        2189 : OGRErr OGRGeometryCollection::addGeometry(const OGRGeometry *poNewGeom)
     352             : 
     353             : {
     354        2189 :     OGRGeometry *poClone = poNewGeom->clone();
     355        2189 :     if (poClone == nullptr)
     356           0 :         return OGRERR_FAILURE;
     357             : 
     358        2189 :     const OGRErr eErr = addGeometryDirectly(poClone);
     359        2189 :     if (eErr != OGRERR_NONE)
     360           2 :         delete poClone;
     361             : 
     362        2189 :     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      188083 : OGRErr OGRGeometryCollection::addGeometryDirectly(OGRGeometry *poNewGeom)
     393             : 
     394             : {
     395      188083 :     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      188077 :     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      188077 :     HomogenizeDimensionalityWith(poNewGeom);
     414             : 
     415             :     OGRGeometry **papoNewGeoms =
     416      188077 :         static_cast<OGRGeometry **>(VSI_REALLOC_VERBOSE(
     417             :             papoGeoms, sizeof(OGRGeometry *) * (nGeomCount + 1)));
     418      188077 :     if (papoNewGeoms == nullptr)
     419           0 :         return OGRERR_FAILURE;
     420             : 
     421      188077 :     papoGeoms = papoNewGeoms;
     422      188077 :     papoGeoms[nGeomCount] = poNewGeom;
     423             : 
     424      188077 :     nGeomCount++;
     425             : 
     426      188077 :     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      102806 : OGRErr OGRGeometryCollection::addGeometry(std::unique_ptr<OGRGeometry> geom)
     448             : {
     449      102806 :     OGRGeometry *poGeom = geom.release();
     450      102806 :     OGRErr eErr = addGeometryDirectly(poGeom);
     451      102806 :     if (eErr != OGRERR_NONE)
     452           2 :         delete poGeom;
     453      102806 :     return eErr;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                       addGeometryComponents()                        */
     458             : /************************************************************************/
     459             : 
     460             : /**
     461             :  * \brief Add the components of another OGRGeometryCollection to this one.
     462             :  *
     463             :  * Some subclasses of OGRGeometryCollection restrict the types of geometry
     464             :  * that can be added, and may return an error.
     465             :  *
     466             :  * @param geom geometry whose components should be added to add to the container.
     467             :  *
     468             :  * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
     469             :  * the geometry type is illegal for the type of geometry container.
     470             :  *
     471             :  * @since 3.13
     472             :  */
     473             : 
     474           4 : OGRErr OGRGeometryCollection::addGeometryComponents(
     475             :     std::unique_ptr<OGRGeometryCollection> geom)
     476             : {
     477           4 :     if (geom->nGeomCount == 0)
     478             :     {
     479           0 :         return OGRERR_NONE;
     480             :     }
     481             : 
     482          10 :     for (int i = 0; i < geom->nGeomCount; i++)
     483             :     {
     484           6 :         if (!isCompatibleSubType(geom->papoGeoms[i]->getGeometryType()))
     485             :         {
     486           0 :             return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
     487             :         }
     488             :     }
     489             : 
     490          10 :     for (int i = 0; i < geom->nGeomCount; i++)
     491             :     {
     492           6 :         HomogenizeDimensionalityWith(geom->papoGeoms[i]);
     493             :     }
     494             : 
     495             :     OGRGeometry **papoNewGeoms = static_cast<OGRGeometry **>(
     496           4 :         VSI_REALLOC_VERBOSE(papoGeoms, sizeof(OGRGeometry *) *
     497             :                                            (nGeomCount + geom->nGeomCount)));
     498           4 :     if (papoNewGeoms == nullptr)
     499           0 :         return OGRERR_FAILURE;
     500             : 
     501          10 :     for (int i = 0; i < geom->nGeomCount; i++)
     502             :     {
     503           6 :         papoNewGeoms[nGeomCount + i] = geom->papoGeoms[i];
     504           6 :         geom->papoGeoms[i] = nullptr;
     505             :     }
     506             : 
     507           4 :     papoGeoms = papoNewGeoms;
     508           4 :     nGeomCount += geom->nGeomCount;
     509             : 
     510           4 :     return OGRERR_NONE;
     511             : }
     512             : 
     513             : /************************************************************************/
     514             : /*                           removeGeometry()                           */
     515             : /************************************************************************/
     516             : 
     517             : /**
     518             :  * \brief Remove a geometry from the container.
     519             :  *
     520             :  * Removing a geometry will cause the geometry count to drop by one, and all
     521             :  * "higher" geometries will shuffle down one in index.
     522             :  *
     523             :  * There is no SFCOM analog to this method.
     524             :  *
     525             :  * This method is the same as the C function OGR_G_RemoveGeometry().
     526             :  *
     527             :  * @param iGeom the index of the geometry to delete.  A value of -1 is a
     528             :  * special flag meaning that all geometries should be removed.
     529             :  *
     530             :  * @param bDelete if TRUE the geometry will be deallocated, otherwise it will
     531             :  * not.  The default is TRUE as the container is considered to own the
     532             :  * geometries in it. Note: using stealGeometry() might be a better alternative
     533             :  * to using bDelete = false.
     534             :  *
     535             :  * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
     536             :  * out of range.
     537             :  */
     538             : 
     539        5248 : OGRErr OGRGeometryCollection::removeGeometry(int iGeom, int bDelete)
     540             : 
     541             : {
     542        5248 :     if (iGeom < -1 || iGeom >= nGeomCount)
     543           4 :         return OGRERR_FAILURE;
     544             : 
     545             :     // Special case.
     546        5244 :     if (iGeom == -1)
     547             :     {
     548           7 :         while (nGeomCount > 0)
     549           5 :             removeGeometry(nGeomCount - 1, bDelete);
     550           2 :         return OGRERR_NONE;
     551             :     }
     552             : 
     553        5242 :     if (bDelete)
     554         134 :         delete papoGeoms[iGeom];
     555             : 
     556        5242 :     memmove(papoGeoms + iGeom, papoGeoms + iGeom + 1,
     557        5242 :             sizeof(OGRGeometry *) * (nGeomCount - iGeom - 1));
     558             : 
     559        5242 :     nGeomCount--;
     560             : 
     561        5242 :     return OGRERR_NONE;
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                           stealGeometry()                            */
     566             : /************************************************************************/
     567             : 
     568             : /**
     569             :  * \brief Remove a geometry from the container and return it to the caller
     570             :  *
     571             :  * Removing a geometry will cause the geometry count to drop by one, and all
     572             :  * "higher" geometries will shuffle down one in index.
     573             :  *
     574             :  * There is no SFCOM analog to this method.
     575             :  *
     576             :  * @param iGeom the index of the geometry to delete.
     577             :  *
     578             :  * @return the sub-geometry, or nullptr in case of error.
     579             :  * @since 3.10
     580             :  */
     581             : 
     582          20 : std::unique_ptr<OGRGeometry> OGRGeometryCollection::stealGeometry(int iGeom)
     583             : {
     584          20 :     if (iGeom < 0 || iGeom >= nGeomCount)
     585           0 :         return nullptr;
     586             : 
     587          40 :     auto poSubGeom = std::unique_ptr<OGRGeometry>(papoGeoms[iGeom]);
     588          20 :     papoGeoms[iGeom] = nullptr;
     589          20 :     removeGeometry(iGeom);
     590          20 :     return poSubGeom;
     591             : }
     592             : 
     593             : /************************************************************************/
     594             : /*                           hasEmptyParts()                            */
     595             : /************************************************************************/
     596             : 
     597           9 : bool OGRGeometryCollection::hasEmptyParts() const
     598             : {
     599          17 :     for (const auto &poSubGeom : *this)
     600             :     {
     601          11 :         if (poSubGeom->IsEmpty() || poSubGeom->hasEmptyParts())
     602           3 :             return true;
     603             :     }
     604           6 :     return false;
     605             : }
     606             : 
     607             : /************************************************************************/
     608             : /*                          removeEmptyParts()                          */
     609             : /************************************************************************/
     610             : 
     611           5 : void OGRGeometryCollection::removeEmptyParts()
     612             : {
     613          13 :     for (int i = nGeomCount - 1; i >= 0; --i)
     614             :     {
     615           8 :         papoGeoms[i]->removeEmptyParts();
     616           8 :         if (papoGeoms[i]->IsEmpty())
     617           3 :             removeGeometry(i, true);
     618             :     }
     619           5 : }
     620             : 
     621             : /************************************************************************/
     622             : /*                              WkbSize()                               */
     623             : /*                                                                      */
     624             : /*      Return the size of this object in well known binary             */
     625             : /*      representation including the byte order, and type information.  */
     626             : /************************************************************************/
     627             : 
     628       22352 : size_t OGRGeometryCollection::WkbSize() const
     629             : 
     630             : {
     631       22352 :     size_t nSize = 9;
     632             : 
     633      106325 :     for (const auto &poGeom : *this)
     634             :     {
     635       83973 :         nSize += poGeom->WkbSize();
     636             :     }
     637             : 
     638       22352 :     return nSize;
     639             : }
     640             : 
     641             : /************************************************************************/
     642             : /*                       importFromWkbInternal()                        */
     643             : /************************************************************************/
     644             : 
     645             : //! @cond Doxygen_Suppress
     646        8811 : OGRErr OGRGeometryCollection::importFromWkbInternal(
     647             :     const unsigned char *pabyData, size_t nSize, int nRecLevel,
     648             :     OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut)
     649             : 
     650             : {
     651        8811 :     nBytesConsumedOut = 0;
     652             :     // Arbitrary value, but certainly large enough for reasonable use cases.
     653        8811 :     if (nRecLevel == 32)
     654             :     {
     655           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     656             :                  "Too many recursion levels (%d) while parsing WKB geometry.",
     657             :                  nRecLevel);
     658           1 :         return OGRERR_CORRUPT_DATA;
     659             :     }
     660             : 
     661        8810 :     OGRwkbByteOrder eByteOrder = wkbXDR;
     662        8810 :     size_t nDataOffset = 0;
     663        8810 :     int nGeomCountNew = 0;
     664        8810 :     OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
     665             :                                                     nDataOffset, eByteOrder, 9,
     666             :                                                     nGeomCountNew, eWkbVariant);
     667             : 
     668        8810 :     if (eErr != OGRERR_NONE)
     669         324 :         return eErr;
     670             : 
     671        8486 :     CPLAssert(nGeomCount == 0);
     672        8486 :     nGeomCount = nGeomCountNew;
     673             : 
     674             :     // coverity[tainted_data]
     675        8486 :     papoGeoms = static_cast<OGRGeometry **>(
     676        8486 :         VSI_CALLOC_VERBOSE(sizeof(OGRGeometry *), nGeomCount));
     677        8486 :     if (nGeomCount != 0 && papoGeoms == nullptr)
     678             :     {
     679           0 :         nGeomCount = 0;
     680           0 :         return OGRERR_NOT_ENOUGH_MEMORY;
     681             :     }
     682             : 
     683             :     /* -------------------------------------------------------------------- */
     684             :     /*      Get the Geoms.                                                  */
     685             :     /* -------------------------------------------------------------------- */
     686       19664 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
     687             :     {
     688             :         // Parses sub-geometry.
     689       12069 :         const unsigned char *pabySubData = pabyData + nDataOffset;
     690       12069 :         if (nSize < 9 && nSize != static_cast<size_t>(-1))
     691         891 :             return OGRERR_NOT_ENOUGH_DATA;
     692             : 
     693       12057 :         OGRwkbGeometryType eSubGeomType = wkbUnknown;
     694       12057 :         eErr = OGRReadWKBGeometryType(pabySubData, eWkbVariant, &eSubGeomType);
     695       12057 :         if (eErr != OGRERR_NONE)
     696         372 :             return eErr;
     697             : 
     698       11685 :         if (!isCompatibleSubType(eSubGeomType))
     699             :         {
     700           1 :             nGeomCount = iGeom;
     701           1 :             CPLDebug(
     702             :                 "OGR",
     703             :                 "Cannot add geometry of type (%d) to geometry of type (%d)",
     704           1 :                 eSubGeomType, getGeometryType());
     705           1 :             return OGRERR_CORRUPT_DATA;
     706             :         }
     707             : 
     708       11684 :         OGRGeometry *poSubGeom = nullptr;
     709       11684 :         size_t nSubGeomBytesConsumed = 0;
     710       11684 :         if (OGR_GT_IsSubClassOf(eSubGeomType, wkbGeometryCollection))
     711             :         {
     712         161 :             poSubGeom = OGRGeometryFactory::createGeometry(eSubGeomType);
     713         161 :             if (poSubGeom == nullptr)
     714           0 :                 eErr = OGRERR_FAILURE;
     715             :             else
     716         161 :                 eErr = poSubGeom->toGeometryCollection()->importFromWkbInternal(
     717             :                     pabySubData, nSize, nRecLevel + 1, eWkbVariant,
     718             :                     nSubGeomBytesConsumed);
     719             :         }
     720             :         else
     721             :         {
     722       11523 :             eErr = OGRGeometryFactory::createFromWkb(
     723             :                 pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant,
     724             :                 nSubGeomBytesConsumed);
     725             : 
     726       11523 :             if (eErr == OGRERR_NONE)
     727             :             {
     728             :                 // if this is a Z or M geom make sure the sub geoms are as well
     729       11049 :                 if (Is3D() && !poSubGeom->Is3D())
     730             :                 {
     731           0 :                     CPLDebug("OGR", "Promoting sub-geometry to 3D");
     732           0 :                     poSubGeom->set3D(TRUE);
     733             :                 }
     734             : 
     735       11049 :                 if (IsMeasured() && !poSubGeom->IsMeasured())
     736             :                 {
     737           0 :                     CPLDebug("OGR", "Promoting sub-geometry to Measured");
     738           0 :                     poSubGeom->setMeasured(TRUE);
     739             :                 }
     740             :             }
     741             :         }
     742             : 
     743       11684 :         if (eErr != OGRERR_NONE)
     744             :         {
     745         506 :             nGeomCount = iGeom;
     746         506 :             delete poSubGeom;
     747         506 :             return eErr;
     748             :         }
     749             : 
     750       11178 :         papoGeoms[iGeom] = poSubGeom;
     751             : 
     752       11178 :         if (papoGeoms[iGeom]->Is3D())
     753        5780 :             flags |= OGR_G_3D;
     754       11178 :         if (papoGeoms[iGeom]->IsMeasured())
     755        5678 :             flags |= OGR_G_MEASURED;
     756             : 
     757       11178 :         CPLAssert(nSubGeomBytesConsumed > 0);
     758       11178 :         if (nSize != static_cast<size_t>(-1))
     759             :         {
     760       11173 :             CPLAssert(nSize >= nSubGeomBytesConsumed);
     761       11173 :             nSize -= nSubGeomBytesConsumed;
     762             :         }
     763             : 
     764       11178 :         nDataOffset += nSubGeomBytesConsumed;
     765             :     }
     766        7595 :     nBytesConsumedOut = nDataOffset;
     767             : 
     768        7595 :     return OGRERR_NONE;
     769             : }
     770             : 
     771             : //! @endcond
     772             : 
     773             : /************************************************************************/
     774             : /*                           importFromWkb()                            */
     775             : /*                                                                      */
     776             : /*      Initialize from serialized stream in well known binary          */
     777             : /*      format.                                                         */
     778             : /************************************************************************/
     779             : 
     780        5007 : OGRErr OGRGeometryCollection::importFromWkb(const unsigned char *pabyData,
     781             :                                             size_t nSize,
     782             :                                             OGRwkbVariant eWkbVariant,
     783             :                                             size_t &nBytesConsumedOut)
     784             : 
     785             : {
     786        5007 :     return importFromWkbInternal(pabyData, nSize, 0, eWkbVariant,
     787        5007 :                                  nBytesConsumedOut);
     788             : }
     789             : 
     790             : /************************************************************************/
     791             : /*                            exportToWkb()                             */
     792             : /*                                                                      */
     793             : /*      Build a well known binary representation of this object.        */
     794             : /************************************************************************/
     795             : 
     796             : OGRErr
     797       12419 : OGRGeometryCollection::exportToWkb(unsigned char *pabyData,
     798             :                                    const OGRwkbExportOptions *psOptions) const
     799             : 
     800             : {
     801       12419 :     if (psOptions == nullptr)
     802             :     {
     803             :         static const OGRwkbExportOptions defaultOptions;
     804           0 :         psOptions = &defaultOptions;
     805             :     }
     806             : 
     807       12419 :     OGRwkbExportOptions sOptions(*psOptions);
     808             : 
     809       12558 :     if (sOptions.eWkbVariant == wkbVariantOldOgc &&
     810         139 :         (wkbFlatten(getGeometryType()) == wkbMultiCurve ||
     811         129 :          wkbFlatten(getGeometryType()) == wkbMultiSurface))
     812             :     {
     813             :         // Does not make sense for new geometries, so patch it.
     814          19 :         sOptions.eWkbVariant = wkbVariantIso;
     815             :     }
     816             : 
     817             :     /* -------------------------------------------------------------------- */
     818             :     /*      Set the byte order.                                             */
     819             :     /* -------------------------------------------------------------------- */
     820       12419 :     pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
     821             :         static_cast<unsigned char>(sOptions.eByteOrder));
     822             : 
     823             :     /* -------------------------------------------------------------------- */
     824             :     /*      Set the geometry feature type, ensuring that 3D flag is         */
     825             :     /*      preserved.                                                      */
     826             :     /* -------------------------------------------------------------------- */
     827       12419 :     GUInt32 nGType = getGeometryType();
     828             : 
     829       12419 :     if (sOptions.eWkbVariant == wkbVariantIso)
     830       12295 :         nGType = getIsoGeometryType();
     831         124 :     else if (sOptions.eWkbVariant == wkbVariantPostGIS1)
     832             :     {
     833           4 :         const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType));
     834           4 :         nGType = wkbFlatten(nGType);
     835           4 :         if (nGType == wkbMultiCurve)
     836           0 :             nGType = POSTGIS15_MULTICURVE;
     837           4 :         else if (nGType == wkbMultiSurface)
     838           0 :             nGType = POSTGIS15_MULTISURFACE;
     839           4 :         if (bIs3D)
     840             :             // Yes, explicitly set wkb25DBit.
     841           1 :             nGType =
     842           1 :                 static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
     843             :     }
     844             : 
     845       12419 :     if (OGR_SWAP(sOptions.eByteOrder))
     846             :     {
     847           7 :         nGType = CPL_SWAP32(nGType);
     848             :     }
     849             : 
     850       12419 :     memcpy(pabyData + 1, &nGType, 4);
     851             : 
     852             :     /* -------------------------------------------------------------------- */
     853             :     /*      Copy in the raw data.                                           */
     854             :     /* -------------------------------------------------------------------- */
     855       12419 :     if (OGR_SWAP(sOptions.eByteOrder))
     856             :     {
     857           7 :         int nCount = CPL_SWAP32(nGeomCount);
     858           7 :         memcpy(pabyData + 5, &nCount, 4);
     859             :     }
     860             :     else
     861             :     {
     862       12412 :         memcpy(pabyData + 5, &nGeomCount, 4);
     863             :     }
     864             : 
     865       12419 :     size_t nOffset = 9;
     866             : 
     867             :     /* ==================================================================== */
     868             :     /*      Serialize each of the Geoms.                                    */
     869             :     /* ==================================================================== */
     870       12419 :     int iGeom = 0;
     871       81332 :     for (auto &&poSubGeom : *this)
     872             :     {
     873       68913 :         poSubGeom->exportToWkb(pabyData + nOffset, &sOptions);
     874             :         // Should normally not happen if everyone else does its job,
     875             :         // but has happened sometimes. (#6332)
     876       68913 :         if (poSubGeom->getCoordinateDimension() != getCoordinateDimension())
     877             :         {
     878           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     879             :                      "Sub-geometry %d has coordinate dimension %d, "
     880             :                      "but container has %d",
     881           0 :                      iGeom, poSubGeom->getCoordinateDimension(),
     882           0 :                      getCoordinateDimension());
     883             :         }
     884             : 
     885       68913 :         nOffset += poSubGeom->WkbSize();
     886       68913 :         iGeom++;
     887             :     }
     888             : 
     889       12419 :     return OGRERR_NONE;
     890             : }
     891             : 
     892             : /************************************************************************/
     893             : /*                       importFromWktInternal()                        */
     894             : /************************************************************************/
     895             : 
     896         640 : OGRErr OGRGeometryCollection::importFromWktInternal(const char **ppszInput,
     897             :                                                     int nRecLevel)
     898             : 
     899             : {
     900             :     // Arbitrary value, but certainly large enough for reasonable usages.
     901         640 :     if (nRecLevel == 32)
     902             :     {
     903           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     904             :                  "Too many recursion levels (%d) while parsing WKT geometry.",
     905             :                  nRecLevel);
     906           1 :         return OGRERR_CORRUPT_DATA;
     907             :     }
     908             : 
     909         639 :     int bHasZ = FALSE;
     910         639 :     int bHasM = FALSE;
     911         639 :     bool bIsEmpty = false;
     912         639 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
     913         639 :     if (eErr != OGRERR_NONE)
     914           4 :         return eErr;
     915         635 :     if (bHasZ)
     916          97 :         flags |= OGR_G_3D;
     917         635 :     if (bHasM)
     918          61 :         flags |= OGR_G_MEASURED;
     919         635 :     if (bIsEmpty)
     920          45 :         return OGRERR_NONE;
     921             : 
     922         590 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
     923         590 :     const char *pszInput = *ppszInput;
     924             : 
     925             :     // Skip first '('.
     926         590 :     pszInput = OGRWktReadToken(pszInput, szToken);
     927             : 
     928             :     /* ==================================================================== */
     929             :     /*      Read each subgeometry in turn.                                  */
     930             :     /* ==================================================================== */
     931         650 :     do
     932             :     {
     933        1240 :         OGRGeometry *poGeom = nullptr;
     934             : 
     935             :         /* --------------------------------------------------------------------
     936             :          */
     937             :         /*      Get the first token, which should be the geometry type. */
     938             :         /* --------------------------------------------------------------------
     939             :          */
     940        1240 :         OGRWktReadToken(pszInput, szToken);
     941             : 
     942             :         /* --------------------------------------------------------------------
     943             :          */
     944             :         /*      Do the import. */
     945             :         /* --------------------------------------------------------------------
     946             :          */
     947        1240 :         if (STARTS_WITH_CI(szToken, "GEOMETRYCOLLECTION"))
     948             :         {
     949         110 :             OGRGeometryCollection *poGC = new OGRGeometryCollection();
     950         110 :             poGeom = poGC;
     951         110 :             eErr = poGC->importFromWktInternal(&pszInput, nRecLevel + 1);
     952             :         }
     953             :         else
     954             :             eErr =
     955        1130 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
     956             : 
     957        1240 :         if (eErr == OGRERR_NONE)
     958             :         {
     959             :             // If this has M, but not Z, it is an error if poGeom does
     960             :             // not have M.
     961        1196 :             if (!Is3D() && IsMeasured() && !poGeom->IsMeasured())
     962           0 :                 eErr = OGRERR_CORRUPT_DATA;
     963             :             else
     964        1196 :                 eErr = addGeometryDirectly(poGeom);
     965             :         }
     966        1240 :         if (eErr != OGRERR_NONE)
     967             :         {
     968          44 :             delete poGeom;
     969          44 :             return eErr;
     970             :         }
     971             : 
     972             :         /* --------------------------------------------------------------------
     973             :          */
     974             :         /*      Read the delimiter following the ring. */
     975             :         /* --------------------------------------------------------------------
     976             :          */
     977             : 
     978        1196 :         pszInput = OGRWktReadToken(pszInput, szToken);
     979        1196 :     } while (szToken[0] == ',');
     980             : 
     981             :     /* -------------------------------------------------------------------- */
     982             :     /*      freak if we don't get a closing bracket.                        */
     983             :     /* -------------------------------------------------------------------- */
     984         546 :     if (szToken[0] != ')')
     985           2 :         return OGRERR_CORRUPT_DATA;
     986             : 
     987         544 :     *ppszInput = pszInput;
     988             : 
     989         544 :     return OGRERR_NONE;
     990             : }
     991             : 
     992             : /************************************************************************/
     993             : /*                           importFromWkt()                            */
     994             : /************************************************************************/
     995             : 
     996         530 : OGRErr OGRGeometryCollection::importFromWkt(const char **ppszInput)
     997             : 
     998             : {
     999         530 :     return importFromWktInternal(ppszInput, 0);
    1000             : }
    1001             : 
    1002             : /************************************************************************/
    1003             : /*                            exportToWkt()                             */
    1004             : /*                                                                      */
    1005             : /*      Translate this structure into its well known text format        */
    1006             : /*      equivalent.                                                     */
    1007             : /************************************************************************/
    1008             : 
    1009         299 : std::string OGRGeometryCollection::exportToWkt(const OGRWktOptions &opts,
    1010             :                                                OGRErr *err) const
    1011             : {
    1012         299 :     return exportToWktInternal(opts, err);
    1013             : }
    1014             : 
    1015             : //! @cond Doxygen_Suppress
    1016        1246 : std::string OGRGeometryCollection::exportToWktInternal(
    1017             :     const OGRWktOptions &opts, OGRErr *err, const std::string &exclude) const
    1018             : {
    1019        1246 :     bool first = true;
    1020        1246 :     const size_t excludeSize = exclude.size();
    1021        2492 :     std::string wkt(getGeometryName());
    1022        1246 :     wkt += wktTypeString(opts.variant);
    1023             : 
    1024             :     try
    1025             :     {
    1026        2954 :         for (const auto &poSubGeom : *this)
    1027             :         {
    1028        1708 :             OGRErr subgeomErr = OGRERR_NONE;
    1029        1708 :             std::string tempWkt = poSubGeom->exportToWkt(opts, &subgeomErr);
    1030        1708 :             if (subgeomErr != OGRERR_NONE)
    1031             :             {
    1032           0 :                 if (err)
    1033           0 :                     *err = subgeomErr;
    1034           0 :                 return std::string();
    1035             :             }
    1036             : 
    1037             :             // For some strange reason we exclude the typename leader when using
    1038             :             // some geometries as part of a collection.
    1039        1708 :             if (excludeSize && (tempWkt.compare(0, excludeSize, exclude) == 0))
    1040             :             {
    1041        1130 :                 auto pos = tempWkt.find('(');
    1042             :                 // We won't have an opening paren if the geom is empty.
    1043        1130 :                 if (pos == std::string::npos)
    1044          31 :                     continue;
    1045        1099 :                 tempWkt = tempWkt.substr(pos);
    1046             :             }
    1047             : 
    1048             :             // Also strange, we allow the inclusion of ISO-only geometries (see
    1049             :             // OGRPolyhedralSurface) in a non-iso geometry collection.  In order
    1050             :             // to facilitate this, we need to rip the ISO bit from the string.
    1051        1677 :             if (opts.variant != wkbVariantIso)
    1052             :             {
    1053             :                 std::string::size_type pos;
    1054         654 :                 if ((pos = tempWkt.find(" Z ")) != std::string::npos)
    1055          11 :                     tempWkt.erase(pos + 1, 2);
    1056         643 :                 else if ((pos = tempWkt.find(" M ")) != std::string::npos)
    1057           0 :                     tempWkt.erase(pos + 1, 2);
    1058         643 :                 else if ((pos = tempWkt.find(" ZM ")) != std::string::npos)
    1059           0 :                     tempWkt.erase(pos + 1, 3);
    1060             :             }
    1061             : 
    1062        1677 :             if (first)
    1063        1086 :                 wkt += '(';
    1064             :             else
    1065         591 :                 wkt += ',';
    1066        1677 :             first = false;
    1067        1677 :             wkt += tempWkt;
    1068             :         }
    1069             : 
    1070        1246 :         if (err)
    1071        1129 :             *err = OGRERR_NONE;
    1072        1246 :         if (first)
    1073         160 :             wkt += "EMPTY";
    1074             :         else
    1075        1086 :             wkt += ')';
    1076        1246 :         return wkt;
    1077             :     }
    1078           0 :     catch (const std::bad_alloc &e)
    1079             :     {
    1080           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1081           0 :         if (err)
    1082           0 :             *err = OGRERR_FAILURE;
    1083           0 :         return std::string();
    1084             :     }
    1085             : }
    1086             : 
    1087             : //! @endcond
    1088             : 
    1089             : /************************************************************************/
    1090             : /*                            getEnvelope()                             */
    1091             : /************************************************************************/
    1092             : 
    1093       16076 : void OGRGeometryCollection::getEnvelope(OGREnvelope *psEnvelope) const
    1094             : 
    1095             : {
    1096       16076 :     OGREnvelope3D oEnv3D;
    1097       16076 :     getEnvelope(&oEnv3D);
    1098       16076 :     psEnvelope->MinX = oEnv3D.MinX;
    1099       16076 :     psEnvelope->MinY = oEnv3D.MinY;
    1100       16076 :     psEnvelope->MaxX = oEnv3D.MaxX;
    1101       16076 :     psEnvelope->MaxY = oEnv3D.MaxY;
    1102       16076 : }
    1103             : 
    1104             : /************************************************************************/
    1105             : /*                            getEnvelope()                             */
    1106             : /************************************************************************/
    1107             : 
    1108       16912 : void OGRGeometryCollection::getEnvelope(OGREnvelope3D *psEnvelope) const
    1109             : 
    1110             : {
    1111       16912 :     OGREnvelope3D oGeomEnv;
    1112       16912 :     bool bExtentSet = false;
    1113             : 
    1114       16912 :     *psEnvelope = OGREnvelope3D();
    1115      403046 :     for (const auto &poSubGeom : *this)
    1116             :     {
    1117      386134 :         if (!poSubGeom->IsEmpty())
    1118             :         {
    1119      386126 :             bExtentSet = true;
    1120      386126 :             poSubGeom->getEnvelope(&oGeomEnv);
    1121      386126 :             psEnvelope->Merge(oGeomEnv);
    1122             :         }
    1123             :     }
    1124             : 
    1125       16912 :     if (!bExtentSet)
    1126             :     {
    1127             :         // To be backward compatible when called on empty geom
    1128          13 :         psEnvelope->MinX = 0.0;
    1129          13 :         psEnvelope->MinY = 0.0;
    1130          13 :         psEnvelope->MinZ = 0.0;
    1131          13 :         psEnvelope->MaxX = 0.0;
    1132          13 :         psEnvelope->MaxY = 0.0;
    1133          13 :         psEnvelope->MaxZ = 0.0;
    1134             :     }
    1135       16912 : }
    1136             : 
    1137             : /************************************************************************/
    1138             : /*                               Equals()                               */
    1139             : /************************************************************************/
    1140             : 
    1141        3880 : OGRBoolean OGRGeometryCollection::Equals(const OGRGeometry *poOther) const
    1142             : 
    1143             : {
    1144        3880 :     if (poOther == this)
    1145           2 :         return TRUE;
    1146             : 
    1147        3878 :     if (poOther->getGeometryType() != getGeometryType())
    1148           0 :         return FALSE;
    1149             : 
    1150        3878 :     if (IsEmpty() && poOther->IsEmpty())
    1151          12 :         return TRUE;
    1152             : 
    1153        3866 :     auto poOGC = poOther->toGeometryCollection();
    1154        3866 :     if (getNumGeometries() != poOGC->getNumGeometries())
    1155           2 :         return FALSE;
    1156             : 
    1157             :     // TODO(schwehr): Should test the SRS.
    1158             : 
    1159        8504 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
    1160             :     {
    1161        4641 :         if (!getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)))
    1162           1 :             return FALSE;
    1163             :     }
    1164             : 
    1165        3863 :     return TRUE;
    1166             : }
    1167             : 
    1168             : /************************************************************************/
    1169             : /*                             transform()                              */
    1170             : /************************************************************************/
    1171             : 
    1172         182 : OGRErr OGRGeometryCollection::transform(OGRCoordinateTransformation *poCT)
    1173             : 
    1174             : {
    1175         182 :     int iGeom = 0;
    1176         410 :     for (auto &poSubGeom : *this)
    1177             :     {
    1178         228 :         const OGRErr eErr = poSubGeom->transform(poCT);
    1179         228 :         if (eErr != OGRERR_NONE)
    1180             :         {
    1181           0 :             if (iGeom != 0)
    1182             :             {
    1183           0 :                 CPLDebug("OGR",
    1184             :                          "OGRGeometryCollection::transform() failed for a "
    1185             :                          "geometry other than the first, meaning some "
    1186             :                          "geometries are transformed and some are not.");
    1187             : 
    1188           0 :                 return OGRERR_FAILURE;
    1189             :             }
    1190             : 
    1191           0 :             return eErr;
    1192             :         }
    1193         228 :         iGeom++;
    1194             :     }
    1195             : 
    1196         182 :     assignSpatialReference(poCT->GetTargetCS());
    1197             : 
    1198         182 :     return OGRERR_NONE;
    1199             : }
    1200             : 
    1201             : /************************************************************************/
    1202             : /*                             closeRings()                             */
    1203             : /************************************************************************/
    1204             : 
    1205         129 : void OGRGeometryCollection::closeRings()
    1206             : 
    1207             : {
    1208         412 :     for (auto &poSubGeom : *this)
    1209             :     {
    1210         283 :         if (OGR_GT_IsSubClassOf(wkbFlatten(poSubGeom->getGeometryType()),
    1211         283 :                                 wkbCurvePolygon))
    1212             :         {
    1213          83 :             OGRCurvePolygon *poPoly = poSubGeom->toCurvePolygon();
    1214          83 :             poPoly->closeRings();
    1215             :         }
    1216             :     }
    1217         129 : }
    1218             : 
    1219             : /************************************************************************/
    1220             : /*                       setCoordinateDimension()                       */
    1221             : /************************************************************************/
    1222             : 
    1223         494 : bool OGRGeometryCollection::setCoordinateDimension(int nNewDimension)
    1224             : 
    1225             : {
    1226        1439 :     for (auto &poSubGeom : *this)
    1227             :     {
    1228         945 :         if (!poSubGeom->setCoordinateDimension(nNewDimension))
    1229           0 :             return false;
    1230             :     }
    1231             : 
    1232         494 :     return OGRGeometry::setCoordinateDimension(nNewDimension);
    1233             : }
    1234             : 
    1235       88874 : bool OGRGeometryCollection::set3D(OGRBoolean bIs3D)
    1236             : {
    1237      195579 :     for (auto &poSubGeom : *this)
    1238             :     {
    1239      106705 :         if (!poSubGeom->set3D(bIs3D))
    1240           0 :             return false;
    1241             :     }
    1242             : 
    1243       88874 :     return OGRGeometry::set3D(bIs3D);
    1244             : }
    1245             : 
    1246       46301 : bool OGRGeometryCollection::setMeasured(OGRBoolean bIsMeasured)
    1247             : {
    1248      153577 :     for (auto &poSubGeom : *this)
    1249             :     {
    1250      107276 :         if (!poSubGeom->setMeasured(bIsMeasured))
    1251           0 :             return false;
    1252             :     }
    1253             : 
    1254       46301 :     return OGRGeometry::setMeasured(bIsMeasured);
    1255             : }
    1256             : 
    1257             : /************************************************************************/
    1258             : /*                             get_Length()                             */
    1259             : /************************************************************************/
    1260             : 
    1261             : /**
    1262             :  * \brief Compute the length of a multicurve.
    1263             :  *
    1264             :  * The length is computed as the sum of the length of all members
    1265             :  * in this collection.
    1266             :  *
    1267             :  * @note No warning will be issued if a member of the collection does not
    1268             :  *       support the get_Length method.
    1269             :  *
    1270             :  * @return computed length.
    1271             :  */
    1272             : 
    1273           9 : double OGRGeometryCollection::get_Length() const
    1274             : {
    1275           9 :     double dfLength = 0.0;
    1276          26 :     for (const auto &poSubGeom : *this)
    1277             :     {
    1278             :         const OGRwkbGeometryType eType =
    1279          17 :             wkbFlatten(poSubGeom->getGeometryType());
    1280          17 :         if (OGR_GT_IsCurve(eType))
    1281             :         {
    1282          13 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1283          13 :             dfLength += poCurve->get_Length();
    1284             :         }
    1285           4 :         else if (OGR_GT_IsSurface(eType))
    1286             :         {
    1287           1 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1288           1 :             dfLength += poSurface->get_Length();
    1289             :         }
    1290           3 :         else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    1291             :         {
    1292             :             const OGRGeometryCollection *poColl =
    1293           2 :                 poSubGeom->toGeometryCollection();
    1294           2 :             dfLength += poColl->get_Length();
    1295             :         }
    1296             :     }
    1297             : 
    1298           9 :     return dfLength;
    1299             : }
    1300             : 
    1301             : /************************************************************************/
    1302             : /*                              get_Area()                              */
    1303             : /************************************************************************/
    1304             : 
    1305             : /**
    1306             :  * \brief Compute area of geometry collection.
    1307             :  *
    1308             :  * The area is computed as the sum of the areas of all members
    1309             :  * in this collection.
    1310             :  *
    1311             :  * @note No warning will be issued if a member of the collection does not
    1312             :  *       support the get_Area method.
    1313             :  *
    1314             :  * @return computed area.
    1315             :  */
    1316             : 
    1317          13 : double OGRGeometryCollection::get_Area() const
    1318             : {
    1319          13 :     double dfArea = 0.0;
    1320          44 :     for (const auto &poSubGeom : *this)
    1321             :     {
    1322          31 :         OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
    1323          31 :         if (OGR_GT_IsSurface(eType))
    1324             :         {
    1325          26 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1326          26 :             dfArea += poSurface->get_Area();
    1327             :         }
    1328           5 :         else if (OGR_GT_IsCurve(eType))
    1329             :         {
    1330           1 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1331           1 :             dfArea += poCurve->get_Area();
    1332             :         }
    1333           4 :         else if (OGR_GT_IsSubClassOf(eType, wkbMultiSurface) ||
    1334             :                  eType == wkbGeometryCollection)
    1335             :         {
    1336           2 :             dfArea += poSubGeom->toGeometryCollection()->get_Area();
    1337             :         }
    1338             :     }
    1339             : 
    1340          13 :     return dfArea;
    1341             : }
    1342             : 
    1343             : /************************************************************************/
    1344             : /*                          get_GeodesicArea()                          */
    1345             : /************************************************************************/
    1346             : 
    1347             : /**
    1348             :  * \brief Compute area of geometry collection, considered as a surface on
    1349             :  * the underlying ellipsoid of the SRS attached to the geometry.
    1350             :  *
    1351             :  * The returned area will always be in square meters, and assumes that
    1352             :  * polygon edges describe geodesic lines on the ellipsoid.
    1353             :  *
    1354             :  * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
    1355             :  * follow the shortest route on the surface of the ellipsoid.
    1356             :  *
    1357             :  * If the geometry' SRS is not a geographic one, geometries are reprojected to
    1358             :  * the underlying geographic SRS of the geometry' SRS.
    1359             :  * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
    1360             :  *
    1361             :  * The area is computed as the sum of the areas of all members
    1362             :  * in this collection.
    1363             :  *
    1364             :  * @note No warning will be issued if a member of the collection does not
    1365             :  *       support the get_GeodesicArea method.
    1366             :  *
    1367             :  * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
    1368             :  * @return the area of the geometry in square meters, or a negative value in case
    1369             :  * of error.
    1370             :  *
    1371             :  * @see get_Area() for an alternative method returning areas computed in
    1372             :  * 2D Cartesian space.
    1373             :  *
    1374             :  * @since GDAL 3.9
    1375             :  */
    1376           6 : double OGRGeometryCollection::get_GeodesicArea(
    1377             :     const OGRSpatialReference *poSRSOverride) const
    1378             : {
    1379           6 :     double dfArea = 0.0;
    1380          11 :     for (const auto &poSubGeom : *this)
    1381             :     {
    1382           8 :         OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
    1383           8 :         if (OGR_GT_IsSurface(eType))
    1384             :         {
    1385           4 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1386             :             const double dfLocalArea =
    1387           4 :                 poSurface->get_GeodesicArea(poSRSOverride);
    1388           4 :             if (dfLocalArea < 0)
    1389           1 :                 return dfLocalArea;
    1390           3 :             dfArea += dfLocalArea;
    1391             :         }
    1392           4 :         else if (OGR_GT_IsCurve(eType))
    1393             :         {
    1394           2 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1395           2 :             const double dfLocalArea = poCurve->get_GeodesicArea(poSRSOverride);
    1396           2 :             if (dfLocalArea < 0)
    1397           1 :                 return dfLocalArea;
    1398           1 :             dfArea += dfLocalArea;
    1399             :         }
    1400           2 :         else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    1401             :         {
    1402             :             const double dfLocalArea =
    1403           2 :                 poSubGeom->toGeometryCollection()->get_GeodesicArea(
    1404           2 :                     poSRSOverride);
    1405           2 :             if (dfLocalArea < 0)
    1406           1 :                 return dfLocalArea;
    1407           1 :             dfArea += dfLocalArea;
    1408             :         }
    1409             :     }
    1410             : 
    1411           3 :     return dfArea;
    1412             : }
    1413             : 
    1414             : /************************************************************************/
    1415             : /*                         get_GeodesicLength()                         */
    1416             : /************************************************************************/
    1417             : 
    1418             : /**
    1419             :  * \brief Get the length of the collection,where curve edges are geodesic lines
    1420             :  * on the underlying ellipsoid of the SRS attached to the geometry.
    1421             :  *
    1422             :  * The returned length will always be in meters.
    1423             :  *
    1424             :  * <a href="https://geographiclib.sourceforge.io/html/python/geodesics.html">Geodesics</a>
    1425             :  * follow the shortest route on the surface of the ellipsoid.
    1426             :  *
    1427             :  * If the geometry' SRS is not a geographic one, geometries are reprojected to
    1428             :  * the underlying geographic SRS of the geometry' SRS.
    1429             :  * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
    1430             :  *
    1431             :  * Note that geometries with circular arcs will be linearized in their original
    1432             :  * coordinate space first, so the resulting geodesic length will be an
    1433             :  * approximation.
    1434             :  *
    1435             :  * The length is computed as the sum of the lengths of all members
    1436             :  * in this collection.
    1437             :  *
    1438             :  * @note No warning will be issued if a member of the collection does not
    1439             :  *       support the get_GeodesicLength method.
    1440             :  *
    1441             :  * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
    1442             :  * @return the length of the geometry in meters, or a negative value in case
    1443             :  * of error.
    1444             :  *
    1445             :  * @see get_Length() for an alternative method returning areas computed in
    1446             :  * 2D Cartesian space.
    1447             :  *
    1448             :  * @since GDAL 3.10
    1449             :  */
    1450           5 : double OGRGeometryCollection::get_GeodesicLength(
    1451             :     const OGRSpatialReference *poSRSOverride) const
    1452             : {
    1453           5 :     double dfLength = 0.0;
    1454          10 :     for (const auto &poSubGeom : *this)
    1455             :     {
    1456             :         const OGRwkbGeometryType eType =
    1457           7 :             wkbFlatten(poSubGeom->getGeometryType());
    1458           7 :         if (OGR_GT_IsSurface(eType))
    1459             :         {
    1460           4 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1461             :             const double dfLocalLength =
    1462           4 :                 poSurface->get_GeodesicLength(poSRSOverride);
    1463           4 :             if (dfLocalLength < 0)
    1464           1 :                 return dfLocalLength;
    1465           3 :             dfLength += dfLocalLength;
    1466             :         }
    1467           3 :         else if (OGR_GT_IsCurve(eType))
    1468             :         {
    1469           1 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1470             :             const double dfLocalLength =
    1471           1 :                 poCurve->get_GeodesicLength(poSRSOverride);
    1472           1 :             if (dfLocalLength < 0)
    1473           0 :                 return dfLocalLength;
    1474           1 :             dfLength += dfLocalLength;
    1475             :         }
    1476           2 :         else if (OGR_GT_IsSubClassOf(eType, wkbGeometryCollection))
    1477             :         {
    1478             :             const double dfLocalLength =
    1479           2 :                 poSubGeom->toGeometryCollection()->get_GeodesicLength(
    1480           2 :                     poSRSOverride);
    1481           2 :             if (dfLocalLength < 0)
    1482           1 :                 return dfLocalLength;
    1483           1 :             dfLength += dfLocalLength;
    1484             :         }
    1485             :     }
    1486             : 
    1487           3 :     return dfLength;
    1488             : }
    1489             : 
    1490             : /************************************************************************/
    1491             : /*                              IsEmpty()                               */
    1492             : /************************************************************************/
    1493             : 
    1494       33505 : OGRBoolean OGRGeometryCollection::IsEmpty() const
    1495             : {
    1496       33644 :     for (const auto &poSubGeom : *this)
    1497             :     {
    1498       32242 :         if (poSubGeom->IsEmpty() == FALSE)
    1499       32103 :             return FALSE;
    1500             :     }
    1501        1402 :     return TRUE;
    1502             : }
    1503             : 
    1504             : /************************************************************************/
    1505             : /*                       assignSpatialReference()                       */
    1506             : /************************************************************************/
    1507             : 
    1508      113529 : void OGRGeometryCollection::assignSpatialReference(
    1509             :     const OGRSpatialReference *poSR)
    1510             : {
    1511      113529 :     OGRGeometry::assignSpatialReference(poSR);
    1512     1525630 :     for (auto &poSubGeom : *this)
    1513             :     {
    1514     1412100 :         poSubGeom->assignSpatialReference(poSR);
    1515             :     }
    1516      113529 : }
    1517             : 
    1518             : /************************************************************************/
    1519             : /*                 OGRGeometryCollection::segmentize()                  */
    1520             : /************************************************************************/
    1521             : 
    1522          13 : bool OGRGeometryCollection::segmentize(double dfMaxLength)
    1523             : {
    1524          26 :     for (auto &poSubGeom : *this)
    1525             :     {
    1526          13 :         if (!poSubGeom->segmentize(dfMaxLength))
    1527           0 :             return false;
    1528             :     }
    1529          13 :     return true;
    1530             : }
    1531             : 
    1532             : /************************************************************************/
    1533             : /*                               swapXY()                               */
    1534             : /************************************************************************/
    1535             : 
    1536          30 : void OGRGeometryCollection::swapXY()
    1537             : {
    1538          76 :     for (auto &poSubGeom : *this)
    1539             :     {
    1540          46 :         poSubGeom->swapXY();
    1541             :     }
    1542          30 : }
    1543             : 
    1544             : /************************************************************************/
    1545             : /*                        isCompatibleSubType()                         */
    1546             : /************************************************************************/
    1547             : 
    1548             : /** Returns whether a geometry of the specified geometry type can be a
    1549             :  * member of this collection.
    1550             :  *
    1551             :  * @param eSubType type of the potential member
    1552             :  * @return TRUE or FALSE
    1553             :  */
    1554             : 
    1555        9310 : OGRBoolean OGRGeometryCollection::isCompatibleSubType(
    1556             :     CPL_UNUSED OGRwkbGeometryType eSubType) const
    1557             : {
    1558             :     // Accept all geometries as sub-geometries.
    1559        9310 :     return TRUE;
    1560             : }
    1561             : 
    1562             : /************************************************************************/
    1563             : /*                          hasCurveGeometry()                          */
    1564             : /************************************************************************/
    1565             : 
    1566        2536 : OGRBoolean OGRGeometryCollection::hasCurveGeometry(int bLookForNonLinear) const
    1567             : {
    1568        6445 :     for (const auto &poSubGeom : *this)
    1569             :     {
    1570        3992 :         if (poSubGeom->hasCurveGeometry(bLookForNonLinear))
    1571          83 :             return TRUE;
    1572             :     }
    1573        2453 :     return FALSE;
    1574             : }
    1575             : 
    1576             : /************************************************************************/
    1577             : /*                         getLinearGeometry()                          */
    1578             : /************************************************************************/
    1579             : 
    1580             : OGRGeometry *
    1581         331 : OGRGeometryCollection::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
    1582             :                                          const char *const *papszOptions) const
    1583             : {
    1584             :     auto poGC = std::unique_ptr<OGRGeometryCollection>(
    1585         331 :         OGRGeometryFactory::createGeometry(OGR_GT_GetLinear(getGeometryType()))
    1586         662 :             ->toGeometryCollection());
    1587         331 :     if (!poGC)
    1588           0 :         return nullptr;
    1589         331 :     poGC->assignSpatialReference(getSpatialReference());
    1590         704 :     for (const auto &poSubGeom : *this)
    1591             :     {
    1592         746 :         OGRGeometry *poSubGeomNew = poSubGeom->getLinearGeometry(
    1593         373 :             dfMaxAngleStepSizeDegrees, papszOptions);
    1594         373 :         if (poGC->addGeometryDirectly(poSubGeomNew) != OGRERR_NONE)
    1595           0 :             return nullptr;
    1596             :     }
    1597         331 :     return poGC.release();
    1598             : }
    1599             : 
    1600             : /************************************************************************/
    1601             : /*                          getCurveGeometry()                          */
    1602             : /************************************************************************/
    1603             : 
    1604             : OGRGeometry *
    1605          15 : OGRGeometryCollection::getCurveGeometry(const char *const *papszOptions) const
    1606             : {
    1607             :     auto poGC = std::unique_ptr<OGRGeometryCollection>(
    1608          15 :         OGRGeometryFactory::createGeometry(OGR_GT_GetCurve(getGeometryType()))
    1609          30 :             ->toGeometryCollection());
    1610          15 :     if (!poGC)
    1611           0 :         return nullptr;
    1612          15 :     poGC->assignSpatialReference(getSpatialReference());
    1613          15 :     bool bHasCurveGeometry = false;
    1614         162 :     for (const auto &poSubGeom : *this)
    1615             :     {
    1616         147 :         OGRGeometry *poSubGeomNew = poSubGeom->getCurveGeometry(papszOptions);
    1617         147 :         if (poSubGeomNew->hasCurveGeometry())
    1618           7 :             bHasCurveGeometry = true;
    1619         147 :         if (poGC->addGeometryDirectly(poSubGeomNew) != OGRERR_NONE)
    1620           0 :             return nullptr;
    1621             :     }
    1622          15 :     if (!bHasCurveGeometry)
    1623             :     {
    1624          10 :         return clone();
    1625             :     }
    1626           5 :     return poGC.release();
    1627             : }
    1628             : 
    1629             : /************************************************************************/
    1630             : /*                     TransferMembersAndDestroy()                      */
    1631             : /************************************************************************/
    1632             : 
    1633             : //! @cond Doxygen_Suppress
    1634             : OGRGeometryCollection *
    1635        1050 : OGRGeometryCollection::TransferMembersAndDestroy(OGRGeometryCollection *poSrc,
    1636             :                                                  OGRGeometryCollection *poDst)
    1637             : {
    1638        1050 :     poDst->assignSpatialReference(poSrc->getSpatialReference());
    1639        1050 :     poDst->set3D(poSrc->Is3D());
    1640        1050 :     poDst->setMeasured(poSrc->IsMeasured());
    1641        1050 :     poDst->nGeomCount = poSrc->nGeomCount;
    1642        1050 :     poDst->papoGeoms = poSrc->papoGeoms;
    1643        1050 :     poSrc->nGeomCount = 0;
    1644        1050 :     poSrc->papoGeoms = nullptr;
    1645        1050 :     delete poSrc;
    1646        1050 :     return poDst;
    1647             : }
    1648             : 
    1649             : //! @endcond
    1650             : 
    1651             : /************************************************************************/
    1652             : /*                      CastToGeometryCollection()                      */
    1653             : /************************************************************************/
    1654             : 
    1655             : /**
    1656             :  * \brief Cast to geometry collection.
    1657             :  *
    1658             :  * This methods cast a derived class of geometry collection to a plain
    1659             :  * geometry collection.
    1660             :  *
    1661             :  * The passed in geometry is consumed and a new one returned (or NULL in case
    1662             :  * of failure).
    1663             :  *
    1664             :  * @param poSrc the input geometry - ownership is passed to the method.
    1665             :  * @return new geometry.
    1666             :  */
    1667             : 
    1668             : OGRGeometryCollection *
    1669         921 : OGRGeometryCollection::CastToGeometryCollection(OGRGeometryCollection *poSrc)
    1670             : {
    1671         921 :     if (wkbFlatten(poSrc->getGeometryType()) == wkbGeometryCollection)
    1672           0 :         return poSrc;
    1673         921 :     return TransferMembersAndDestroy(poSrc, new OGRGeometryCollection());
    1674             : }

Generated by: LCOV version 1.14