LCOV - code coverage report
Current view: top level - ogr - ogrgeometrycollection.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 507 556 91.2 %
Date: 2025-01-18 12:42:00 Functions: 52 52 100.0 %

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

Generated by: LCOV version 1.14