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

Generated by: LCOV version 1.14