LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_group.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 426 539 79.0 %
Date: 2026-06-19 21:24:00 Functions: 28 38 73.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_group.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  Implementation of GDALGroup class
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_float.h"
      15             : #include "gdal_multidim.h"
      16             : 
      17             : #include <list>
      18             : #include <map>
      19             : #include <queue>
      20             : #include <set>
      21             : 
      22             : /************************************************************************/
      23             : /*                             GDALGroup()                              */
      24             : /************************************************************************/
      25             : 
      26             : //! @cond Doxygen_Suppress
      27       14015 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
      28       14015 :                      const std::string &osContext)
      29       14015 :     : m_osName(osParentName.empty() ? "/" : osName),
      30             :       m_osFullName(
      31       28030 :           !osParentName.empty()
      32       21057 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
      33             :               : "/"),
      34       35072 :       m_osContext(osContext)
      35             : {
      36       14015 : }
      37             : 
      38             : //! @endcond
      39             : 
      40             : /************************************************************************/
      41             : /*                             ~GDALGroup()                             */
      42             : /************************************************************************/
      43             : 
      44             : GDALGroup::~GDALGroup() = default;
      45             : 
      46             : /************************************************************************/
      47             : /*                          GetMDArrayNames()                           */
      48             : /************************************************************************/
      49             : 
      50             : /** Return the list of multidimensional array names contained in this group.
      51             :  *
      52             :  * @note Driver implementation: optionally implemented. If implemented,
      53             :  * OpenMDArray() should also be implemented.
      54             :  *
      55             :  * Drivers known to implement it: MEM, netCDF.
      56             :  *
      57             :  * This is the same as the C function GDALGroupGetMDArrayNames().
      58             :  *
      59             :  * @param papszOptions Driver specific options determining how arrays
      60             :  * should be retrieved. Pass nullptr for default behavior.
      61             :  *
      62             :  * @return the array names.
      63             :  */
      64             : std::vector<std::string>
      65          10 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
      66             : {
      67          10 :     return {};
      68             : }
      69             : 
      70             : /************************************************************************/
      71             : /*                    GetMDArrayFullNamesRecursive()                    */
      72             : /************************************************************************/
      73             : 
      74             : /** Return the list of multidimensional array full names contained in this
      75             :  * group and its subgroups.
      76             :  *
      77             :  * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
      78             :  *
      79             :  * @param papszGroupDiscoverOptions Driver specific options determining how groups
      80             :  * should be retrieved. Pass nullptr for default behavior.
      81             :  * @param papszArrayDiscoverOptions Driver specific options determining how arrays
      82             :  * should be retrieved. Pass nullptr for default behavior.
      83             :  * @param papszGroupOpenOptions Driver specific options determining how a group should
      84             :  * be opened.  Pass nullptr for default behavior.
      85             :  *
      86             :  * @return the array full names.
      87             :  *
      88             :  * @since 3.11
      89             :  */
      90          31 : std::vector<std::string> GDALGroup::GetMDArrayFullNamesRecursive(
      91             :     CSLConstList papszGroupDiscoverOptions,
      92             :     CSLConstList papszArrayDiscoverOptions,
      93             :     CSLConstList papszGroupOpenOptions) const
      94             : {
      95          31 :     std::vector<std::string> ret;
      96          62 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
      97          31 :     stackGroups.push_back(nullptr);  // nullptr means this
      98          65 :     while (!stackGroups.empty())
      99             :     {
     100          68 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     101          34 :         stackGroups.erase(stackGroups.begin());
     102          34 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     103          85 :         for (const std::string &arrayName :
     104         204 :              poCurGroup->GetMDArrayNames(papszArrayDiscoverOptions))
     105             :         {
     106         170 :             std::string osFullName = poCurGroup->GetFullName();
     107          85 :             if (!osFullName.empty() && osFullName.back() != '/')
     108           3 :                 osFullName += '/';
     109          85 :             osFullName += arrayName;
     110          85 :             ret.push_back(std::move(osFullName));
     111             :         }
     112          34 :         auto insertionPoint = stackGroups.begin();
     113           3 :         for (const auto &osSubGroup :
     114          40 :              poCurGroup->GetGroupNames(papszGroupDiscoverOptions))
     115             :         {
     116             :             auto poSubGroup =
     117           6 :                 poCurGroup->OpenGroup(osSubGroup, papszGroupOpenOptions);
     118           3 :             if (poSubGroup)
     119           3 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     120             :         }
     121             :     }
     122             : 
     123          62 :     return ret;
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                            OpenMDArray()                             */
     128             : /************************************************************************/
     129             : 
     130             : /** Open and return a multidimensional array.
     131             :  *
     132             :  * @note Driver implementation: optionally implemented. If implemented,
     133             :  * GetMDArrayNames() should also be implemented.
     134             :  *
     135             :  * Drivers known to implement it: MEM, netCDF.
     136             :  *
     137             :  * This is the same as the C function GDALGroupOpenMDArray().
     138             :  *
     139             :  * @param osName Array name.
     140             :  * @param papszOptions Driver specific options determining how the array should
     141             :  * be opened.  Pass nullptr for default behavior.
     142             :  *
     143             :  * @return the array, or nullptr.
     144             :  */
     145             : std::shared_ptr<GDALMDArray>
     146           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     147             :                        CPL_UNUSED CSLConstList papszOptions) const
     148             : {
     149           0 :     return nullptr;
     150             : }
     151             : 
     152             : /************************************************************************/
     153             : /*                            GetMDArrays()                             */
     154             : /************************************************************************/
     155             : 
     156             : /** Open and return all multidimensional arrays of this group.
     157             :  *
     158             :  * @param papszDiscoverOptions Driver specific options determining how arrays
     159             :  * should be retrieved. Pass nullptr for default behavior.
     160             :  * @param papszOpenOptions Driver specific options determining how an array should
     161             :  * be opened.  Pass nullptr for default behavior.
     162             :  *
     163             :  * @return the arrays
     164             :  * @since GDAL 3.14
     165             :  */
     166             : std::vector<std::shared_ptr<GDALMDArray>>
     167           0 : GDALGroup::GetMDArrays(CSLConstList papszDiscoverOptions,
     168             :                        CSLConstList papszOpenOptions) const
     169             : {
     170           0 :     std::vector<std::shared_ptr<GDALMDArray>> ret;
     171           0 :     for (const auto &osName : GetMDArrayNames(papszDiscoverOptions))
     172             :     {
     173           0 :         if (auto poMDArray = OpenMDArray(osName, papszOpenOptions))
     174           0 :             ret.push_back(std::move(poMDArray));
     175             :     }
     176           0 :     return ret;
     177             : }
     178             : 
     179             : /************************************************************************/
     180             : /*                        GetMDArraysRecursive()                        */
     181             : /************************************************************************/
     182             : 
     183             : /** Open and return all multidimensional arrays of this group and its subgroups.
     184             :  *
     185             :  * @param papszGroupDiscoverOptions Driver specific options determining how groups
     186             :  * should be retrieved. Pass nullptr for default behavior.
     187             :  * @param papszArrayDiscoverOptions Driver specific options determining how arrays
     188             :  * should be retrieved. Pass nullptr for default behavior.
     189             :  * @param papszGroupOpenOptions Driver specific options determining how a group should
     190             :  * be opened.  Pass nullptr for default behavior.
     191             :  * @param papszArrayOpenOptions Driver specific options determining how an array should
     192             :  * be opened.  Pass nullptr for default behavior.
     193             :  *
     194             :  * @return the arrays
     195             :  * @since GDAL 3.14
     196             :  */
     197             : std::vector<std::shared_ptr<GDALMDArray>>
     198           6 : GDALGroup::GetMDArraysRecursive(CSLConstList papszGroupDiscoverOptions,
     199             :                                 CSLConstList papszArrayDiscoverOptions,
     200             :                                 CSLConstList papszGroupOpenOptions,
     201             :                                 CSLConstList papszArrayOpenOptions) const
     202             : {
     203           6 :     std::vector<std::shared_ptr<GDALMDArray>> ret;
     204          12 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     205           6 :     stackGroups.push_back(nullptr);  // nullptr means this
     206          12 :     while (!stackGroups.empty())
     207             :     {
     208          12 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     209           6 :         stackGroups.erase(stackGroups.begin());
     210           6 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     211          19 :         for (const std::string &arrayName :
     212          44 :              poCurGroup->GetMDArrayNames(papszArrayDiscoverOptions))
     213             :         {
     214             :             auto poArray =
     215          38 :                 poCurGroup->OpenMDArray(arrayName, papszArrayOpenOptions);
     216          19 :             if (poArray)
     217          19 :                 ret.push_back(std::move(poArray));
     218             :         }
     219           6 :         auto insertionPoint = stackGroups.begin();
     220           0 :         for (const auto &osSubGroup :
     221           6 :              poCurGroup->GetGroupNames(papszGroupDiscoverOptions))
     222             :         {
     223             :             auto poSubGroup =
     224           0 :                 poCurGroup->OpenGroup(osSubGroup, papszGroupOpenOptions);
     225           0 :             if (poSubGroup)
     226           0 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     227             :         }
     228             :     }
     229             : 
     230          12 :     return ret;
     231             : }
     232             : 
     233             : /************************************************************************/
     234             : /*                           GetGroupNames()                            */
     235             : /************************************************************************/
     236             : 
     237             : /** Return the list of sub-groups contained in this group.
     238             :  *
     239             :  * @note Driver implementation: optionally implemented. If implemented,
     240             :  * OpenGroup() should also be implemented.
     241             :  *
     242             :  * Drivers known to implement it: MEM, netCDF.
     243             :  *
     244             :  * This is the same as the C function GDALGroupGetGroupNames().
     245             :  *
     246             :  * @param papszOptions Driver specific options determining how groups
     247             :  * should be retrieved. Pass nullptr for default behavior.
     248             :  *
     249             :  * @return the group names.
     250             :  */
     251             : std::vector<std::string>
     252          14 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     253             : {
     254          14 :     return {};
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                             OpenGroup()                              */
     259             : /************************************************************************/
     260             : 
     261             : /** Open and return a sub-group.
     262             :  *
     263             :  * @note Driver implementation: optionally implemented. If implemented,
     264             :  * GetGroupNames() should also be implemented.
     265             :  *
     266             :  * Drivers known to implement it: MEM, netCDF.
     267             :  *
     268             :  * This is the same as the C function GDALGroupOpenGroup().
     269             :  *
     270             :  * @param osName Sub-group name.
     271             :  * @param papszOptions Driver specific options determining how the sub-group
     272             :  * should be opened.  Pass nullptr for default behavior.
     273             :  *
     274             :  * @return the group, or nullptr.
     275             :  */
     276             : std::shared_ptr<GDALGroup>
     277           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     278             :                      CPL_UNUSED CSLConstList papszOptions) const
     279             : {
     280           4 :     return nullptr;
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*                             GetGroups()                              */
     285             : /************************************************************************/
     286             : 
     287             : /** Open and return all subgroups of this group.
     288             :  *
     289             :  * @param papszDiscoverOptions Driver specific options determining how subgroups
     290             :  * should be retrieved. Pass nullptr for default behavior.
     291             :  * @param papszOpenOptions Driver specific options determining how a subgroup should
     292             :  * be opened. Pass nullptr for default behavior.
     293             :  *
     294             :  * @return the subgroups
     295             :  * @since GDAL 3.14
     296             :  */
     297             : std::vector<std::shared_ptr<GDALGroup>>
     298           0 : GDALGroup::GetGroups(CSLConstList papszDiscoverOptions,
     299             :                      CSLConstList papszOpenOptions) const
     300             : {
     301           0 :     std::vector<std::shared_ptr<GDALGroup>> ret;
     302           0 :     for (const auto &osName : GetGroupNames(papszDiscoverOptions))
     303             :     {
     304           0 :         if (auto poSubGroup = OpenGroup(osName, papszOpenOptions))
     305           0 :             ret.push_back(std::move(poSubGroup));
     306             :     }
     307           0 :     return ret;
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                    GetMDArrayFullNamesRecursive()                    */
     312             : /************************************************************************/
     313             : 
     314             : /** Open and return all subgroups of this group, in a recursive way.
     315             :  *
     316             :  * @param papszDiscoverOptions Driver specific options determining how subgroups
     317             :  * should be retrieved. Pass nullptr for default behavior.
     318             :  * @param papszOpenOptions Driver specific options determining how a subgroup should
     319             :  * be opened. Pass nullptr for default behavior.
     320             :  *
     321             :  * @return the subgroups
     322             :  * @since GDAL 3.14
     323             :  */
     324             : std::vector<std::shared_ptr<GDALGroup>>
     325           4 : GDALGroup::GetGroupsRecursive(CSLConstList papszDiscoverOptions,
     326             :                               CSLConstList papszOpenOptions) const
     327             : {
     328           4 :     std::vector<std::shared_ptr<GDALGroup>> ret;
     329           8 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     330           4 :     stackGroups.push_back(nullptr);  // nullptr means this
     331           8 :     while (!stackGroups.empty())
     332             :     {
     333           8 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     334           4 :         stackGroups.erase(stackGroups.begin());
     335           4 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     336           4 :         auto insertionPoint = stackGroups.begin();
     337           0 :         for (const auto &osSubGroup :
     338           4 :              poCurGroup->GetGroupNames(papszDiscoverOptions))
     339             :         {
     340             :             auto poSubGroup =
     341           0 :                 poCurGroup->OpenGroup(osSubGroup, papszOpenOptions);
     342           0 :             if (poSubGroup)
     343             :             {
     344           0 :                 ret.push_back(poSubGroup);
     345           0 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     346             :             }
     347             :         }
     348             :     }
     349             : 
     350           8 :     return ret;
     351             : }
     352             : 
     353             : /************************************************************************/
     354             : /*                        GetVectorLayerNames()                         */
     355             : /************************************************************************/
     356             : 
     357             : /** Return the list of layer names contained in this group.
     358             :  *
     359             :  * @note Driver implementation: optionally implemented. If implemented,
     360             :  * OpenVectorLayer() should also be implemented.
     361             :  *
     362             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     363             :  *
     364             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     365             :  * GDALDataset::GetLayer() should then be used.
     366             :  *
     367             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     368             :  *
     369             :  * @param papszOptions Driver specific options determining how layers
     370             :  * should be retrieved. Pass nullptr for default behavior.
     371             :  *
     372             :  * @return the vector layer names.
     373             :  * @since GDAL 3.4
     374             :  */
     375             : std::vector<std::string>
     376           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     377             : {
     378           1 :     return {};
     379             : }
     380             : 
     381             : /************************************************************************/
     382             : /*                          OpenVectorLayer()                           */
     383             : /************************************************************************/
     384             : 
     385             : /** Open and return a vector layer.
     386             :  *
     387             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     388             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     389             :  * dataset (contrary to the general design of this class where objects can be
     390             :  * used independently of the object that returned them)
     391             :  *
     392             :  * @note Driver implementation: optionally implemented. If implemented,
     393             :  * GetVectorLayerNames() should also be implemented.
     394             :  *
     395             :  * Drivers known to implement it: MEM, netCDF.
     396             :  *
     397             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     398             :  *
     399             :  * @param osName Vector layer name.
     400             :  * @param papszOptions Driver specific options determining how the layer should
     401             :  * be opened.  Pass nullptr for default behavior.
     402             :  *
     403             :  * @return the group, or nullptr.
     404             :  */
     405           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     406             :                                      CPL_UNUSED CSLConstList papszOptions) const
     407             : {
     408           2 :     return nullptr;
     409             : }
     410             : 
     411             : /************************************************************************/
     412             : /*                           GetDimensions()                            */
     413             : /************************************************************************/
     414             : 
     415             : /** Return the list of dimensions contained in this group and used by its
     416             :  * arrays.
     417             :  *
     418             :  * This is for dimensions that can potentially be used by several arrays.
     419             :  * Not all drivers might implement this. To retrieve the dimensions used by
     420             :  * a specific array, use GDALMDArray::GetDimensions().
     421             :  *
     422             :  * Drivers known to implement it: MEM, netCDF
     423             :  *
     424             :  * This is the same as the C function GDALGroupGetDimensions().
     425             :  *
     426             :  * @param papszOptions Driver specific options determining how dimensions
     427             :  * should be retrieved. Pass nullptr for default behavior.
     428             :  *
     429             :  * @return the dimensions.
     430             :  */
     431             : std::vector<std::shared_ptr<GDALDimension>>
     432          26 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     433             : {
     434          26 :     return {};
     435             : }
     436             : 
     437             : /************************************************************************/
     438             : /*                       GetDimensionsRecursive()                       */
     439             : /************************************************************************/
     440             : 
     441             : /** Return all dimensions arrays of this group, its arrays, its subgroups,
     442             :  * in a recursive way
     443             :  *
     444             :  * @param papszDimensionDiscoverOptions Driver specific options determining how dimensions
     445             :  * should be retrieved. Pass nullptr for default behavior.
     446             :  * @param papszGroupDiscoverOptions Driver specific options determining how groups
     447             :  * should be retrieved. Pass nullptr for default behavior.
     448             :  * @param papszArrayDiscoverOptions Driver specific options determining how arrays
     449             :  * should be retrieved. Pass nullptr for default behavior.
     450             :  * @param papszGroupOpenOptions Driver specific options determining how a group should
     451             :  * be opened.  Pass nullptr for default behavior.
     452             :  * @param papszArrayOpenOptions Driver specific options determining how an array should
     453             :  * be opened.  Pass nullptr for default behavior.
     454             :  *
     455             :  * @return the dimensions
     456             :  * @since GDAL 3.14
     457             :  */
     458             : std::vector<std::shared_ptr<GDALDimension>>
     459           4 : GDALGroup::GetDimensionsRecursive(CSLConstList papszDimensionDiscoverOptions,
     460             :                                   CSLConstList papszGroupDiscoverOptions,
     461             :                                   CSLConstList papszArrayDiscoverOptions,
     462             :                                   CSLConstList papszGroupOpenOptions,
     463             :                                   CSLConstList papszArrayOpenOptions) const
     464             : {
     465           8 :     std::set<std::string> setDimensionNames;
     466           4 :     std::vector<std::shared_ptr<GDALDimension>> ret;
     467             : 
     468             :     const auto AddDimensions =
     469          15 :         [&setDimensionNames,
     470          30 :          &ret](const std::vector<std::shared_ptr<GDALDimension>> &apoDims)
     471             :     {
     472          37 :         for (const auto &poDim : apoDims)
     473             :         {
     474          22 :             const std::string &osName = poDim->GetFullName();
     475          22 :             if (osName.empty() || osName.front() != '/')
     476           0 :                 ret.push_back(poDim);
     477          22 :             else if (setDimensionNames.insert(osName).second)
     478           8 :                 ret.push_back(poDim);
     479             :         }
     480          15 :     };
     481             : 
     482           8 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     483           4 :     stackGroups.push_back(nullptr);  // nullptr means this
     484           8 :     while (!stackGroups.empty())
     485             :     {
     486           8 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     487           4 :         stackGroups.erase(stackGroups.begin());
     488           4 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     489             : 
     490           4 :         AddDimensions(poCurGroup->GetDimensions(papszDimensionDiscoverOptions));
     491             : 
     492          11 :         for (const std::string &arrayName :
     493          26 :              poCurGroup->GetMDArrayNames(papszArrayDiscoverOptions))
     494             :         {
     495          22 :             if (auto poMDArray = OpenMDArray(arrayName, papszArrayOpenOptions))
     496             :             {
     497          11 :                 AddDimensions(poMDArray->GetDimensions());
     498             :             }
     499             :         }
     500           4 :         auto insertionPoint = stackGroups.begin();
     501           0 :         for (const auto &osSubGroup :
     502           4 :              poCurGroup->GetGroupNames(papszGroupDiscoverOptions))
     503             :         {
     504             :             auto poSubGroup =
     505           0 :                 poCurGroup->OpenGroup(osSubGroup, papszGroupOpenOptions);
     506           0 :             if (poSubGroup)
     507           0 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     508             :         }
     509             :     }
     510           8 :     return ret;
     511             : }
     512             : 
     513             : /************************************************************************/
     514             : /*                         GetStructuralInfo()                          */
     515             : /************************************************************************/
     516             : 
     517             : /** Return structural information on the group.
     518             :  *
     519             :  * This may be the compression, etc..
     520             :  *
     521             :  * The return value should not be freed and is valid until GDALGroup is
     522             :  * released or this function called again.
     523             :  *
     524             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     525             :  */
     526          76 : CSLConstList GDALGroup::GetStructuralInfo() const
     527             : {
     528          76 :     return nullptr;
     529             : }
     530             : 
     531             : /************************************************************************/
     532             : /*                            CreateGroup()                             */
     533             : /************************************************************************/
     534             : 
     535             : /** Create a sub-group within a group.
     536             :  *
     537             :  * Optionally implemented by drivers.
     538             :  *
     539             :  * Drivers known to implement it: MEM, netCDF
     540             :  *
     541             :  * This is the same as the C function GDALGroupCreateGroup().
     542             :  *
     543             :  * @param osName Sub-group name.
     544             :  * @param papszOptions Driver specific options determining how the sub-group
     545             :  * should be created.
     546             :  *
     547             :  * @return the new sub-group, or nullptr in case of error.
     548             :  */
     549             : std::shared_ptr<GDALGroup>
     550           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     551             :                        CPL_UNUSED CSLConstList papszOptions)
     552             : {
     553           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     554           0 :     return nullptr;
     555             : }
     556             : 
     557             : /************************************************************************/
     558             : /*                            DeleteGroup()                             */
     559             : /************************************************************************/
     560             : 
     561             : /** Delete a sub-group from a group.
     562             :  *
     563             :  * Optionally implemented.
     564             :  *
     565             :  * After this call, if a previously obtained instance of the deleted object
     566             :  * is still alive, no method other than for freeing it should be invoked.
     567             :  *
     568             :  * Drivers known to implement it: MEM, Zarr
     569             :  *
     570             :  * This is the same as the C function GDALGroupDeleteGroup().
     571             :  *
     572             :  * @param osName Sub-group name.
     573             :  * @param papszOptions Driver specific options determining how the group.
     574             :  * should be deleted.
     575             :  *
     576             :  * @return true in case of success
     577             :  * @since GDAL 3.8
     578             :  */
     579           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     580             :                             CPL_UNUSED CSLConstList papszOptions)
     581             : {
     582           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     583           0 :     return false;
     584             : }
     585             : 
     586             : /************************************************************************/
     587             : /*                          CreateDimension()                           */
     588             : /************************************************************************/
     589             : 
     590             : /** Create a dimension within a group.
     591             :  *
     592             :  * @note Driver implementation: drivers supporting CreateDimension() should
     593             :  * implement this method, but do not have necessarily to implement
     594             :  * GDALGroup::GetDimensions().
     595             :  *
     596             :  * Drivers known to implement it: MEM, netCDF
     597             :  *
     598             :  * This is the same as the C function GDALGroupCreateDimension().
     599             :  *
     600             :  * @param osName Dimension name.
     601             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     602             :  * @param osDirection Dimension direction (might be empty, and ignored by
     603             :  * drivers)
     604             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     605             :  * @param papszOptions Driver specific options determining how the dimension
     606             :  * should be created.
     607             :  *
     608             :  * @return the new dimension, or nullptr if case of error
     609             :  */
     610           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     611             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     612             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     613             :     CPL_UNUSED CSLConstList papszOptions)
     614             : {
     615           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     616             :              "CreateDimension() not implemented");
     617           0 :     return nullptr;
     618             : }
     619             : 
     620             : /************************************************************************/
     621             : /*                           CreateMDArray()                            */
     622             : /************************************************************************/
     623             : 
     624             : /** Create a multidimensional array within a group.
     625             :  *
     626             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     627             :  * belong to this group, either by retrieving them with GetDimensions()
     628             :  * or creating a new one with CreateDimension().
     629             :  *
     630             :  * Optionally implemented.
     631             :  *
     632             :  * Drivers known to implement it: MEM, netCDF
     633             :  *
     634             :  * This is the same as the C function GDALGroupCreateMDArray().
     635             :  *
     636             :  * @note Driver implementation: drivers should take into account the possibility
     637             :  * that GDALDimension object passed in aoDimensions might belong to a different
     638             :  * group / dataset / driver and act accordingly.
     639             :  *
     640             :  * @param osName Array name.
     641             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     642             :  *                     dimension first to the fastest varying dimension last.
     643             :  *                     Might be empty for a scalar array (if supported by
     644             :  * driver)
     645             :  * @param oDataType  Array data type.
     646             :  * @param papszOptions Driver specific options determining how the array
     647             :  * should be created.
     648             :  *
     649             :  * @return the new array, or nullptr in case of error
     650             :  */
     651           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     652             :     CPL_UNUSED const std::string &osName,
     653             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     654             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     655             :     CPL_UNUSED CSLConstList papszOptions)
     656             : {
     657           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     658           0 :     return nullptr;
     659             : }
     660             : 
     661             : /************************************************************************/
     662             : /*                           DeleteMDArray()                            */
     663             : /************************************************************************/
     664             : 
     665             : /** Delete an array from a group.
     666             :  *
     667             :  * Optionally implemented.
     668             :  *
     669             :  * After this call, if a previously obtained instance of the deleted object
     670             :  * is still alive, no method other than for freeing it should be invoked.
     671             :  *
     672             :  * Drivers known to implement it: MEM, Zarr
     673             :  *
     674             :  * This is the same as the C function GDALGroupDeleteMDArray().
     675             :  *
     676             :  * @param osName Arrayname.
     677             :  * @param papszOptions Driver specific options determining how the array.
     678             :  * should be deleted.
     679             :  *
     680             :  * @return true in case of success
     681             :  * @since GDAL 3.8
     682             :  */
     683           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     684             :                               CPL_UNUSED CSLConstList papszOptions)
     685             : {
     686           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     687           0 :     return false;
     688             : }
     689             : 
     690             : /************************************************************************/
     691             : /*                          GetTotalCopyCost()                          */
     692             : /************************************************************************/
     693             : 
     694             : /** Return a total "cost" to copy the group.
     695             :  *
     696             :  * Used as a parameter for CopFrom()
     697             :  */
     698          36 : GUInt64 GDALGroup::GetTotalCopyCost() const
     699             : {
     700          36 :     GUInt64 nCost = COPY_COST;
     701          36 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     702             : 
     703          72 :     auto groupNames = GetGroupNames();
     704          42 :     for (const auto &name : groupNames)
     705             :     {
     706          12 :         auto subGroup = OpenGroup(name);
     707           6 :         if (subGroup)
     708             :         {
     709           6 :             nCost += subGroup->GetTotalCopyCost();
     710             :         }
     711             :     }
     712             : 
     713          36 :     auto arrayNames = GetMDArrayNames();
     714         114 :     for (const auto &name : arrayNames)
     715             :     {
     716         156 :         auto array = OpenMDArray(name);
     717          78 :         if (array)
     718             :         {
     719          78 :             nCost += array->GetTotalCopyCost();
     720             :         }
     721             :     }
     722          72 :     return nCost;
     723             : }
     724             : 
     725             : /************************************************************************/
     726             : /*                              CopyFrom()                              */
     727             : /************************************************************************/
     728             : 
     729             : /** Copy the content of a group into a new (generally empty) group.
     730             :  *
     731             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     732             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     733             :  *                   of some output drivers this is not recommended)
     734             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     735             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     736             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     737             :  *                be pursued.
     738             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     739             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     740             :  * @param pfnProgress Progress callback, or nullptr.
     741             :  * @param pProgressData Progress user data, or nullptr.
     742             :  * @param papszOptions Creation options. Currently, only array creation
     743             :  *                     options are supported. They must be prefixed with
     744             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     745             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     746             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     747             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     748             :  *                     Restriction to arrays of a given name is done with adding
     749             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     750             :  *                     a full qualified name.
     751             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     752             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     753             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     754             :  * and maximum of the source array. The integer data type used can be set with
     755             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     756             :  *
     757             :  * @return true in case of success (or partial success if bStrict == false).
     758             :  */
     759          36 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     760             :                          GDALDataset *poSrcDS,
     761             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     762             :                          bool bStrict, GUInt64 &nCurCost,
     763             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     764             :                          void *pProgressData, CSLConstList papszOptions)
     765             : {
     766          36 :     if (pfnProgress == nullptr)
     767           0 :         pfnProgress = GDALDummyProgress;
     768             : 
     769             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     770             :     if (!(x))                                                                  \
     771             :     {                                                                          \
     772             :         if (bStrict)                                                           \
     773             :             return false;                                                      \
     774             :         continue;                                                              \
     775             :     }                                                                          \
     776             :     (void)0
     777             : 
     778             :     try
     779             :     {
     780          36 :         nCurCost += GDALGroup::COPY_COST;
     781             : 
     782          72 :         const auto srcDims = poSrcGroup->GetDimensions();
     783             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     784          72 :             mapExistingDstDims;
     785          72 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     786          96 :         for (const auto &dim : srcDims)
     787             :         {
     788             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     789          60 :                                           dim->GetDirection(), dim->GetSize());
     790          60 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     791          60 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     792         120 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     793          60 :             if (poIndexingVarSrc)
     794             :             {
     795             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     796          41 :                                                        ->GetName()] =
     797          82 :                     dim->GetName();
     798             :             }
     799             :         }
     800             : 
     801          72 :         auto attrs = poSrcGroup->GetAttributes();
     802          62 :         for (const auto &attr : attrs)
     803             :         {
     804             :             auto dstAttr =
     805          26 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     806          52 :                                 attr->GetDataType());
     807          26 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     808          26 :             auto raw(attr->ReadAsRaw());
     809          26 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     810           0 :                 return false;
     811             :         }
     812          36 :         if (!attrs.empty())
     813             :         {
     814          10 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     815          10 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     816           0 :                 return false;
     817             :         }
     818             : 
     819             :         const auto CopyArray =
     820          78 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     821             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     822             :              papszOptions, bStrict, &nCurCost,
     823         694 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     824             :         {
     825             :             // Map source dimensions to target dimensions
     826         156 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     827          78 :             const auto &srcArrayDims(srcArray->GetDimensions());
     828         193 :             for (const auto &dim : srcArrayDims)
     829             :             {
     830             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     831         115 :                     dim->GetFullName());
     832         115 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     833             :                 {
     834         105 :                     dstArrayDims.emplace_back(dstDim);
     835             :                 }
     836             :                 else
     837             :                 {
     838          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     839          19 :                     if (oIter != mapExistingDstDims.end() &&
     840           9 :                         oIter->second->GetSize() == dim->GetSize())
     841             :                     {
     842           8 :                         dstArrayDims.emplace_back(oIter->second);
     843             :                     }
     844             :                     else
     845             :                     {
     846           2 :                         std::string newDimName;
     847           2 :                         if (oIter == mapExistingDstDims.end())
     848             :                         {
     849           1 :                             newDimName = dim->GetName();
     850             :                         }
     851             :                         else
     852             :                         {
     853           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     854           3 :                                                          '_' + dim->GetName());
     855           1 :                             newDimName = newDimNamePrefix;
     856           1 :                             int nIterCount = 2;
     857           0 :                             while (
     858           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     859             :                             {
     860           0 :                                 newDimName = newDimNamePrefix +
     861           0 :                                              CPLSPrintf("_%d", nIterCount);
     862           0 :                                 nIterCount++;
     863             :                             }
     864             :                         }
     865           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     866             :                                                  dim->GetDirection(),
     867           4 :                                                  dim->GetSize());
     868           2 :                         if (!dstDim)
     869           0 :                             return false;
     870           2 :                         mapExistingDstDims[newDimName] = dstDim;
     871           2 :                         dstArrayDims.emplace_back(dstDim);
     872             :                     }
     873             :                 }
     874             :             }
     875             : 
     876         156 :             CPLStringList aosArrayCO;
     877          78 :             bool bAutoScale = false;
     878          78 :             GDALDataType eAutoScaleType = GDT_UInt16;
     879          85 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     880             :             {
     881           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     882             :                 {
     883           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     884           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     885             :                     {
     886           1 :                         const char *pszNext = strchr(pszOption, ':');
     887           1 :                         if (pszNext != nullptr)
     888             :                         {
     889           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     890           1 :                             if (static_cast<size_t>(nDim) ==
     891           1 :                                 dstArrayDims.size())
     892             :                             {
     893           1 :                                 pszOption = pszNext + 1;
     894             :                             }
     895             :                             else
     896             :                             {
     897           0 :                                 pszOption = nullptr;
     898             :                             }
     899             :                         }
     900             :                     }
     901           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     902             :                     {
     903           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     904           2 :                         const char *pszNext = strchr(pszName, ':');
     905           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     906           2 :                             pszNext[-1] == ')')
     907             :                         {
     908           4 :                             CPLString osName;
     909           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     910           3 :                             if (osName == srcArray->GetName() ||
     911           1 :                                 osName == srcArray->GetFullName())
     912             :                             {
     913           2 :                                 pszOption = pszNext + 1;
     914             :                             }
     915             :                             else
     916             :                             {
     917           0 :                                 pszOption = nullptr;
     918             :                             }
     919             :                         }
     920             :                     }
     921           7 :                     if (pszOption)
     922             :                     {
     923           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
     924             :                         {
     925             :                             bAutoScale =
     926           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
     927             :                         }
     928           5 :                         else if (STARTS_WITH_CI(pszOption,
     929             :                                                 "AUTOSCALE_DATA_TYPE="))
     930             :                         {
     931           1 :                             const char *pszDataType =
     932             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
     933           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
     934           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
     935           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
     936             :                             {
     937           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
     938             :                                          "Unsupported value for "
     939             :                                          "AUTOSCALE_DATA_TYPE");
     940           0 :                                 return false;
     941             :                             }
     942             :                         }
     943             :                         else
     944             :                         {
     945           4 :                             aosArrayCO.AddString(pszOption);
     946             :                         }
     947             :                     }
     948             :                 }
     949             :             }
     950             : 
     951          78 :             if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
     952             :             {
     953         154 :                 const auto anBlockSize = srcArray->GetBlockSize();
     954         154 :                 std::string osBlockSize;
     955          83 :                 for (auto v : anBlockSize)
     956             :                 {
     957          78 :                     if (v == 0)
     958             :                     {
     959          72 :                         osBlockSize.clear();
     960          72 :                         break;
     961             :                     }
     962           6 :                     if (!osBlockSize.empty())
     963           2 :                         osBlockSize += ',';
     964           6 :                     osBlockSize += std::to_string(v);
     965             :                 }
     966          77 :                 if (!osBlockSize.empty())
     967           3 :                     aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
     968             :             }
     969             : 
     970             :             auto oIterDimName =
     971          78 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
     972          78 :             const auto &srcArrayType = srcArray->GetDataType();
     973             : 
     974          78 :             std::shared_ptr<GDALMDArray> dstArray;
     975             : 
     976             :             // Only autoscale non-indexing variables
     977          78 :             bool bHasOffset = false;
     978          78 :             bool bHasScale = false;
     979           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
     980           4 :                 (srcArrayType.GetNumericDataType() == GDT_Float16 ||
     981           2 :                  srcArrayType.GetNumericDataType() == GDT_Float32 ||
     982           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
     983           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
     984          82 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
     985          80 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
     986             :             {
     987           2 :                 constexpr bool bApproxOK = false;
     988           2 :                 constexpr bool bForce = true;
     989           2 :                 double dfMin = 0.0;
     990           2 :                 double dfMax = 0.0;
     991           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
     992             :                                             nullptr, nullptr, nullptr, nullptr,
     993           2 :                                             nullptr) != CE_None)
     994             :                 {
     995           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     996             :                              "Could not retrieve statistics for array %s",
     997           0 :                              srcArray->GetName().c_str());
     998           0 :                     return false;
     999             :                 }
    1000           2 :                 double dfDTMin = 0;
    1001           2 :                 double dfDTMax = 0;
    1002             : #define setDTMinMax(ctype)                                                     \
    1003             :     do                                                                         \
    1004             :     {                                                                          \
    1005             :         dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
    1006             :         dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
    1007             :     } while (0)
    1008             : 
    1009           2 :                 switch (eAutoScaleType)
    1010             :                 {
    1011           0 :                     case GDT_UInt8:
    1012           0 :                         setDTMinMax(GByte);
    1013           0 :                         break;
    1014           0 :                     case GDT_Int8:
    1015           0 :                         setDTMinMax(GInt8);
    1016           0 :                         break;
    1017           1 :                     case GDT_UInt16:
    1018           1 :                         setDTMinMax(GUInt16);
    1019           1 :                         break;
    1020           1 :                     case GDT_Int16:
    1021           1 :                         setDTMinMax(GInt16);
    1022           1 :                         break;
    1023           0 :                     case GDT_UInt32:
    1024           0 :                         setDTMinMax(GUInt32);
    1025           0 :                         break;
    1026           0 :                     case GDT_Int32:
    1027           0 :                         setDTMinMax(GInt32);
    1028           0 :                         break;
    1029           0 :                     case GDT_UInt64:
    1030           0 :                         setDTMinMax(std::uint64_t);
    1031           0 :                         break;
    1032           0 :                     case GDT_Int64:
    1033           0 :                         setDTMinMax(std::int64_t);
    1034           0 :                         break;
    1035           0 :                     case GDT_Float16:
    1036             :                     case GDT_Float32:
    1037             :                     case GDT_Float64:
    1038             :                     case GDT_Unknown:
    1039             :                     case GDT_CInt16:
    1040             :                     case GDT_CInt32:
    1041             :                     case GDT_CFloat16:
    1042             :                     case GDT_CFloat32:
    1043             :                     case GDT_CFloat64:
    1044             :                     case GDT_TypeCount:
    1045           0 :                         CPLAssert(false);
    1046             :                 }
    1047             : 
    1048             :                 dstArray =
    1049           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1050           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1051           4 :                                   aosArrayCO.List());
    1052           2 :                 if (!dstArray)
    1053           0 :                     return !bStrict;
    1054             : 
    1055           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1056             :                 {
    1057             :                     // If there's a nodata value in the source array, reserve
    1058             :                     // DTMax for that purpose in the target scaled array
    1059           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1060             :                     {
    1061           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1062             :                                  "Cannot set nodata value");
    1063           0 :                         return false;
    1064             :                     }
    1065           1 :                     dfDTMax--;
    1066             :                 }
    1067           2 :                 const double dfScale =
    1068           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1069           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1070             : 
    1071           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1072           2 :                     !dstArray->SetScale(dfScale))
    1073             :                 {
    1074           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1075             :                              "Cannot set scale/offset");
    1076           0 :                     return false;
    1077             :                 }
    1078             : 
    1079           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1080           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1081             :                 {
    1082           1 :                     poUnscaled->SetNoDataValue(
    1083             :                         srcArray->GetNoDataValueAsDouble());
    1084             :                 }
    1085             : 
    1086             :                 // Copy source array into unscaled array
    1087           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1088             :                                           nCurCost, nTotalCost, pfnProgress,
    1089           2 :                                           pProgressData))
    1090             :                 {
    1091           0 :                     return false;
    1092             :                 }
    1093             :             }
    1094             :             else
    1095             :             {
    1096         152 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1097         152 :                                          srcArrayType, aosArrayCO.List());
    1098          76 :                 if (!dstArray)
    1099           0 :                     return !bStrict;
    1100             : 
    1101         152 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1102             :                                         nCurCost, nTotalCost, pfnProgress,
    1103          76 :                                         pProgressData))
    1104             :                 {
    1105           0 :                     return false;
    1106             :                 }
    1107             :             }
    1108             : 
    1109             :             // If this array is the indexing variable of a dimension, link them
    1110             :             // together.
    1111          78 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1112             :             {
    1113             :                 auto oCorrespondingDimIter =
    1114          41 :                     mapExistingDstDims.find(oIterDimName->second);
    1115          41 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1116             :                 {
    1117             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1118          41 :                         CPLQuietErrorHandler);
    1119          82 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1120          41 :                         std::move(dstArray));
    1121             :                 }
    1122             :             }
    1123             : 
    1124          78 :             return true;
    1125          36 :         };
    1126             : 
    1127          72 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1128             : 
    1129             :         // Start by copying arrays that are indexing variables of dimensions
    1130         114 :         for (const auto &name : arrayNames)
    1131             :         {
    1132          78 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1133          78 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1134             : 
    1135          78 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1136         197 :                               srcArray->GetName()) &&
    1137         119 :                 !OpenMDArray(name))
    1138             :             {
    1139          41 :                 if (!CopyArray(srcArray))
    1140           0 :                     return false;
    1141             :             }
    1142             :         }
    1143             : 
    1144             :         // Then copy regular arrays
    1145         114 :         for (const auto &name : arrayNames)
    1146             :         {
    1147          78 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1148          78 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1149             : 
    1150          78 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1151         193 :                                srcArray->GetName()) &&
    1152         115 :                 !OpenMDArray(name))
    1153             :             {
    1154          37 :                 if (!CopyArray(srcArray))
    1155           0 :                     return false;
    1156             :             }
    1157             :         }
    1158             : 
    1159          72 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1160          42 :         for (const auto &name : groupNames)
    1161             :         {
    1162           6 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1163           6 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1164           6 :             auto dstSubGroup = CreateGroup(name);
    1165           6 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1166          12 :             if (!dstSubGroup->CopyFrom(
    1167             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1168           6 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1169           0 :                 return false;
    1170             :         }
    1171             : 
    1172          36 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1173           0 :             return false;
    1174             : 
    1175          36 :         return true;
    1176             :     }
    1177           0 :     catch (const std::exception &e)
    1178             :     {
    1179           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1180           0 :         return false;
    1181             :     }
    1182             : }
    1183             : 
    1184             : /************************************************************************/
    1185             : /*                         GetInnerMostGroup()                          */
    1186             : /************************************************************************/
    1187             : 
    1188             : //! @cond Doxygen_Suppress
    1189             : const GDALGroup *
    1190        2446 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1191             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1192             :                              std::string &osLastPart) const
    1193             : {
    1194        2446 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1195          16 :         return nullptr;
    1196        2430 :     const GDALGroup *poCurGroup = this;
    1197             :     CPLStringList aosTokens(
    1198        4860 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1199        2430 :     if (aosTokens.size() == 0)
    1200             :     {
    1201             :         // "/" case: the root group itself is the innermost group
    1202           2 :         osLastPart.clear();
    1203           2 :         return poCurGroup;
    1204             :     }
    1205             : 
    1206        2977 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1207             :     {
    1208         565 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1209         565 :         if (!curGroupHolder)
    1210             :         {
    1211          16 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1212             :                      aosTokens[i]);
    1213          16 :             return nullptr;
    1214             :         }
    1215         549 :         poCurGroup = curGroupHolder.get();
    1216             :     }
    1217        2412 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1218        2412 :     return poCurGroup;
    1219             : }
    1220             : 
    1221             : //! @endcond
    1222             : 
    1223             : /************************************************************************/
    1224             : /*                      OpenMDArrayFromFullname()                       */
    1225             : /************************************************************************/
    1226             : 
    1227             : /** Get an array from its fully qualified name */
    1228             : std::shared_ptr<GDALMDArray>
    1229         938 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1230             :                                    CSLConstList papszOptions) const
    1231             : {
    1232        1876 :     std::string osName;
    1233         938 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1234         938 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1235         938 :     if (poGroup == nullptr)
    1236          24 :         return nullptr;
    1237         914 :     return poGroup->OpenMDArray(osName, papszOptions);
    1238             : }
    1239             : 
    1240             : /************************************************************************/
    1241             : /*                     OpenAttributeFromFullname()                      */
    1242             : /************************************************************************/
    1243             : 
    1244             : /** Get an attribute from its fully qualified name */
    1245             : std::shared_ptr<GDALAttribute>
    1246           9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
    1247             :                                      CSLConstList papszOptions) const
    1248             : {
    1249           9 :     const auto pos = osFullName.rfind('/');
    1250           9 :     if (pos == std::string::npos)
    1251           0 :         return nullptr;
    1252          18 :     const std::string attrName = osFullName.substr(pos + 1);
    1253           9 :     if (pos == 0)
    1254           2 :         return GetAttribute(attrName);
    1255          14 :     const std::string container = osFullName.substr(0, pos);
    1256          14 :     auto poArray = OpenMDArrayFromFullname(container, papszOptions);
    1257           7 :     if (poArray)
    1258           4 :         return poArray->GetAttribute(attrName);
    1259           6 :     auto poGroup = OpenGroupFromFullname(container, papszOptions);
    1260           3 :     if (poGroup)
    1261           1 :         return poGroup->GetAttribute(attrName);
    1262           2 :     return nullptr;
    1263             : }
    1264             : 
    1265             : /************************************************************************/
    1266             : /*                           ResolveMDArray()                           */
    1267             : /************************************************************************/
    1268             : 
    1269             : /** Locate an array in a group and its subgroups by name.
    1270             :  *
    1271             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1272             :  * used
    1273             :  * Otherwise the search will start from the group identified by osStartingPath,
    1274             :  * and an array whose name is osName will be looked for in this group (if
    1275             :  * osStartingPath is empty or "/", then the current group is used). If there
    1276             :  * is no match, then a recursive descendant search will be made in its
    1277             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1278             :  * existing) of the group pointed by osStartingPath will be used as the new
    1279             :  * starting point for the search.
    1280             :  *
    1281             :  * @param osName name, qualified or not
    1282             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1283             :  *                       the search should be started. If this is a non-empty
    1284             :  *                       string, the group on which this method is called should
    1285             :  *                       nominally be the root group (otherwise the path will
    1286             :  *                       be interpreted as from the current group)
    1287             :  * @param papszOptions options to pass to OpenMDArray()
    1288             :  * @since GDAL 3.2
    1289             :  */
    1290             : std::shared_ptr<GDALMDArray>
    1291          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1292             :                           const std::string &osStartingPath,
    1293             :                           CSLConstList papszOptions) const
    1294             : {
    1295          19 :     if (!osName.empty() && osName[0] == '/')
    1296             :     {
    1297           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1298           1 :         if (poArray)
    1299           1 :             return poArray;
    1300             :     }
    1301          36 :     std::string osPath(osStartingPath);
    1302          36 :     std::set<std::string> oSetAlreadyVisited;
    1303             : 
    1304             :     while (true)
    1305             :     {
    1306           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1307           0 :         std::shared_ptr<GDALGroup> poGroup;
    1308             : 
    1309          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1310          22 :         bool goOn = false;
    1311          22 :         if (osPath.empty() || osPath == "/")
    1312             :         {
    1313          11 :             goOn = true;
    1314             :         }
    1315             :         else
    1316             :         {
    1317          22 :             std::string osLastPart;
    1318             :             const GDALGroup *poGroupPtr =
    1319          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1320          11 :             if (poGroupPtr)
    1321          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1322          22 :             if (poGroup &&
    1323          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1324             :             {
    1325          11 :                 oQueue.push(poGroup);
    1326          11 :                 goOn = true;
    1327             :             }
    1328             :         }
    1329             : 
    1330          22 :         if (goOn)
    1331             :         {
    1332          17 :             do
    1333             :             {
    1334             :                 const GDALGroup *groupPtr;
    1335          39 :                 if (!oQueue.empty())
    1336             :                 {
    1337          28 :                     poGroup = oQueue.front();
    1338          28 :                     oQueue.pop();
    1339          28 :                     groupPtr = poGroup.get();
    1340             :                 }
    1341             :                 else
    1342             :                 {
    1343          11 :                     groupPtr = this;
    1344             :                 }
    1345             : 
    1346          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1347          39 :                 if (poArray)
    1348          16 :                     return poArray;
    1349             : 
    1350          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1351          47 :                 for (const auto &osGroupName : aosGroupNames)
    1352             :                 {
    1353          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1354          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1355          48 :                                                      poSubGroup->GetFullName()))
    1356             :                     {
    1357          24 :                         oQueue.push(poSubGroup);
    1358          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1359             :                     }
    1360             :                 }
    1361          23 :             } while (!oQueue.empty());
    1362             :         }
    1363             : 
    1364           6 :         if (osPath.empty() || osPath == "/")
    1365           2 :             break;
    1366             : 
    1367           4 :         const auto nPos = osPath.rfind('/');
    1368           4 :         if (nPos == 0)
    1369           1 :             osPath = "/";
    1370             :         else
    1371             :         {
    1372           3 :             if (nPos == std::string::npos)
    1373           0 :                 break;
    1374           3 :             osPath.resize(nPos);
    1375             :         }
    1376           4 :     }
    1377           2 :     return nullptr;
    1378             : }
    1379             : 
    1380             : /************************************************************************/
    1381             : /*                       OpenGroupFromFullname()                        */
    1382             : /************************************************************************/
    1383             : 
    1384             : /** Get a group from its fully qualified name.
    1385             :  * @since GDAL 3.2
    1386             :  */
    1387             : std::shared_ptr<GDALGroup>
    1388        1273 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1389             :                                  CSLConstList papszOptions) const
    1390             : {
    1391        2546 :     std::string osName;
    1392        1273 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1393        1273 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1394        1273 :     if (poGroup == nullptr)
    1395           4 :         return nullptr;
    1396        1269 :     if (osName.empty())
    1397           2 :         return m_pSelf.lock();
    1398        1267 :     return poGroup->OpenGroup(osName, papszOptions);
    1399             : }
    1400             : 
    1401             : /************************************************************************/
    1402             : /*                     OpenDimensionFromFullname()                      */
    1403             : /************************************************************************/
    1404             : 
    1405             : /** Get a dimension from its fully qualified name */
    1406             : std::shared_ptr<GDALDimension>
    1407         224 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1408             : {
    1409         448 :     std::string osName;
    1410         224 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1411         224 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1412         224 :     if (poGroup == nullptr)
    1413           4 :         return nullptr;
    1414         440 :     auto dims(poGroup->GetDimensions());
    1415         378 :     for (auto &dim : dims)
    1416             :     {
    1417         326 :         if (dim->GetName() == osName)
    1418         168 :             return dim;
    1419             :     }
    1420          52 :     return nullptr;
    1421             : }
    1422             : 
    1423             : /************************************************************************/
    1424             : /*                          ClearStatistics()                           */
    1425             : /************************************************************************/
    1426             : 
    1427             : /**
    1428             :  * \brief Clear statistics.
    1429             :  *
    1430             :  * @since GDAL 3.4
    1431             :  */
    1432           0 : void GDALGroup::ClearStatistics()
    1433             : {
    1434           0 :     auto groupNames = GetGroupNames();
    1435           0 :     for (const auto &name : groupNames)
    1436             :     {
    1437           0 :         auto subGroup = OpenGroup(name);
    1438           0 :         if (subGroup)
    1439             :         {
    1440           0 :             subGroup->ClearStatistics();
    1441             :         }
    1442             :     }
    1443             : 
    1444           0 :     auto arrayNames = GetMDArrayNames();
    1445           0 :     for (const auto &name : arrayNames)
    1446             :     {
    1447           0 :         auto array = OpenMDArray(name);
    1448           0 :         if (array)
    1449             :         {
    1450           0 :             array->ClearStatistics();
    1451             :         }
    1452             :     }
    1453           0 : }
    1454             : 
    1455             : /************************************************************************/
    1456             : /*                               Rename()                               */
    1457             : /************************************************************************/
    1458             : 
    1459             : /** Rename the group.
    1460             :  *
    1461             :  * This is not implemented by all drivers.
    1462             :  *
    1463             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1464             :  *
    1465             :  * This is the same as the C function GDALGroupRename().
    1466             :  *
    1467             :  * @param osNewName New name.
    1468             :  *
    1469             :  * @return true in case of success
    1470             :  * @since GDAL 3.8
    1471             :  */
    1472           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1473             : {
    1474           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1475           0 :     return false;
    1476             : }
    1477             : 
    1478             : /************************************************************************/
    1479             : /*                             BaseRename()                             */
    1480             : /************************************************************************/
    1481             : 
    1482             : //! @cond Doxygen_Suppress
    1483           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1484             : {
    1485           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1486           8 :     m_osFullName += osNewName;
    1487           8 :     m_osName = osNewName;
    1488             : 
    1489           8 :     NotifyChildrenOfRenaming();
    1490           8 : }
    1491             : 
    1492             : //! @endcond
    1493             : 
    1494             : /************************************************************************/
    1495             : /*                           ParentRenamed()                            */
    1496             : /************************************************************************/
    1497             : 
    1498             : //! @cond Doxygen_Suppress
    1499           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1500             : {
    1501           7 :     m_osFullName = osNewParentFullName;
    1502           7 :     m_osFullName += "/";
    1503           7 :     m_osFullName += m_osName;
    1504             : 
    1505           7 :     NotifyChildrenOfRenaming();
    1506           7 : }
    1507             : 
    1508             : //! @endcond
    1509             : 
    1510             : /************************************************************************/
    1511             : /*                              Deleted()                               */
    1512             : /************************************************************************/
    1513             : 
    1514             : //! @cond Doxygen_Suppress
    1515          28 : void GDALGroup::Deleted()
    1516             : {
    1517          28 :     m_bValid = false;
    1518             : 
    1519          28 :     NotifyChildrenOfDeletion();
    1520          28 : }
    1521             : 
    1522             : //! @endcond
    1523             : 
    1524             : /************************************************************************/
    1525             : /*                           ParentDeleted()                            */
    1526             : /************************************************************************/
    1527             : 
    1528             : //! @cond Doxygen_Suppress
    1529           3 : void GDALGroup::ParentDeleted()
    1530             : {
    1531           3 :     Deleted();
    1532           3 : }
    1533             : 
    1534             : //! @endcond
    1535             : 
    1536             : /************************************************************************/
    1537             : /*                     CheckValidAndErrorOutIfNot()                     */
    1538             : /************************************************************************/
    1539             : 
    1540             : //! @cond Doxygen_Suppress
    1541       41920 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1542             : {
    1543       41920 :     if (!m_bValid)
    1544             :     {
    1545          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1546             :                  "This object has been deleted. No action on it is possible");
    1547             :     }
    1548       41920 :     return m_bValid;
    1549             : }
    1550             : 
    1551             : //! @endcond
    1552             : 
    1553             : /************************************************************************/
    1554             : /*                       RecursivelyVisitArrays()                       */
    1555             : /************************************************************************/
    1556             : 
    1557             : /** Recursively visit arrays of this group and its subgroups.
    1558             :  *
    1559             :  * @param visitor Callback function called on visited arrays.
    1560             :  *
    1561             :  * @since GDAL 3.14
    1562             :  */
    1563           2 : void GDALGroup::RecursivelyVisitArrays(
    1564             :     std::function<void(const std::shared_ptr<GDALMDArray> &)> visitor)
    1565             : {
    1566           4 :     std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1567           4 :     auto poSelf = m_pSelf.lock();
    1568           2 :     if (poSelf)
    1569           2 :         oQueue.push(poSelf);
    1570           6 :     while (!oQueue.empty())
    1571             :     {
    1572           8 :         auto poGroup = oQueue.front();
    1573           4 :         oQueue.pop();
    1574             : 
    1575          10 :         for (const std::string &osName : poGroup->GetMDArrayNames())
    1576             :         {
    1577          12 :             auto poArray = poGroup->OpenMDArray(osName);
    1578           6 :             if (poArray)
    1579           6 :                 visitor(poArray);
    1580             :         }
    1581             : 
    1582           6 :         for (const std::string &osName : poGroup->GetGroupNames())
    1583             :         {
    1584           4 :             auto poSubGroup = poGroup->OpenGroup(osName);
    1585           2 :             if (poSubGroup)
    1586           2 :                 oQueue.push(poSubGroup);
    1587             :         }
    1588             :     }
    1589           2 : }

Generated by: LCOV version 1.14