LCOV - code coverage report
Current view: top level - ogr - ogrgeometrycollection.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 448 485 92.4 %
Date: 2024-05-04 12:52:34 Functions: 46 46 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "ogr_geometry.h"
      32             : 
      33             : #include <cstddef>
      34             : #include <cstring>
      35             : #include <new>
      36             : 
      37             : #include "cpl_conv.h"
      38             : #include "cpl_error.h"
      39             : #include "cpl_string.h"
      40             : #include "cpl_vsi.h"
      41             : #include "ogr_api.h"
      42             : #include "ogr_core.h"
      43             : #include "ogr_p.h"
      44             : #include "ogr_spatialref.h"
      45             : 
      46             : /************************************************************************/
      47             : /*                       OGRGeometryCollection()                        */
      48             : /************************************************************************/
      49             : 
      50             : /**
      51             :  * \brief Create an empty geometry collection.
      52             :  */
      53             : 
      54             : OGRGeometryCollection::OGRGeometryCollection() = default;
      55             : 
      56             : /************************************************************************/
      57             : /*         OGRGeometryCollection( const OGRGeometryCollection& )        */
      58             : /************************************************************************/
      59             : 
      60             : /**
      61             :  * \brief Copy constructor.
      62             :  *
      63             :  * Note: before GDAL 2.1, only the default implementation of the constructor
      64             :  * existed, which could be unsafe to use.
      65             :  *
      66             :  * @since GDAL 2.1
      67             :  */
      68             : 
      69        5215 : OGRGeometryCollection::OGRGeometryCollection(const OGRGeometryCollection &other)
      70        5215 :     : OGRGeometry(other)
      71             : {
      72             :     // Do not use addGeometry() as it is virtual.
      73        5215 :     papoGeoms = static_cast<OGRGeometry **>(
      74        5215 :         VSI_CALLOC_VERBOSE(sizeof(void *), other.nGeomCount));
      75        5215 :     if (papoGeoms)
      76             :     {
      77        5215 :         nGeomCount = other.nGeomCount;
      78       13741 :         for (int i = 0; i < other.nGeomCount; i++)
      79             :         {
      80        8526 :             papoGeoms[i] = other.papoGeoms[i]->clone();
      81             :         }
      82             :     }
      83        5215 : }
      84             : 
      85             : /************************************************************************/
      86             : /*                       ~OGRGeometryCollection()                       */
      87             : /************************************************************************/
      88             : 
      89       75388 : OGRGeometryCollection::~OGRGeometryCollection()
      90             : 
      91             : {
      92       70509 :     OGRGeometryCollection::empty();
      93       75388 : }
      94             : 
      95             : /************************************************************************/
      96             : /*               operator=( const OGRGeometryCollection&)               */
      97             : /************************************************************************/
      98             : 
      99             : /**
     100             :  * \brief Assignment operator.
     101             :  *
     102             :  * Note: before GDAL 2.1, only the default implementation of the operator
     103             :  * existed, which could be unsafe to use.
     104             :  *
     105             :  * @since GDAL 2.1
     106             :  */
     107             : 
     108             : OGRGeometryCollection &
     109          29 : OGRGeometryCollection::operator=(const OGRGeometryCollection &other)
     110             : {
     111          29 :     if (this != &other)
     112             :     {
     113          28 :         empty();
     114             : 
     115          28 :         OGRGeometry::operator=(other);
     116             : 
     117          50 :         for (int i = 0; i < other.nGeomCount; i++)
     118             :         {
     119          22 :             addGeometry(other.papoGeoms[i]);
     120             :         }
     121             :     }
     122          29 :     return *this;
     123             : }
     124             : 
     125             : /************************************************************************/
     126             : /*                               empty()                                */
     127             : /************************************************************************/
     128             : 
     129       82209 : void OGRGeometryCollection::empty()
     130             : 
     131             : {
     132       82209 :     if (papoGeoms != nullptr)
     133             :     {
     134      147034 :         for (auto &&poSubGeom : *this)
     135             :         {
     136       79534 :             delete poSubGeom;
     137             :         }
     138       67500 :         CPLFree(papoGeoms);
     139             :     }
     140             : 
     141       82209 :     nGeomCount = 0;
     142       82209 :     papoGeoms = nullptr;
     143       82209 : }
     144             : 
     145             : /************************************************************************/
     146             : /*                               clone()                                */
     147             : /************************************************************************/
     148             : 
     149         602 : OGRGeometryCollection *OGRGeometryCollection::clone() const
     150             : 
     151             : {
     152         602 :     return new (std::nothrow) OGRGeometryCollection(*this);
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                          getGeometryType()                           */
     157             : /************************************************************************/
     158             : 
     159        8555 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
     160             : 
     161             : {
     162        8555 :     if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED))
     163        2967 :         return wkbGeometryCollectionZM;
     164        5588 :     else if (flags & OGR_G_MEASURED)
     165         247 :         return wkbGeometryCollectionM;
     166        5341 :     else if (flags & OGR_G_3D)
     167        2500 :         return wkbGeometryCollection25D;
     168             :     else
     169        2841 :         return wkbGeometryCollection;
     170             : }
     171             : 
     172             : /************************************************************************/
     173             : /*                            getDimension()                            */
     174             : /************************************************************************/
     175             : 
     176           3 : int OGRGeometryCollection::getDimension() const
     177             : 
     178             : {
     179           3 :     int nDimension = 0;
     180             :     // FIXME? Not sure if it is really appropriate to take the max in case
     181             :     // of geometries of different dimension.
     182           7 :     for (auto &&poSubGeom : *this)
     183             :     {
     184           5 :         int nSubGeomDimension = poSubGeom->getDimension();
     185           5 :         if (nSubGeomDimension > nDimension)
     186             :         {
     187           3 :             nDimension = nSubGeomDimension;
     188           3 :             if (nDimension == 2)
     189           1 :                 break;
     190             :         }
     191             :     }
     192           3 :     return nDimension;
     193             : }
     194             : 
     195             : /************************************************************************/
     196             : /*                            flattenTo2D()                             */
     197             : /************************************************************************/
     198             : 
     199         100 : void OGRGeometryCollection::flattenTo2D()
     200             : 
     201             : {
     202         295 :     for (auto &&poSubGeom : *this)
     203             :     {
     204         195 :         poSubGeom->flattenTo2D();
     205             :     }
     206             : 
     207         100 :     flags &= ~OGR_G_3D;
     208         100 :     flags &= ~OGR_G_MEASURED;
     209         100 : }
     210             : 
     211             : /************************************************************************/
     212             : /*                          getGeometryName()                           */
     213             : /************************************************************************/
     214             : 
     215        1020 : const char *OGRGeometryCollection::getGeometryName() const
     216             : 
     217             : {
     218        1020 :     return "GEOMETRYCOLLECTION";
     219             : }
     220             : 
     221             : /************************************************************************/
     222             : /*                          getNumGeometries()                          */
     223             : /************************************************************************/
     224             : 
     225             : /**
     226             :  * \brief Fetch number of geometries in container.
     227             :  *
     228             :  * This method relates to the SFCOM IGeometryCollect::get_NumGeometries()
     229             :  * method.
     230             :  *
     231             :  * @return count of children geometries.  May be zero.
     232             :  */
     233             : 
     234      119803 : int OGRGeometryCollection::getNumGeometries() const
     235             : 
     236             : {
     237      119803 :     return nGeomCount;
     238             : }
     239             : 
     240             : /************************************************************************/
     241             : /*                           getGeometryRef()                           */
     242             : /************************************************************************/
     243             : 
     244             : /**
     245             :  * \brief Fetch geometry from container.
     246             :  *
     247             :  * This method returns a pointer to a geometry within the container.  The
     248             :  * returned geometry remains owned by the container, and should not be
     249             :  * modified.  The pointer is only valid until the next change to the
     250             :  * geometry container.  Use IGeometry::clone() to make a copy.
     251             :  *
     252             :  * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
     253             :  *
     254             :  * @param i the index of the geometry to fetch, between 0 and
     255             :  *          getNumGeometries() - 1.
     256             :  * @return pointer to requested geometry.
     257             :  */
     258             : 
     259        7496 : OGRGeometry *OGRGeometryCollection::getGeometryRef(int i)
     260             : 
     261             : {
     262        7496 :     if (i < 0 || i >= nGeomCount)
     263           0 :         return nullptr;
     264             : 
     265        7496 :     return papoGeoms[i];
     266             : }
     267             : 
     268             : /**
     269             :  * \brief Fetch geometry from container.
     270             :  *
     271             :  * This method returns a pointer to a geometry within the container.  The
     272             :  * returned geometry remains owned by the container, and should not be
     273             :  * modified.  The pointer is only valid until the next change to the
     274             :  * geometry container.  Use IGeometry::clone() to make a copy.
     275             :  *
     276             :  * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
     277             :  *
     278             :  * @param i the index of the geometry to fetch, between 0 and
     279             :  *          getNumGeometries() - 1.
     280             :  * @return pointer to requested geometry.
     281             :  */
     282             : 
     283      174370 : const OGRGeometry *OGRGeometryCollection::getGeometryRef(int i) const
     284             : 
     285             : {
     286      174370 :     if (i < 0 || i >= nGeomCount)
     287           0 :         return nullptr;
     288             : 
     289      174370 :     return papoGeoms[i];
     290             : }
     291             : 
     292             : /************************************************************************/
     293             : /*                            addGeometry()                             */
     294             : /*                                                                      */
     295             : /*      Add a new geometry to a collection.  Subclasses should          */
     296             : /*      override this to verify the type of the new geometry, and       */
     297             : /*      then call this method to actually add it.                       */
     298             : /************************************************************************/
     299             : 
     300             : /**
     301             :  * \brief Add a geometry to the container.
     302             :  *
     303             :  * Some subclasses of OGRGeometryCollection restrict the types of geometry
     304             :  * that can be added, and may return an error.  The passed geometry is cloned
     305             :  * to make an internal copy.
     306             :  *
     307             :  * There is no SFCOM analog to this method.
     308             :  *
     309             :  * This method is the same as the C function OGR_G_AddGeometry().
     310             :  *
     311             :  * @param poNewGeom geometry to add to the container.
     312             :  *
     313             :  * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
     314             :  * the geometry type is illegal for the type of geometry container.
     315             :  */
     316             : 
     317        1737 : OGRErr OGRGeometryCollection::addGeometry(const OGRGeometry *poNewGeom)
     318             : 
     319             : {
     320        1737 :     OGRGeometry *poClone = poNewGeom->clone();
     321        1737 :     if (poClone == nullptr)
     322           0 :         return OGRERR_FAILURE;
     323             : 
     324        1737 :     const OGRErr eErr = addGeometryDirectly(poClone);
     325        1737 :     if (eErr != OGRERR_NONE)
     326           2 :         delete poClone;
     327             : 
     328        1737 :     return eErr;
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                        addGeometryDirectly()                         */
     333             : /*                                                                      */
     334             : /*      Add a new geometry to a collection.  Subclasses should          */
     335             : /*      override this to verify the type of the new geometry, and       */
     336             : /*      then call this method to actually add it.                       */
     337             : /************************************************************************/
     338             : 
     339             : /**
     340             :  * \brief Add a geometry directly to the container.
     341             :  *
     342             :  * Some subclasses of OGRGeometryCollection restrict the types of geometry
     343             :  * that can be added, and may return an error.  Ownership of the passed
     344             :  * geometry is taken by the container rather than cloning as addGeometry()
     345             :  * does, but only if the method is successful. If the method fails, ownership
     346             :  * still belongs to the caller.
     347             :  *
     348             :  * This method is the same as the C function OGR_G_AddGeometryDirectly().
     349             :  *
     350             :  * There is no SFCOM analog to this method.
     351             :  *
     352             :  * @param poNewGeom geometry to add to the container.
     353             :  *
     354             :  * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
     355             :  * the geometry type is illegal for the type of geometry container.
     356             :  */
     357             : 
     358       51127 : OGRErr OGRGeometryCollection::addGeometryDirectly(OGRGeometry *poNewGeom)
     359             : 
     360             : {
     361       51127 :     if (!isCompatibleSubType(poNewGeom->getGeometryType()))
     362           4 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
     363             : 
     364       51123 :     HomogenizeDimensionalityWith(poNewGeom);
     365             : 
     366             :     OGRGeometry **papoNewGeoms = static_cast<OGRGeometry **>(
     367       51123 :         VSI_REALLOC_VERBOSE(papoGeoms, sizeof(void *) * (nGeomCount + 1)));
     368       51122 :     if (papoNewGeoms == nullptr)
     369           0 :         return OGRERR_FAILURE;
     370             : 
     371       51122 :     papoGeoms = papoNewGeoms;
     372       51122 :     papoGeoms[nGeomCount] = poNewGeom;
     373             : 
     374       51122 :     nGeomCount++;
     375             : 
     376       51122 :     return OGRERR_NONE;
     377             : }
     378             : 
     379             : /************************************************************************/
     380             : /*                            addGeometry()                             */
     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.
     388             :  *
     389             :  * There is no SFCOM analog to this method.
     390             :  *
     391             :  * @param geom geometry to add to the container.
     392             :  *
     393             :  * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
     394             :  * the geometry type is illegal for the type of geometry container.
     395             :  */
     396             : 
     397         936 : OGRErr OGRGeometryCollection::addGeometry(std::unique_ptr<OGRGeometry> geom)
     398             : {
     399         936 :     OGRGeometry *poGeom = geom.release();
     400         936 :     OGRErr eErr = addGeometryDirectly(poGeom);
     401         936 :     if (eErr != OGRERR_NONE)
     402           0 :         delete poGeom;
     403         936 :     return eErr;
     404             : }
     405             : 
     406             : /************************************************************************/
     407             : /*                           removeGeometry()                           */
     408             : /************************************************************************/
     409             : 
     410             : /**
     411             :  * \brief Remove a geometry from the container.
     412             :  *
     413             :  * Removing a geometry will cause the geometry count to drop by one, and all
     414             :  * "higher" geometries will shuffle down one in index.
     415             :  *
     416             :  * There is no SFCOM analog to this method.
     417             :  *
     418             :  * This method is the same as the C function OGR_G_RemoveGeometry().
     419             :  *
     420             :  * @param iGeom the index of the geometry to delete.  A value of -1 is a
     421             :  * special flag meaning that all geometries should be removed.
     422             :  *
     423             :  * @param bDelete if TRUE the geometry will be deallocated, otherwise it will
     424             :  * not.  The default is TRUE as the container is considered to own the
     425             :  * geometries in it.
     426             :  *
     427             :  * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
     428             :  * out of range.
     429             :  */
     430             : 
     431       19249 : OGRErr OGRGeometryCollection::removeGeometry(int iGeom, int bDelete)
     432             : 
     433             : {
     434       19249 :     if (iGeom < -1 || iGeom >= nGeomCount)
     435           4 :         return OGRERR_FAILURE;
     436             : 
     437             :     // Special case.
     438       19245 :     if (iGeom == -1)
     439             :     {
     440           7 :         while (nGeomCount > 0)
     441           5 :             removeGeometry(nGeomCount - 1, bDelete);
     442           2 :         return OGRERR_NONE;
     443             :     }
     444             : 
     445       19243 :     if (bDelete)
     446          63 :         delete papoGeoms[iGeom];
     447             : 
     448       19243 :     memmove(papoGeoms + iGeom, papoGeoms + iGeom + 1,
     449       19243 :             sizeof(void *) * (nGeomCount - iGeom - 1));
     450             : 
     451       19243 :     nGeomCount--;
     452             : 
     453       19243 :     return OGRERR_NONE;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                              WkbSize()                               */
     458             : /*                                                                      */
     459             : /*      Return the size of this object in well known binary             */
     460             : /*      representation including the byte order, and type information.  */
     461             : /************************************************************************/
     462             : 
     463        9368 : size_t OGRGeometryCollection::WkbSize() const
     464             : 
     465             : {
     466        9368 :     size_t nSize = 9;
     467             : 
     468       22751 :     for (const auto &poGeom : *this)
     469             :     {
     470       13383 :         nSize += poGeom->WkbSize();
     471             :     }
     472             : 
     473        9368 :     return nSize;
     474             : }
     475             : 
     476             : /************************************************************************/
     477             : /*                       importFromWkbInternal()                        */
     478             : /************************************************************************/
     479             : 
     480             : //! @cond Doxygen_Suppress
     481        8393 : OGRErr OGRGeometryCollection::importFromWkbInternal(
     482             :     const unsigned char *pabyData, size_t nSize, int nRecLevel,
     483             :     OGRwkbVariant eWkbVariant, size_t &nBytesConsumedOut)
     484             : 
     485             : {
     486        8393 :     nBytesConsumedOut = 0;
     487             :     // Arbitrary value, but certainly large enough for reasonable use cases.
     488        8393 :     if (nRecLevel == 32)
     489             :     {
     490           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     491             :                  "Too many recursion levels (%d) while parsing WKB geometry.",
     492             :                  nRecLevel);
     493           1 :         return OGRERR_CORRUPT_DATA;
     494             :     }
     495             : 
     496        8392 :     OGRwkbByteOrder eByteOrder = wkbXDR;
     497        8392 :     size_t nDataOffset = 0;
     498        8392 :     int nGeomCountNew = 0;
     499        8392 :     OGRErr eErr = importPreambleOfCollectionFromWkb(pabyData, nSize,
     500             :                                                     nDataOffset, eByteOrder, 9,
     501             :                                                     nGeomCountNew, eWkbVariant);
     502             : 
     503        8392 :     if (eErr != OGRERR_NONE)
     504         324 :         return eErr;
     505             : 
     506        8068 :     CPLAssert(nGeomCount == 0);
     507        8068 :     nGeomCount = nGeomCountNew;
     508             : 
     509             :     // coverity[tainted_data]
     510        8068 :     papoGeoms = static_cast<OGRGeometry **>(
     511        8068 :         VSI_CALLOC_VERBOSE(sizeof(void *), nGeomCount));
     512        8068 :     if (nGeomCount != 0 && papoGeoms == nullptr)
     513             :     {
     514           0 :         nGeomCount = 0;
     515           0 :         return OGRERR_NOT_ENOUGH_MEMORY;
     516             :     }
     517             : 
     518             :     /* -------------------------------------------------------------------- */
     519             :     /*      Get the Geoms.                                                  */
     520             :     /* -------------------------------------------------------------------- */
     521       17530 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
     522             :     {
     523             :         // Parses sub-geometry.
     524       10353 :         const unsigned char *pabySubData = pabyData + nDataOffset;
     525       10353 :         if (nSize < 9 && nSize != static_cast<size_t>(-1))
     526         891 :             return OGRERR_NOT_ENOUGH_DATA;
     527             : 
     528       10341 :         OGRwkbGeometryType eSubGeomType = wkbUnknown;
     529       10341 :         eErr = OGRReadWKBGeometryType(pabySubData, eWkbVariant, &eSubGeomType);
     530       10341 :         if (eErr != OGRERR_NONE)
     531         372 :             return eErr;
     532             : 
     533        9969 :         if (!isCompatibleSubType(eSubGeomType))
     534             :         {
     535           1 :             nGeomCount = iGeom;
     536           1 :             CPLDebug(
     537             :                 "OGR",
     538             :                 "Cannot add geometry of type (%d) to geometry of type (%d)",
     539           1 :                 eSubGeomType, getGeometryType());
     540           1 :             return OGRERR_CORRUPT_DATA;
     541             :         }
     542             : 
     543        9968 :         OGRGeometry *poSubGeom = nullptr;
     544        9968 :         size_t nSubGeomBytesConsumed = 0;
     545        9968 :         if (OGR_GT_IsSubClassOf(eSubGeomType, wkbGeometryCollection))
     546             :         {
     547         161 :             poSubGeom = OGRGeometryFactory::createGeometry(eSubGeomType);
     548         161 :             if (poSubGeom == nullptr)
     549           0 :                 eErr = OGRERR_FAILURE;
     550             :             else
     551         161 :                 eErr = poSubGeom->toGeometryCollection()->importFromWkbInternal(
     552             :                     pabySubData, nSize, nRecLevel + 1, eWkbVariant,
     553             :                     nSubGeomBytesConsumed);
     554             :         }
     555             :         else
     556             :         {
     557        9807 :             eErr = OGRGeometryFactory::createFromWkb(
     558             :                 pabySubData, nullptr, &poSubGeom, nSize, eWkbVariant,
     559             :                 nSubGeomBytesConsumed);
     560             : 
     561        9807 :             if (eErr == OGRERR_NONE)
     562             :             {
     563             :                 // if this is a Z or M geom make sure the sub geoms are as well
     564        9333 :                 if (Is3D() && !poSubGeom->Is3D())
     565             :                 {
     566           0 :                     CPLDebug("OGR", "Promoting sub-geometry to 3D");
     567           0 :                     poSubGeom->set3D(TRUE);
     568             :                 }
     569             : 
     570        9333 :                 if (IsMeasured() && !poSubGeom->IsMeasured())
     571             :                 {
     572           0 :                     CPLDebug("OGR", "Promoting sub-geometry to Measured");
     573           0 :                     poSubGeom->setMeasured(TRUE);
     574             :                 }
     575             :             }
     576             :         }
     577             : 
     578        9968 :         if (eErr != OGRERR_NONE)
     579             :         {
     580         506 :             nGeomCount = iGeom;
     581         506 :             delete poSubGeom;
     582         506 :             return eErr;
     583             :         }
     584             : 
     585        9462 :         papoGeoms[iGeom] = poSubGeom;
     586             : 
     587        9462 :         if (papoGeoms[iGeom]->Is3D())
     588        5776 :             flags |= OGR_G_3D;
     589        9462 :         if (papoGeoms[iGeom]->IsMeasured())
     590        5678 :             flags |= OGR_G_MEASURED;
     591             : 
     592        9462 :         CPLAssert(nSubGeomBytesConsumed > 0);
     593        9462 :         if (nSize != static_cast<size_t>(-1))
     594             :         {
     595        9457 :             CPLAssert(nSize >= nSubGeomBytesConsumed);
     596        9457 :             nSize -= nSubGeomBytesConsumed;
     597             :         }
     598             : 
     599        9462 :         nDataOffset += nSubGeomBytesConsumed;
     600             :     }
     601        7177 :     nBytesConsumedOut = nDataOffset;
     602             : 
     603        7177 :     return OGRERR_NONE;
     604             : }
     605             : 
     606             : //! @endcond
     607             : 
     608             : /************************************************************************/
     609             : /*                           importFromWkb()                            */
     610             : /*                                                                      */
     611             : /*      Initialize from serialized stream in well known binary          */
     612             : /*      format.                                                         */
     613             : /************************************************************************/
     614             : 
     615        4894 : OGRErr OGRGeometryCollection::importFromWkb(const unsigned char *pabyData,
     616             :                                             size_t nSize,
     617             :                                             OGRwkbVariant eWkbVariant,
     618             :                                             size_t &nBytesConsumedOut)
     619             : 
     620             : {
     621        4894 :     return importFromWkbInternal(pabyData, nSize, 0, eWkbVariant,
     622        4894 :                                  nBytesConsumedOut);
     623             : }
     624             : 
     625             : /************************************************************************/
     626             : /*                            exportToWkb()                             */
     627             : /*                                                                      */
     628             : /*      Build a well known binary representation of this object.        */
     629             : /************************************************************************/
     630             : 
     631             : OGRErr
     632        9202 : OGRGeometryCollection::exportToWkb(unsigned char *pabyData,
     633             :                                    const OGRwkbExportOptions *psOptions) const
     634             : 
     635             : {
     636        9202 :     if (psOptions == nullptr)
     637             :     {
     638             :         static const OGRwkbExportOptions defaultOptions;
     639           0 :         psOptions = &defaultOptions;
     640             :     }
     641             : 
     642        9202 :     OGRwkbExportOptions sOptions(*psOptions);
     643             : 
     644       13933 :     if (sOptions.eWkbVariant == wkbVariantOldOgc &&
     645        4732 :         (wkbFlatten(getGeometryType()) == wkbMultiCurve ||
     646        4721 :          wkbFlatten(getGeometryType()) == wkbMultiSurface))
     647             :     {
     648             :         // Does not make sense for new geometries, so patch it.
     649          19 :         sOptions.eWkbVariant = wkbVariantIso;
     650             :     }
     651             : 
     652             :     /* -------------------------------------------------------------------- */
     653             :     /*      Set the byte order.                                             */
     654             :     /* -------------------------------------------------------------------- */
     655        9201 :     pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(
     656             :         static_cast<unsigned char>(sOptions.eByteOrder));
     657             : 
     658             :     /* -------------------------------------------------------------------- */
     659             :     /*      Set the geometry feature type, ensuring that 3D flag is         */
     660             :     /*      preserved.                                                      */
     661             :     /* -------------------------------------------------------------------- */
     662        9201 :     GUInt32 nGType = getGeometryType();
     663             : 
     664        9201 :     if (sOptions.eWkbVariant == wkbVariantIso)
     665        4485 :         nGType = getIsoGeometryType();
     666        4716 :     else if (sOptions.eWkbVariant == wkbVariantPostGIS1)
     667             :     {
     668           4 :         const bool bIs3D = wkbHasZ(static_cast<OGRwkbGeometryType>(nGType));
     669           4 :         nGType = wkbFlatten(nGType);
     670           4 :         if (nGType == wkbMultiCurve)
     671           0 :             nGType = POSTGIS15_MULTICURVE;
     672           4 :         else if (nGType == wkbMultiSurface)
     673           0 :             nGType = POSTGIS15_MULTISURFACE;
     674           4 :         if (bIs3D)
     675             :             // Yes, explicitly set wkb25DBit.
     676           1 :             nGType =
     677           1 :                 static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
     678             :     }
     679             : 
     680        9201 :     if (OGR_SWAP(sOptions.eByteOrder))
     681             :     {
     682           6 :         nGType = CPL_SWAP32(nGType);
     683             :     }
     684             : 
     685        9201 :     memcpy(pabyData + 1, &nGType, 4);
     686             : 
     687             :     /* -------------------------------------------------------------------- */
     688             :     /*      Copy in the raw data.                                           */
     689             :     /* -------------------------------------------------------------------- */
     690        9201 :     if (OGR_SWAP(sOptions.eByteOrder))
     691             :     {
     692           6 :         int nCount = CPL_SWAP32(nGeomCount);
     693           6 :         memcpy(pabyData + 5, &nCount, 4);
     694             :     }
     695             :     else
     696             :     {
     697        9195 :         memcpy(pabyData + 5, &nGeomCount, 4);
     698             :     }
     699             : 
     700        9201 :     size_t nOffset = 9;
     701             : 
     702             :     /* ==================================================================== */
     703             :     /*      Serialize each of the Geoms.                                    */
     704             :     /* ==================================================================== */
     705        9201 :     int iGeom = 0;
     706       22340 :     for (auto &&poSubGeom : *this)
     707             :     {
     708       13139 :         poSubGeom->exportToWkb(pabyData + nOffset, &sOptions);
     709             :         // Should normally not happen if everyone else does its job,
     710             :         // but has happened sometimes. (#6332)
     711       13140 :         if (poSubGeom->getCoordinateDimension() != getCoordinateDimension())
     712             :         {
     713           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     714             :                      "Sub-geometry %d has coordinate dimension %d, "
     715             :                      "but container has %d",
     716           0 :                      iGeom, poSubGeom->getCoordinateDimension(),
     717           0 :                      getCoordinateDimension());
     718             :         }
     719             : 
     720       13139 :         nOffset += poSubGeom->WkbSize();
     721       13139 :         iGeom++;
     722             :     }
     723             : 
     724        9201 :     return OGRERR_NONE;
     725             : }
     726             : 
     727             : /************************************************************************/
     728             : /*                       importFromWktInternal()                        */
     729             : /************************************************************************/
     730             : 
     731         628 : OGRErr OGRGeometryCollection::importFromWktInternal(const char **ppszInput,
     732             :                                                     int nRecLevel)
     733             : 
     734             : {
     735             :     // Arbitrary value, but certainly large enough for reasonable usages.
     736         628 :     if (nRecLevel == 32)
     737             :     {
     738           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     739             :                  "Too many recursion levels (%d) while parsing WKT geometry.",
     740             :                  nRecLevel);
     741           1 :         return OGRERR_CORRUPT_DATA;
     742             :     }
     743             : 
     744         627 :     int bHasZ = FALSE;
     745         627 :     int bHasM = FALSE;
     746         627 :     bool bIsEmpty = false;
     747         627 :     OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
     748         627 :     if (eErr != OGRERR_NONE)
     749           4 :         return eErr;
     750         623 :     if (bHasZ)
     751         111 :         flags |= OGR_G_3D;
     752         623 :     if (bHasM)
     753          57 :         flags |= OGR_G_MEASURED;
     754         623 :     if (bIsEmpty)
     755          34 :         return OGRERR_NONE;
     756             : 
     757         589 :     char szToken[OGR_WKT_TOKEN_MAX] = {};
     758         589 :     const char *pszInput = *ppszInput;
     759             : 
     760             :     // Skip first '('.
     761         589 :     pszInput = OGRWktReadToken(pszInput, szToken);
     762             : 
     763             :     /* ==================================================================== */
     764             :     /*      Read each subgeometry in turn.                                  */
     765             :     /* ==================================================================== */
     766         685 :     do
     767             :     {
     768        1274 :         OGRGeometry *poGeom = nullptr;
     769             : 
     770             :         /* --------------------------------------------------------------------
     771             :          */
     772             :         /*      Get the first token, which should be the geometry type. */
     773             :         /* --------------------------------------------------------------------
     774             :          */
     775        1274 :         OGRWktReadToken(pszInput, szToken);
     776             : 
     777             :         /* --------------------------------------------------------------------
     778             :          */
     779             :         /*      Do the import. */
     780             :         /* --------------------------------------------------------------------
     781             :          */
     782        1274 :         if (STARTS_WITH_CI(szToken, "GEOMETRYCOLLECTION"))
     783             :         {
     784         104 :             OGRGeometryCollection *poGC = new OGRGeometryCollection();
     785         104 :             poGeom = poGC;
     786         104 :             eErr = poGC->importFromWktInternal(&pszInput, nRecLevel + 1);
     787             :         }
     788             :         else
     789             :             eErr =
     790        1170 :                 OGRGeometryFactory::createFromWkt(&pszInput, nullptr, &poGeom);
     791             : 
     792        1274 :         if (eErr == OGRERR_NONE)
     793             :         {
     794             :             // If this has M, but not Z, it is an error if poGeom does
     795             :             // not have M.
     796        1230 :             if (!Is3D() && IsMeasured() && !poGeom->IsMeasured())
     797           0 :                 eErr = OGRERR_CORRUPT_DATA;
     798             :             else
     799        1230 :                 eErr = addGeometryDirectly(poGeom);
     800             :         }
     801        1274 :         if (eErr != OGRERR_NONE)
     802             :         {
     803          44 :             delete poGeom;
     804          44 :             return eErr;
     805             :         }
     806             : 
     807             :         /* --------------------------------------------------------------------
     808             :          */
     809             :         /*      Read the delimiter following the ring. */
     810             :         /* --------------------------------------------------------------------
     811             :          */
     812             : 
     813        1230 :         pszInput = OGRWktReadToken(pszInput, szToken);
     814        1230 :     } while (szToken[0] == ',');
     815             : 
     816             :     /* -------------------------------------------------------------------- */
     817             :     /*      freak if we don't get a closing bracket.                        */
     818             :     /* -------------------------------------------------------------------- */
     819         545 :     if (szToken[0] != ')')
     820           2 :         return OGRERR_CORRUPT_DATA;
     821             : 
     822         543 :     *ppszInput = pszInput;
     823             : 
     824         543 :     return OGRERR_NONE;
     825             : }
     826             : 
     827             : /************************************************************************/
     828             : /*                           importFromWkt()                            */
     829             : /************************************************************************/
     830             : 
     831         524 : OGRErr OGRGeometryCollection::importFromWkt(const char **ppszInput)
     832             : 
     833             : {
     834         524 :     return importFromWktInternal(ppszInput, 0);
     835             : }
     836             : 
     837             : /************************************************************************/
     838             : /*                            exportToWkt()                             */
     839             : /*                                                                      */
     840             : /*      Translate this structure into its well known text format        */
     841             : /*      equivalent.                                                     */
     842             : /************************************************************************/
     843             : 
     844         275 : std::string OGRGeometryCollection::exportToWkt(const OGRWktOptions &opts,
     845             :                                                OGRErr *err) const
     846             : {
     847         275 :     return exportToWktInternal(opts, err);
     848             : }
     849             : 
     850             : //! @cond Doxygen_Suppress
     851        1111 : std::string OGRGeometryCollection::exportToWktInternal(
     852             :     const OGRWktOptions &opts, OGRErr *err, const std::string &exclude) const
     853             : {
     854        1111 :     bool first = true;
     855        1111 :     const size_t excludeSize = exclude.size();
     856        2222 :     std::string wkt(getGeometryName());
     857        1111 :     wkt += wktTypeString(opts.variant);
     858             : 
     859             :     try
     860             :     {
     861        2645 :         for (int i = 0; i < nGeomCount; ++i)
     862             :         {
     863        1534 :             OGRGeometry *geom = papoGeoms[i];
     864        1534 :             OGRErr subgeomErr = OGRERR_NONE;
     865        1534 :             std::string tempWkt = geom->exportToWkt(opts, &subgeomErr);
     866        1534 :             if (subgeomErr != OGRERR_NONE)
     867             :             {
     868           0 :                 if (err)
     869           0 :                     *err = subgeomErr;
     870           0 :                 return std::string();
     871             :             }
     872             : 
     873             :             // For some strange reason we exclude the typename leader when using
     874             :             // some geometries as part of a collection.
     875        1534 :             if (excludeSize && (tempWkt.compare(0, excludeSize, exclude) == 0))
     876             :             {
     877        1023 :                 auto pos = tempWkt.find('(');
     878             :                 // We won't have an opening paren if the geom is empty.
     879        1023 :                 if (pos == std::string::npos)
     880          20 :                     continue;
     881        1003 :                 tempWkt = tempWkt.substr(pos);
     882             :             }
     883             : 
     884             :             // Also strange, we allow the inclusion of ISO-only geometries (see
     885             :             // OGRPolyhedralSurface) in a non-iso geometry collection.  In order
     886             :             // to facilitate this, we need to rip the ISO bit from the string.
     887        1514 :             if (opts.variant != wkbVariantIso)
     888             :             {
     889             :                 std::string::size_type pos;
     890         676 :                 if ((pos = tempWkt.find(" Z ")) != std::string::npos)
     891          11 :                     tempWkt.erase(pos + 1, 2);
     892         665 :                 else if ((pos = tempWkt.find(" M ")) != std::string::npos)
     893           0 :                     tempWkt.erase(pos + 1, 2);
     894         665 :                 else if ((pos = tempWkt.find(" ZM ")) != std::string::npos)
     895           0 :                     tempWkt.erase(pos + 1, 3);
     896             :             }
     897             : 
     898        1514 :             if (first)
     899         984 :                 wkt += '(';
     900             :             else
     901         530 :                 wkt += ',';
     902        1514 :             first = false;
     903        1514 :             wkt += tempWkt;
     904             :         }
     905             : 
     906        1111 :         if (err)
     907         994 :             *err = OGRERR_NONE;
     908        1111 :         if (first)
     909         127 :             wkt += "EMPTY";
     910             :         else
     911         984 :             wkt += ')';
     912        1111 :         return wkt;
     913             :     }
     914           0 :     catch (const std::bad_alloc &e)
     915             :     {
     916           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     917           0 :         if (err)
     918           0 :             *err = OGRERR_FAILURE;
     919           0 :         return std::string();
     920             :     }
     921             : }
     922             : 
     923             : //! @endcond
     924             : 
     925             : /************************************************************************/
     926             : /*                            getEnvelope()                             */
     927             : /************************************************************************/
     928             : 
     929        6043 : void OGRGeometryCollection::getEnvelope(OGREnvelope *psEnvelope) const
     930             : 
     931             : {
     932        6043 :     OGREnvelope3D oEnv3D;
     933        6043 :     getEnvelope(&oEnv3D);
     934        6043 :     psEnvelope->MinX = oEnv3D.MinX;
     935        6043 :     psEnvelope->MinY = oEnv3D.MinY;
     936        6043 :     psEnvelope->MaxX = oEnv3D.MaxX;
     937        6043 :     psEnvelope->MaxY = oEnv3D.MaxY;
     938        6043 : }
     939             : 
     940             : /************************************************************************/
     941             : /*                            getEnvelope()                             */
     942             : /************************************************************************/
     943             : 
     944        7128 : void OGRGeometryCollection::getEnvelope(OGREnvelope3D *psEnvelope) const
     945             : 
     946             : {
     947        7128 :     OGREnvelope3D oGeomEnv;
     948        7128 :     bool bExtentSet = false;
     949             : 
     950        7128 :     *psEnvelope = OGREnvelope3D();
     951       19080 :     for (auto &&poSubGeom : *this)
     952             :     {
     953       11952 :         if (!poSubGeom->IsEmpty())
     954             :         {
     955       11945 :             bExtentSet = true;
     956       11945 :             poSubGeom->getEnvelope(&oGeomEnv);
     957       11945 :             psEnvelope->Merge(oGeomEnv);
     958             :         }
     959             :     }
     960             : 
     961        7128 :     if (!bExtentSet)
     962             :     {
     963             :         // To be backward compatible when called on empty geom
     964          13 :         psEnvelope->MinX = 0.0;
     965          13 :         psEnvelope->MinY = 0.0;
     966          13 :         psEnvelope->MinZ = 0.0;
     967          13 :         psEnvelope->MaxX = 0.0;
     968          13 :         psEnvelope->MaxY = 0.0;
     969          13 :         psEnvelope->MaxZ = 0.0;
     970             :     }
     971        7128 : }
     972             : 
     973             : /************************************************************************/
     974             : /*                               Equals()                               */
     975             : /************************************************************************/
     976             : 
     977        3818 : OGRBoolean OGRGeometryCollection::Equals(const OGRGeometry *poOther) const
     978             : 
     979             : {
     980        3818 :     if (poOther == this)
     981           2 :         return TRUE;
     982             : 
     983        3816 :     if (poOther->getGeometryType() != getGeometryType())
     984           0 :         return FALSE;
     985             : 
     986        3816 :     if (IsEmpty() && poOther->IsEmpty())
     987          12 :         return TRUE;
     988             : 
     989        3804 :     auto poOGC = poOther->toGeometryCollection();
     990        3804 :     if (getNumGeometries() != poOGC->getNumGeometries())
     991           2 :         return FALSE;
     992             : 
     993             :     // TODO(schwehr): Should test the SRS.
     994             : 
     995        8341 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
     996             :     {
     997        4540 :         if (!getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)))
     998           1 :             return FALSE;
     999             :     }
    1000             : 
    1001        3801 :     return TRUE;
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                             transform()                              */
    1006             : /************************************************************************/
    1007             : 
    1008         148 : OGRErr OGRGeometryCollection::transform(OGRCoordinateTransformation *poCT)
    1009             : 
    1010             : {
    1011         148 :     int iGeom = 0;
    1012         332 :     for (auto &&poSubGeom : *this)
    1013             :     {
    1014         184 :         const OGRErr eErr = poSubGeom->transform(poCT);
    1015         184 :         if (eErr != OGRERR_NONE)
    1016             :         {
    1017           0 :             if (iGeom != 0)
    1018             :             {
    1019           0 :                 CPLDebug("OGR",
    1020             :                          "OGRGeometryCollection::transform() failed for a "
    1021             :                          "geometry other than the first, meaning some "
    1022             :                          "geometries are transformed and some are not.");
    1023             : 
    1024           0 :                 return OGRERR_FAILURE;
    1025             :             }
    1026             : 
    1027           0 :             return eErr;
    1028             :         }
    1029         184 :         iGeom++;
    1030             :     }
    1031             : 
    1032         148 :     assignSpatialReference(poCT->GetTargetCS());
    1033             : 
    1034         148 :     return OGRERR_NONE;
    1035             : }
    1036             : 
    1037             : /************************************************************************/
    1038             : /*                             closeRings()                             */
    1039             : /************************************************************************/
    1040             : 
    1041         255 : void OGRGeometryCollection::closeRings()
    1042             : 
    1043             : {
    1044         754 :     for (auto &&poSubGeom : *this)
    1045             :     {
    1046         499 :         if (OGR_GT_IsSubClassOf(wkbFlatten(poSubGeom->getGeometryType()),
    1047         499 :                                 wkbCurvePolygon))
    1048             :         {
    1049         219 :             OGRCurvePolygon *poPoly = poSubGeom->toCurvePolygon();
    1050         219 :             poPoly->closeRings();
    1051             :         }
    1052             :     }
    1053         255 : }
    1054             : 
    1055             : /************************************************************************/
    1056             : /*                       setCoordinateDimension()                       */
    1057             : /************************************************************************/
    1058             : 
    1059         475 : void OGRGeometryCollection::setCoordinateDimension(int nNewDimension)
    1060             : 
    1061             : {
    1062        1357 :     for (auto &&poSubGeom : *this)
    1063             :     {
    1064         882 :         poSubGeom->setCoordinateDimension(nNewDimension);
    1065             :     }
    1066             : 
    1067         475 :     OGRGeometry::setCoordinateDimension(nNewDimension);
    1068         475 : }
    1069             : 
    1070       54771 : void OGRGeometryCollection::set3D(OGRBoolean bIs3D)
    1071             : {
    1072       57648 :     for (auto &&poSubGeom : *this)
    1073             :     {
    1074        2877 :         poSubGeom->set3D(bIs3D);
    1075             :     }
    1076             : 
    1077       54771 :     OGRGeometry::set3D(bIs3D);
    1078       54771 : }
    1079             : 
    1080       44189 : void OGRGeometryCollection::setMeasured(OGRBoolean bIsMeasured)
    1081             : {
    1082       47750 :     for (auto &&poSubGeom : *this)
    1083             :     {
    1084        3561 :         poSubGeom->setMeasured(bIsMeasured);
    1085             :     }
    1086             : 
    1087       44189 :     OGRGeometry::setMeasured(bIsMeasured);
    1088       44189 : }
    1089             : 
    1090             : /************************************************************************/
    1091             : /*                              get_Length()                            */
    1092             : /************************************************************************/
    1093             : 
    1094             : /**
    1095             :  * \brief Compute the length of a multicurve.
    1096             :  *
    1097             :  * The length is computed as the sum of the length of all members
    1098             :  * in this collection.
    1099             :  *
    1100             :  * @note No warning will be issued if a member of the collection does not
    1101             :  *       support the get_Length method.
    1102             :  *
    1103             :  * @return computed length.
    1104             :  */
    1105             : 
    1106           9 : double OGRGeometryCollection::get_Length() const
    1107             : {
    1108           9 :     double dfLength = 0.0;
    1109          26 :     for (auto &&poSubGeom : *this)
    1110             :     {
    1111             :         const OGRwkbGeometryType eType =
    1112          17 :             wkbFlatten(poSubGeom->getGeometryType());
    1113          17 :         if (OGR_GT_IsCurve(eType))
    1114             :         {
    1115          13 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1116          13 :             dfLength += poCurve->get_Length();
    1117             :         }
    1118           4 :         else if (OGR_GT_IsSubClassOf(eType, wkbMultiCurve) ||
    1119             :                  eType == wkbGeometryCollection)
    1120             :         {
    1121             :             const OGRGeometryCollection *poColl =
    1122           2 :                 poSubGeom->toGeometryCollection();
    1123           2 :             dfLength += poColl->get_Length();
    1124             :         }
    1125             :     }
    1126             : 
    1127           9 :     return dfLength;
    1128             : }
    1129             : 
    1130             : /************************************************************************/
    1131             : /*                              get_Area()                              */
    1132             : /************************************************************************/
    1133             : 
    1134             : /**
    1135             :  * \brief Compute area of geometry collection.
    1136             :  *
    1137             :  * The area is computed as the sum of the areas of all members
    1138             :  * in this collection.
    1139             :  *
    1140             :  * @note No warning will be issued if a member of the collection does not
    1141             :  *       support the get_Area method.
    1142             :  *
    1143             :  * @return computed area.
    1144             :  */
    1145             : 
    1146          11 : double OGRGeometryCollection::get_Area() const
    1147             : {
    1148          11 :     double dfArea = 0.0;
    1149          31 :     for (auto &&poSubGeom : *this)
    1150             :     {
    1151          20 :         OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
    1152          20 :         if (OGR_GT_IsSurface(eType))
    1153             :         {
    1154          15 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1155          15 :             dfArea += poSurface->get_Area();
    1156             :         }
    1157           5 :         else if (OGR_GT_IsCurve(eType))
    1158             :         {
    1159           1 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1160           1 :             dfArea += poCurve->get_Area();
    1161             :         }
    1162           4 :         else if (OGR_GT_IsSubClassOf(eType, wkbMultiSurface) ||
    1163             :                  eType == wkbGeometryCollection)
    1164             :         {
    1165           2 :             dfArea += poSubGeom->toGeometryCollection()->get_Area();
    1166             :         }
    1167             :     }
    1168             : 
    1169          11 :     return dfArea;
    1170             : }
    1171             : 
    1172             : /************************************************************************/
    1173             : /*                        get_GeodesicArea()                            */
    1174             : /************************************************************************/
    1175             : 
    1176             : /**
    1177             :  * \brief Compute area of geometry collection, considered as a surface on
    1178             :  * the underlying ellipsoid of the SRS attached to the geometry.
    1179             :  *
    1180             :  * The returned area will always be in square meters, and assumes that
    1181             :  * polygon edges describe geodesic lines on the ellipsoid.
    1182             :  *
    1183             :  * If the geometry' SRS is not a geographic one, geometries are reprojected to
    1184             :  * the underlying geographic SRS of the geometry' SRS.
    1185             :  * OGRSpatialReference::GetDataAxisToSRSAxisMapping() is honored.
    1186             :  *
    1187             :  * The area is computed as the sum of the areas of all members
    1188             :  * in this collection.
    1189             :  *
    1190             :  * @note No warning will be issued if a member of the collection does not
    1191             :  *       support the get_GeodesicArea method.
    1192             :  *
    1193             :  * @param poSRSOverride If not null, overrides OGRGeometry::getSpatialReference()
    1194             :  * @return the area of the geometry in square meters, or a negative value in case
    1195             :  * of error.
    1196             :  *
    1197             :  * @see get_Area() for an alternative method returning areas computed in
    1198             :  * 2D Cartesian space.
    1199             :  *
    1200             :  * @since GDAL 3.9
    1201             :  */
    1202           6 : double OGRGeometryCollection::get_GeodesicArea(
    1203             :     const OGRSpatialReference *poSRSOverride) const
    1204             : {
    1205           6 :     if (!poSRSOverride)
    1206           4 :         poSRSOverride = getSpatialReference();
    1207             : 
    1208           6 :     double dfArea = 0.0;
    1209          11 :     for (auto &&poSubGeom : *this)
    1210             :     {
    1211           8 :         OGRwkbGeometryType eType = wkbFlatten(poSubGeom->getGeometryType());
    1212           8 :         if (OGR_GT_IsSurface(eType))
    1213             :         {
    1214           4 :             const OGRSurface *poSurface = poSubGeom->toSurface();
    1215             :             const double dfLocalArea =
    1216           4 :                 poSurface->get_GeodesicArea(poSRSOverride);
    1217           4 :             if (dfLocalArea < 0)
    1218           1 :                 return dfLocalArea;
    1219           3 :             dfArea += dfLocalArea;
    1220             :         }
    1221           4 :         else if (OGR_GT_IsCurve(eType))
    1222             :         {
    1223           2 :             const OGRCurve *poCurve = poSubGeom->toCurve();
    1224           2 :             const double dfLocalArea = poCurve->get_GeodesicArea(poSRSOverride);
    1225           2 :             if (dfLocalArea < 0)
    1226           1 :                 return dfLocalArea;
    1227           1 :             dfArea += dfLocalArea;
    1228             :         }
    1229           2 :         else if (OGR_GT_IsSubClassOf(eType, wkbMultiSurface) ||
    1230             :                  eType == wkbGeometryCollection)
    1231             :         {
    1232             :             const double dfLocalArea =
    1233           2 :                 poSubGeom->toGeometryCollection()->get_GeodesicArea(
    1234           2 :                     poSRSOverride);
    1235           2 :             if (dfLocalArea < 0)
    1236           1 :                 return dfLocalArea;
    1237           1 :             dfArea += dfLocalArea;
    1238             :         }
    1239             :     }
    1240             : 
    1241           3 :     return dfArea;
    1242             : }
    1243             : 
    1244             : /************************************************************************/
    1245             : /*                               IsEmpty()                              */
    1246             : /************************************************************************/
    1247             : 
    1248       18592 : OGRBoolean OGRGeometryCollection::IsEmpty() const
    1249             : {
    1250       18735 :     for (auto &&poSubGeom : *this)
    1251             :     {
    1252       17883 :         if (poSubGeom->IsEmpty() == FALSE)
    1253       17740 :             return FALSE;
    1254             :     }
    1255         852 :     return TRUE;
    1256             : }
    1257             : 
    1258             : /************************************************************************/
    1259             : /*                       assignSpatialReference()                       */
    1260             : /************************************************************************/
    1261             : 
    1262       46176 : void OGRGeometryCollection::assignSpatialReference(
    1263             :     const OGRSpatialReference *poSR)
    1264             : {
    1265       46176 :     OGRGeometry::assignSpatialReference(poSR);
    1266      108731 :     for (auto &&poSubGeom : *this)
    1267             :     {
    1268       62555 :         poSubGeom->assignSpatialReference(poSR);
    1269             :     }
    1270       46176 : }
    1271             : 
    1272             : /************************************************************************/
    1273             : /*              OGRGeometryCollection::segmentize()                     */
    1274             : /************************************************************************/
    1275             : 
    1276          12 : void OGRGeometryCollection::segmentize(double dfMaxLength)
    1277             : {
    1278          24 :     for (auto &&poSubGeom : *this)
    1279             :     {
    1280          12 :         poSubGeom->segmentize(dfMaxLength);
    1281             :     }
    1282          12 : }
    1283             : 
    1284             : /************************************************************************/
    1285             : /*                               swapXY()                               */
    1286             : /************************************************************************/
    1287             : 
    1288          30 : void OGRGeometryCollection::swapXY()
    1289             : {
    1290          76 :     for (auto &&poSubGeom : *this)
    1291             :     {
    1292          46 :         poSubGeom->swapXY();
    1293             :     }
    1294          30 : }
    1295             : 
    1296             : /************************************************************************/
    1297             : /*                          isCompatibleSubType()                       */
    1298             : /************************************************************************/
    1299             : 
    1300             : /** Returns whether a geometry of the specified geometry type can be a
    1301             :  * member of this collection.
    1302             :  *
    1303             :  * @param eSubType type of the potential member
    1304             :  * @return TRUE or FALSE
    1305             :  */
    1306             : 
    1307        8903 : OGRBoolean OGRGeometryCollection::isCompatibleSubType(
    1308             :     CPL_UNUSED OGRwkbGeometryType eSubType) const
    1309             : {
    1310             :     // Accept all geometries as sub-geometries.
    1311        8903 :     return TRUE;
    1312             : }
    1313             : 
    1314             : /************************************************************************/
    1315             : /*                         hasCurveGeometry()                           */
    1316             : /************************************************************************/
    1317             : 
    1318        2352 : OGRBoolean OGRGeometryCollection::hasCurveGeometry(int bLookForNonLinear) const
    1319             : {
    1320        6039 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
    1321             :     {
    1322        3742 :         if (papoGeoms[iGeom]->hasCurveGeometry(bLookForNonLinear))
    1323          55 :             return TRUE;
    1324             :     }
    1325        2297 :     return FALSE;
    1326             : }
    1327             : 
    1328             : /************************************************************************/
    1329             : /*                         getLinearGeometry()                        */
    1330             : /************************************************************************/
    1331             : 
    1332             : OGRGeometry *
    1333         321 : OGRGeometryCollection::getLinearGeometry(double dfMaxAngleStepSizeDegrees,
    1334             :                                          const char *const *papszOptions) const
    1335             : {
    1336             :     OGRGeometryCollection *poGC =
    1337         321 :         OGRGeometryFactory::createGeometry(OGR_GT_GetLinear(getGeometryType()))
    1338         321 :             ->toGeometryCollection();
    1339         321 :     if (poGC == nullptr)
    1340           0 :         return nullptr;
    1341         321 :     poGC->assignSpatialReference(getSpatialReference());
    1342         672 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
    1343             :     {
    1344         702 :         OGRGeometry *poSubGeom = papoGeoms[iGeom]->getLinearGeometry(
    1345         351 :             dfMaxAngleStepSizeDegrees, papszOptions);
    1346         351 :         poGC->addGeometryDirectly(poSubGeom);
    1347             :     }
    1348         321 :     return poGC;
    1349             : }
    1350             : 
    1351             : /************************************************************************/
    1352             : /*                             getCurveGeometry()                       */
    1353             : /************************************************************************/
    1354             : 
    1355             : OGRGeometry *
    1356          15 : OGRGeometryCollection::getCurveGeometry(const char *const *papszOptions) const
    1357             : {
    1358             :     OGRGeometryCollection *poGC =
    1359          15 :         OGRGeometryFactory::createGeometry(OGR_GT_GetCurve(getGeometryType()))
    1360          15 :             ->toGeometryCollection();
    1361          15 :     if (poGC == nullptr)
    1362           0 :         return nullptr;
    1363          15 :     poGC->assignSpatialReference(getSpatialReference());
    1364          15 :     bool bHasCurveGeometry = false;
    1365         162 :     for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
    1366             :     {
    1367             :         OGRGeometry *poSubGeom =
    1368         147 :             papoGeoms[iGeom]->getCurveGeometry(papszOptions);
    1369         147 :         if (poSubGeom->hasCurveGeometry())
    1370           7 :             bHasCurveGeometry = true;
    1371         147 :         poGC->addGeometryDirectly(poSubGeom);
    1372             :     }
    1373          15 :     if (!bHasCurveGeometry)
    1374             :     {
    1375          10 :         delete poGC;
    1376          10 :         return clone();
    1377             :     }
    1378           5 :     return poGC;
    1379             : }
    1380             : 
    1381             : /************************************************************************/
    1382             : /*                      TransferMembersAndDestroy()                     */
    1383             : /************************************************************************/
    1384             : 
    1385             : //! @cond Doxygen_Suppress
    1386             : OGRGeometryCollection *
    1387         230 : OGRGeometryCollection::TransferMembersAndDestroy(OGRGeometryCollection *poSrc,
    1388             :                                                  OGRGeometryCollection *poDst)
    1389             : {
    1390         230 :     poDst->assignSpatialReference(poSrc->getSpatialReference());
    1391         230 :     poDst->set3D(poSrc->Is3D());
    1392         230 :     poDst->setMeasured(poSrc->IsMeasured());
    1393         230 :     poDst->nGeomCount = poSrc->nGeomCount;
    1394         230 :     poDst->papoGeoms = poSrc->papoGeoms;
    1395         230 :     poSrc->nGeomCount = 0;
    1396         230 :     poSrc->papoGeoms = nullptr;
    1397         230 :     delete poSrc;
    1398         230 :     return poDst;
    1399             : }
    1400             : 
    1401             : //! @endcond
    1402             : 
    1403             : /************************************************************************/
    1404             : /*                        CastToGeometryCollection()                    */
    1405             : /************************************************************************/
    1406             : 
    1407             : /**
    1408             :  * \brief Cast to geometry collection.
    1409             :  *
    1410             :  * This methods cast a derived class of geometry collection to a plain
    1411             :  * geometry collection.
    1412             :  *
    1413             :  * The passed in geometry is consumed and a new one returned (or NULL in case
    1414             :  * of failure).
    1415             :  *
    1416             :  * @param poSrc the input geometry - ownership is passed to the method.
    1417             :  * @return new geometry.
    1418             :  * @since GDAL 2.2
    1419             :  */
    1420             : 
    1421             : OGRGeometryCollection *
    1422         111 : OGRGeometryCollection::CastToGeometryCollection(OGRGeometryCollection *poSrc)
    1423             : {
    1424         111 :     if (wkbFlatten(poSrc->getGeometryType()) == wkbGeometryCollection)
    1425           0 :         return poSrc;
    1426         111 :     return TransferMembersAndDestroy(poSrc, new OGRGeometryCollection());
    1427             : }

Generated by: LCOV version 1.14