LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4861 5379 90.4 %
Date: 2025-07-09 17:50:03 Functions: 471 546 86.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       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 <assert.h>
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include <list>
      18             : #include <queue>
      19             : #include <set>
      20             : #include <utility>
      21             : #include <time.h>
      22             : 
      23             : #include <cmath>
      24             : #include <ctype.h>  // isalnum
      25             : 
      26             : #include "cpl_error_internal.h"
      27             : #include "cpl_float.h"
      28             : #include "gdal_priv.h"
      29             : #include "gdal_pam.h"
      30             : #include "gdal_rat.h"
      31             : #include "gdal_utils.h"
      32             : #include "cpl_safemaths.hpp"
      33             : #include "memmultidim.h"
      34             : #include "ogrsf_frmts.h"
      35             : #include "gdalmultidim_priv.h"
      36             : 
      37             : #if defined(__clang__) || defined(_MSC_VER)
      38             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      39             : #endif
      40             : 
      41             : /************************************************************************/
      42             : /*                       GDALMDArrayUnscaled                            */
      43             : /************************************************************************/
      44             : 
      45             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      46             : {
      47             :   private:
      48             :     std::shared_ptr<GDALMDArray> m_poParent{};
      49             :     const GDALExtendedDataType m_dt;
      50             :     bool m_bHasNoData;
      51             :     const double m_dfScale;
      52             :     const double m_dfOffset;
      53             :     std::vector<GByte> m_abyRawNoData{};
      54             : 
      55             :   protected:
      56          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      57             :                                  double dfScale, double dfOffset,
      58             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      59          26 :         : GDALAbstractMDArray(std::string(),
      60          26 :                               "Unscaled view of " + poParent->GetFullName()),
      61             :           GDALPamMDArray(
      62          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      63          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      64          13 :           m_poParent(std::move(poParent)),
      65             :           m_dt(GDALExtendedDataType::Create(eDT)),
      66          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      67          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      68             :     {
      69          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      70             :         const auto eNonComplexDT =
      71          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      72          26 :         GDALCopyWords64(
      73          13 :             &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
      74             :             eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
      75          13 :             GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      76          13 :     }
      77             : 
      78             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      79             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      80             :                const GDALExtendedDataType &bufferDataType,
      81             :                void *pDstBuffer) const override;
      82             : 
      83             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      84             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      85             :                 const GDALExtendedDataType &bufferDataType,
      86             :                 const void *pSrcBuffer) override;
      87             : 
      88           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      89             :                      CSLConstList papszOptions) const override
      90             :     {
      91           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      92             :     }
      93             : 
      94             :   public:
      95             :     static std::shared_ptr<GDALMDArrayUnscaled>
      96          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
      97             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
      98             :     {
      99             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
     100          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
     101          13 :         newAr->SetSelf(newAr);
     102          13 :         return newAr;
     103             :     }
     104             : 
     105           1 :     bool IsWritable() const override
     106             :     {
     107           1 :         return m_poParent->IsWritable();
     108             :     }
     109             : 
     110          15 :     const std::string &GetFilename() const override
     111             :     {
     112          15 :         return m_poParent->GetFilename();
     113             :     }
     114             : 
     115             :     const std::vector<std::shared_ptr<GDALDimension>> &
     116         220 :     GetDimensions() const override
     117             :     {
     118         220 :         return m_poParent->GetDimensions();
     119             :     }
     120             : 
     121         103 :     const GDALExtendedDataType &GetDataType() const override
     122             :     {
     123         103 :         return m_dt;
     124             :     }
     125             : 
     126           1 :     const std::string &GetUnit() const override
     127             :     {
     128           1 :         return m_poParent->GetUnit();
     129             :     }
     130             : 
     131           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     132             :     {
     133           1 :         return m_poParent->GetSpatialRef();
     134             :     }
     135             : 
     136           6 :     const void *GetRawNoDataValue() const override
     137             :     {
     138           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     139             :     }
     140             : 
     141           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     142             :     {
     143           1 :         m_bHasNoData = true;
     144           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     145           1 :         return true;
     146             :     }
     147             : 
     148           4 :     std::vector<GUInt64> GetBlockSize() const override
     149             :     {
     150           4 :         return m_poParent->GetBlockSize();
     151             :     }
     152             : 
     153             :     std::shared_ptr<GDALAttribute>
     154           0 :     GetAttribute(const std::string &osName) const override
     155             :     {
     156           0 :         return m_poParent->GetAttribute(osName);
     157             :     }
     158             : 
     159             :     std::vector<std::shared_ptr<GDALAttribute>>
     160           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     161             :     {
     162           1 :         return m_poParent->GetAttributes(papszOptions);
     163             :     }
     164             : 
     165           0 :     bool SetUnit(const std::string &osUnit) override
     166             :     {
     167           0 :         return m_poParent->SetUnit(osUnit);
     168             :     }
     169             : 
     170           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     171             :     {
     172           0 :         return m_poParent->SetSpatialRef(poSRS);
     173             :     }
     174             : 
     175             :     std::shared_ptr<GDALAttribute>
     176           1 :     CreateAttribute(const std::string &osName,
     177             :                     const std::vector<GUInt64> &anDimensions,
     178             :                     const GDALExtendedDataType &oDataType,
     179             :                     CSLConstList papszOptions = nullptr) override
     180             :     {
     181           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     182           1 :                                            papszOptions);
     183             :     }
     184             : };
     185             : 
     186             : /************************************************************************/
     187             : /*                         ~GDALIHasAttribute()                         */
     188             : /************************************************************************/
     189             : 
     190             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     191             : 
     192             : /************************************************************************/
     193             : /*                            GetAttribute()                            */
     194             : /************************************************************************/
     195             : 
     196             : /** Return an attribute by its name.
     197             :  *
     198             :  * If the attribute does not exist, nullptr should be silently returned.
     199             :  *
     200             :  * @note Driver implementation: this method will fallback to
     201             :  * GetAttributeFromAttributes() is not explicitly implemented
     202             :  *
     203             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     204             :  *
     205             :  * This is the same as the C function GDALGroupGetAttribute() or
     206             :  * GDALMDArrayGetAttribute().
     207             :  *
     208             :  * @param osName Attribute name
     209             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     210             :  */
     211             : std::shared_ptr<GDALAttribute>
     212        1057 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     213             : {
     214        1057 :     return GetAttributeFromAttributes(osName);
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                       GetAttributeFromAttributes()                   */
     219             : /************************************************************************/
     220             : 
     221             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     222             :  */
     223             : std::shared_ptr<GDALAttribute>
     224        1057 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     225             : {
     226        2114 :     auto attrs(GetAttributes());
     227        5493 :     for (const auto &attr : attrs)
     228             :     {
     229        5174 :         if (attr->GetName() == osName)
     230         738 :             return attr;
     231             :     }
     232         319 :     return nullptr;
     233             : }
     234             : 
     235             : /************************************************************************/
     236             : /*                           GetAttributes()                            */
     237             : /************************************************************************/
     238             : 
     239             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     240             :  *
     241             :  * If the attribute does not exist, nullptr should be silently returned.
     242             :  *
     243             :  * @note Driver implementation: optionally implemented. If implemented,
     244             :  * GetAttribute() should also be implemented.
     245             :  *
     246             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     247             :  *
     248             :  * This is the same as the C function GDALGroupGetAttributes() or
     249             :  * GDALMDArrayGetAttributes().
     250             : 
     251             :  * @param papszOptions Driver specific options determining how attributes
     252             :  * should be retrieved. Pass nullptr for default behavior.
     253             :  *
     254             :  * @return the attributes.
     255             :  */
     256             : std::vector<std::shared_ptr<GDALAttribute>>
     257          44 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     258             : {
     259          44 :     return {};
     260             : }
     261             : 
     262             : /************************************************************************/
     263             : /*                             CreateAttribute()                         */
     264             : /************************************************************************/
     265             : 
     266             : /** Create an attribute within a GDALMDArray or GDALGroup.
     267             :  *
     268             :  * The attribute might not be "physically" created until a value is written
     269             :  * into it.
     270             :  *
     271             :  * Optionally implemented.
     272             :  *
     273             :  * Drivers known to implement it: MEM, netCDF
     274             :  *
     275             :  * This is the same as the C function GDALGroupCreateAttribute() or
     276             :  * GDALMDArrayCreateAttribute()
     277             :  *
     278             :  * @param osName Attribute name.
     279             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     280             :  *                     dimension first to the fastest varying dimension last.
     281             :  *                     Empty for a scalar attribute (common case)
     282             :  * @param oDataType  Attribute data type.
     283             :  * @param papszOptions Driver specific options determining how the attribute.
     284             :  * should be created.
     285             :  *
     286             :  * @return the new attribute, or nullptr if case of error
     287             :  */
     288           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     289             :     CPL_UNUSED const std::string &osName,
     290             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     291             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     292             :     CPL_UNUSED CSLConstList papszOptions)
     293             : {
     294           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     295             :              "CreateAttribute() not implemented");
     296           0 :     return nullptr;
     297             : }
     298             : 
     299             : /************************************************************************/
     300             : /*                          DeleteAttribute()                           */
     301             : /************************************************************************/
     302             : 
     303             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     304             :  *
     305             :  * Optionally implemented.
     306             :  *
     307             :  * After this call, if a previously obtained instance of the deleted object
     308             :  * is still alive, no method other than for freeing it should be invoked.
     309             :  *
     310             :  * Drivers known to implement it: MEM, netCDF
     311             :  *
     312             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     313             :  * GDALMDArrayDeleteAttribute()
     314             :  *
     315             :  * @param osName Attribute name.
     316             :  * @param papszOptions Driver specific options determining how the attribute.
     317             :  * should be deleted.
     318             :  *
     319             :  * @return true in case of success
     320             :  * @since GDAL 3.8
     321             :  */
     322           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     323             :                                         CPL_UNUSED CSLConstList papszOptions)
     324             : {
     325           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     326             :              "DeleteAttribute() not implemented");
     327           0 :     return false;
     328             : }
     329             : 
     330             : /************************************************************************/
     331             : /*                            GDALGroup()                               */
     332             : /************************************************************************/
     333             : 
     334             : //! @cond Doxygen_Suppress
     335        6919 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     336        6919 :                      const std::string &osContext)
     337        6919 :     : m_osName(osParentName.empty() ? "/" : osName),
     338             :       m_osFullName(
     339       13838 :           !osParentName.empty()
     340       10713 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     341             :               : "/"),
     342       17632 :       m_osContext(osContext)
     343             : {
     344        6919 : }
     345             : 
     346             : //! @endcond
     347             : 
     348             : /************************************************************************/
     349             : /*                            ~GDALGroup()                              */
     350             : /************************************************************************/
     351             : 
     352             : GDALGroup::~GDALGroup() = default;
     353             : 
     354             : /************************************************************************/
     355             : /*                          GetMDArrayNames()                           */
     356             : /************************************************************************/
     357             : 
     358             : /** Return the list of multidimensional array names contained in this group.
     359             :  *
     360             :  * @note Driver implementation: optionally implemented. If implemented,
     361             :  * OpenMDArray() should also be implemented.
     362             :  *
     363             :  * Drivers known to implement it: MEM, netCDF.
     364             :  *
     365             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     366             :  *
     367             :  * @param papszOptions Driver specific options determining how arrays
     368             :  * should be retrieved. Pass nullptr for default behavior.
     369             :  *
     370             :  * @return the array names.
     371             :  */
     372             : std::vector<std::string>
     373           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     374             : {
     375           0 :     return {};
     376             : }
     377             : 
     378             : /************************************************************************/
     379             : /*                     GetMDArrayFullNamesRecursive()                   */
     380             : /************************************************************************/
     381             : 
     382             : /** Return the list of multidimensional array full names contained in this
     383             :  * group and its subgroups.
     384             :  *
     385             :  * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
     386             :  *
     387             :  * @param papszGroupOptions Driver specific options determining how groups
     388             :  * should be retrieved. Pass nullptr for default behavior.
     389             :  * @param papszArrayOptions Driver specific options determining how arrays
     390             :  * should be retrieved. Pass nullptr for default behavior.
     391             :  *
     392             :  * @return the array full names.
     393             :  *
     394             :  * @since 3.11
     395             :  */
     396             : std::vector<std::string>
     397           3 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
     398             :                                         CSLConstList papszArrayOptions) const
     399             : {
     400           3 :     std::vector<std::string> ret;
     401           6 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     402           3 :     stackGroups.push_back(nullptr);  // nullptr means this
     403           9 :     while (!stackGroups.empty())
     404             :     {
     405          12 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     406           6 :         stackGroups.erase(stackGroups.begin());
     407           6 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     408          10 :         for (const std::string &arrayName :
     409          26 :              poCurGroup->GetMDArrayNames(papszArrayOptions))
     410             :         {
     411          20 :             std::string osFullName = poCurGroup->GetFullName();
     412          10 :             if (!osFullName.empty() && osFullName.back() != '/')
     413           3 :                 osFullName += '/';
     414          10 :             osFullName += arrayName;
     415          10 :             ret.push_back(std::move(osFullName));
     416             :         }
     417           6 :         auto insertionPoint = stackGroups.begin();
     418           3 :         for (const auto &osSubGroup :
     419          12 :              poCurGroup->GetGroupNames(papszGroupOptions))
     420             :         {
     421           6 :             auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
     422           3 :             if (poSubGroup)
     423           3 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     424             :         }
     425             :     }
     426             : 
     427           6 :     return ret;
     428             : }
     429             : 
     430             : /************************************************************************/
     431             : /*                            OpenMDArray()                             */
     432             : /************************************************************************/
     433             : 
     434             : /** Open and return a multidimensional array.
     435             :  *
     436             :  * @note Driver implementation: optionally implemented. If implemented,
     437             :  * GetMDArrayNames() should also be implemented.
     438             :  *
     439             :  * Drivers known to implement it: MEM, netCDF.
     440             :  *
     441             :  * This is the same as the C function GDALGroupOpenMDArray().
     442             :  *
     443             :  * @param osName Array name.
     444             :  * @param papszOptions Driver specific options determining how the array should
     445             :  * be opened.  Pass nullptr for default behavior.
     446             :  *
     447             :  * @return the array, or nullptr.
     448             :  */
     449             : std::shared_ptr<GDALMDArray>
     450           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     451             :                        CPL_UNUSED CSLConstList papszOptions) const
     452             : {
     453           0 :     return nullptr;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                           GetGroupNames()                            */
     458             : /************************************************************************/
     459             : 
     460             : /** Return the list of sub-groups contained in this group.
     461             :  *
     462             :  * @note Driver implementation: optionally implemented. If implemented,
     463             :  * OpenGroup() should also be implemented.
     464             :  *
     465             :  * Drivers known to implement it: MEM, netCDF.
     466             :  *
     467             :  * This is the same as the C function GDALGroupGetGroupNames().
     468             :  *
     469             :  * @param papszOptions Driver specific options determining how groups
     470             :  * should be retrieved. Pass nullptr for default behavior.
     471             :  *
     472             :  * @return the group names.
     473             :  */
     474             : std::vector<std::string>
     475           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     476             : {
     477           4 :     return {};
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                             OpenGroup()                              */
     482             : /************************************************************************/
     483             : 
     484             : /** Open and return a sub-group.
     485             :  *
     486             :  * @note Driver implementation: optionally implemented. If implemented,
     487             :  * GetGroupNames() should also be implemented.
     488             :  *
     489             :  * Drivers known to implement it: MEM, netCDF.
     490             :  *
     491             :  * This is the same as the C function GDALGroupOpenGroup().
     492             :  *
     493             :  * @param osName Sub-group name.
     494             :  * @param papszOptions Driver specific options determining how the sub-group
     495             :  * should be opened.  Pass nullptr for default behavior.
     496             :  *
     497             :  * @return the group, or nullptr.
     498             :  */
     499             : std::shared_ptr<GDALGroup>
     500           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     501             :                      CPL_UNUSED CSLConstList papszOptions) const
     502             : {
     503           4 :     return nullptr;
     504             : }
     505             : 
     506             : /************************************************************************/
     507             : /*                        GetVectorLayerNames()                         */
     508             : /************************************************************************/
     509             : 
     510             : /** Return the list of layer names contained in this group.
     511             :  *
     512             :  * @note Driver implementation: optionally implemented. If implemented,
     513             :  * OpenVectorLayer() should also be implemented.
     514             :  *
     515             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     516             :  *
     517             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     518             :  * GDALDataset::GetLayer() should then be used.
     519             :  *
     520             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     521             :  *
     522             :  * @param papszOptions Driver specific options determining how layers
     523             :  * should be retrieved. Pass nullptr for default behavior.
     524             :  *
     525             :  * @return the vector layer names.
     526             :  * @since GDAL 3.4
     527             :  */
     528             : std::vector<std::string>
     529           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     530             : {
     531           1 :     return {};
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                           OpenVectorLayer()                          */
     536             : /************************************************************************/
     537             : 
     538             : /** Open and return a vector layer.
     539             :  *
     540             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     541             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     542             :  * dataset (contrary to the general design of this class where objects can be
     543             :  * used independently of the object that returned them)
     544             :  *
     545             :  * @note Driver implementation: optionally implemented. If implemented,
     546             :  * GetVectorLayerNames() should also be implemented.
     547             :  *
     548             :  * Drivers known to implement it: MEM, netCDF.
     549             :  *
     550             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     551             :  *
     552             :  * @param osName Vector layer name.
     553             :  * @param papszOptions Driver specific options determining how the layer should
     554             :  * be opened.  Pass nullptr for default behavior.
     555             :  *
     556             :  * @return the group, or nullptr.
     557             :  */
     558           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     559             :                                      CPL_UNUSED CSLConstList papszOptions) const
     560             : {
     561           2 :     return nullptr;
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                             GetDimensions()                          */
     566             : /************************************************************************/
     567             : 
     568             : /** Return the list of dimensions contained in this group and used by its
     569             :  * arrays.
     570             :  *
     571             :  * This is for dimensions that can potentially be used by several arrays.
     572             :  * Not all drivers might implement this. To retrieve the dimensions used by
     573             :  * a specific array, use GDALMDArray::GetDimensions().
     574             :  *
     575             :  * Drivers known to implement it: MEM, netCDF
     576             :  *
     577             :  * This is the same as the C function GDALGroupGetDimensions().
     578             :  *
     579             :  * @param papszOptions Driver specific options determining how groups
     580             :  * should be retrieved. Pass nullptr for default behavior.
     581             :  *
     582             :  * @return the dimensions.
     583             :  */
     584             : std::vector<std::shared_ptr<GDALDimension>>
     585          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     586             : {
     587          11 :     return {};
     588             : }
     589             : 
     590             : /************************************************************************/
     591             : /*                         GetStructuralInfo()                          */
     592             : /************************************************************************/
     593             : 
     594             : /** Return structural information on the group.
     595             :  *
     596             :  * This may be the compression, etc..
     597             :  *
     598             :  * The return value should not be freed and is valid until GDALGroup is
     599             :  * released or this function called again.
     600             :  *
     601             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     602             :  */
     603          41 : CSLConstList GDALGroup::GetStructuralInfo() const
     604             : {
     605          41 :     return nullptr;
     606             : }
     607             : 
     608             : /************************************************************************/
     609             : /*                              CreateGroup()                           */
     610             : /************************************************************************/
     611             : 
     612             : /** Create a sub-group within a group.
     613             :  *
     614             :  * Optionally implemented by drivers.
     615             :  *
     616             :  * Drivers known to implement it: MEM, netCDF
     617             :  *
     618             :  * This is the same as the C function GDALGroupCreateGroup().
     619             :  *
     620             :  * @param osName Sub-group name.
     621             :  * @param papszOptions Driver specific options determining how the sub-group
     622             :  * should be created.
     623             :  *
     624             :  * @return the new sub-group, or nullptr in case of error.
     625             :  */
     626             : std::shared_ptr<GDALGroup>
     627           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     628             :                        CPL_UNUSED CSLConstList papszOptions)
     629             : {
     630           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     631           0 :     return nullptr;
     632             : }
     633             : 
     634             : /************************************************************************/
     635             : /*                          DeleteGroup()                               */
     636             : /************************************************************************/
     637             : 
     638             : /** Delete a sub-group from a group.
     639             :  *
     640             :  * Optionally implemented.
     641             :  *
     642             :  * After this call, if a previously obtained instance of the deleted object
     643             :  * is still alive, no method other than for freeing it should be invoked.
     644             :  *
     645             :  * Drivers known to implement it: MEM, Zarr
     646             :  *
     647             :  * This is the same as the C function GDALGroupDeleteGroup().
     648             :  *
     649             :  * @param osName Sub-group name.
     650             :  * @param papszOptions Driver specific options determining how the group.
     651             :  * should be deleted.
     652             :  *
     653             :  * @return true in case of success
     654             :  * @since GDAL 3.8
     655             :  */
     656           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     657             :                             CPL_UNUSED CSLConstList papszOptions)
     658             : {
     659           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     660           0 :     return false;
     661             : }
     662             : 
     663             : /************************************************************************/
     664             : /*                            CreateDimension()                         */
     665             : /************************************************************************/
     666             : 
     667             : /** Create a dimension within a group.
     668             :  *
     669             :  * @note Driver implementation: drivers supporting CreateDimension() should
     670             :  * implement this method, but do not have necessarily to implement
     671             :  * GDALGroup::GetDimensions().
     672             :  *
     673             :  * Drivers known to implement it: MEM, netCDF
     674             :  *
     675             :  * This is the same as the C function GDALGroupCreateDimension().
     676             :  *
     677             :  * @param osName Dimension name.
     678             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     679             :  * @param osDirection Dimension direction (might be empty, and ignored by
     680             :  * drivers)
     681             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     682             :  * @param papszOptions Driver specific options determining how the dimension
     683             :  * should be created.
     684             :  *
     685             :  * @return the new dimension, or nullptr if case of error
     686             :  */
     687           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     688             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     689             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     690             :     CPL_UNUSED CSLConstList papszOptions)
     691             : {
     692           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     693             :              "CreateDimension() not implemented");
     694           0 :     return nullptr;
     695             : }
     696             : 
     697             : /************************************************************************/
     698             : /*                             CreateMDArray()                          */
     699             : /************************************************************************/
     700             : 
     701             : /** Create a multidimensional array within a group.
     702             :  *
     703             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     704             :  * belong to this group, either by retrieving them with GetDimensions()
     705             :  * or creating a new one with CreateDimension().
     706             :  *
     707             :  * Optionally implemented.
     708             :  *
     709             :  * Drivers known to implement it: MEM, netCDF
     710             :  *
     711             :  * This is the same as the C function GDALGroupCreateMDArray().
     712             :  *
     713             :  * @note Driver implementation: drivers should take into account the possibility
     714             :  * that GDALDimension object passed in aoDimensions might belong to a different
     715             :  * group / dataset / driver and act accordingly.
     716             :  *
     717             :  * @param osName Array name.
     718             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     719             :  *                     dimension first to the fastest varying dimension last.
     720             :  *                     Might be empty for a scalar array (if supported by
     721             :  * driver)
     722             :  * @param oDataType  Array data type.
     723             :  * @param papszOptions Driver specific options determining how the array
     724             :  * should be created.
     725             :  *
     726             :  * @return the new array, or nullptr in case of error
     727             :  */
     728           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     729             :     CPL_UNUSED const std::string &osName,
     730             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     731             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     732             :     CPL_UNUSED CSLConstList papszOptions)
     733             : {
     734           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     735           0 :     return nullptr;
     736             : }
     737             : 
     738             : /************************************************************************/
     739             : /*                          DeleteMDArray()                             */
     740             : /************************************************************************/
     741             : 
     742             : /** Delete an array from a group.
     743             :  *
     744             :  * Optionally implemented.
     745             :  *
     746             :  * After this call, if a previously obtained instance of the deleted object
     747             :  * is still alive, no method other than for freeing it should be invoked.
     748             :  *
     749             :  * Drivers known to implement it: MEM, Zarr
     750             :  *
     751             :  * This is the same as the C function GDALGroupDeleteMDArray().
     752             :  *
     753             :  * @param osName Arrayname.
     754             :  * @param papszOptions Driver specific options determining how the array.
     755             :  * should be deleted.
     756             :  *
     757             :  * @return true in case of success
     758             :  * @since GDAL 3.8
     759             :  */
     760           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     761             :                               CPL_UNUSED CSLConstList papszOptions)
     762             : {
     763           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     764           0 :     return false;
     765             : }
     766             : 
     767             : /************************************************************************/
     768             : /*                           GetTotalCopyCost()                         */
     769             : /************************************************************************/
     770             : 
     771             : /** Return a total "cost" to copy the group.
     772             :  *
     773             :  * Used as a parameter for CopFrom()
     774             :  */
     775          25 : GUInt64 GDALGroup::GetTotalCopyCost() const
     776             : {
     777          25 :     GUInt64 nCost = COPY_COST;
     778          25 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     779             : 
     780          50 :     auto groupNames = GetGroupNames();
     781          31 :     for (const auto &name : groupNames)
     782             :     {
     783          12 :         auto subGroup = OpenGroup(name);
     784           6 :         if (subGroup)
     785             :         {
     786           6 :             nCost += subGroup->GetTotalCopyCost();
     787             :         }
     788             :     }
     789             : 
     790          25 :     auto arrayNames = GetMDArrayNames();
     791          71 :     for (const auto &name : arrayNames)
     792             :     {
     793          92 :         auto array = OpenMDArray(name);
     794          46 :         if (array)
     795             :         {
     796          46 :             nCost += array->GetTotalCopyCost();
     797             :         }
     798             :     }
     799          50 :     return nCost;
     800             : }
     801             : 
     802             : /************************************************************************/
     803             : /*                               CopyFrom()                             */
     804             : /************************************************************************/
     805             : 
     806             : /** Copy the content of a group into a new (generally empty) group.
     807             :  *
     808             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     809             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     810             :  *                   of some output drivers this is not recommended)
     811             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     812             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     813             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     814             :  *                be pursued.
     815             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     816             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     817             :  * @param pfnProgress Progress callback, or nullptr.
     818             :  * @param pProgressData Progress user data, or nulptr.
     819             :  * @param papszOptions Creation options. Currently, only array creation
     820             :  *                     options are supported. They must be prefixed with
     821             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     822             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     823             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     824             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     825             :  *                     Restriction to arrays of a given name is done with adding
     826             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     827             :  *                     a full qualified name.
     828             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     829             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     830             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     831             :  * and maximum of the source array. The integer data type used can be set with
     832             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     833             :  *
     834             :  * @return true in case of success (or partial success if bStrict == false).
     835             :  */
     836          25 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     837             :                          GDALDataset *poSrcDS,
     838             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     839             :                          bool bStrict, GUInt64 &nCurCost,
     840             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     841             :                          void *pProgressData, CSLConstList papszOptions)
     842             : {
     843          25 :     if (pfnProgress == nullptr)
     844           0 :         pfnProgress = GDALDummyProgress;
     845             : 
     846             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     847             :     if (!(x))                                                                  \
     848             :     {                                                                          \
     849             :         if (bStrict)                                                           \
     850             :             return false;                                                      \
     851             :         continue;                                                              \
     852             :     }                                                                          \
     853             :     (void)0
     854             : 
     855             :     try
     856             :     {
     857          25 :         nCurCost += GDALGroup::COPY_COST;
     858             : 
     859          50 :         const auto srcDims = poSrcGroup->GetDimensions();
     860             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     861          50 :             mapExistingDstDims;
     862          50 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     863          63 :         for (const auto &dim : srcDims)
     864             :         {
     865             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     866          38 :                                           dim->GetDirection(), dim->GetSize());
     867          38 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     868          38 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     869          76 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     870          38 :             if (poIndexingVarSrc)
     871             :             {
     872             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     873          20 :                                                        ->GetName()] =
     874          40 :                     dim->GetName();
     875             :             }
     876             :         }
     877             : 
     878          50 :         auto attrs = poSrcGroup->GetAttributes();
     879          32 :         for (const auto &attr : attrs)
     880             :         {
     881             :             auto dstAttr =
     882           7 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     883          14 :                                 attr->GetDataType());
     884           7 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     885           7 :             auto raw(attr->ReadAsRaw());
     886           7 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     887           0 :                 return false;
     888             :         }
     889          25 :         if (!attrs.empty())
     890             :         {
     891           5 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     892           5 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     893           0 :                 return false;
     894             :         }
     895             : 
     896             :         const auto CopyArray =
     897          46 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     898             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     899             :              papszOptions, bStrict, &nCurCost,
     900         417 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     901             :         {
     902             :             // Map source dimensions to target dimensions
     903          92 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     904          46 :             const auto &srcArrayDims(srcArray->GetDimensions());
     905         118 :             for (const auto &dim : srcArrayDims)
     906             :             {
     907             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     908          72 :                     dim->GetFullName());
     909          72 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     910             :                 {
     911          62 :                     dstArrayDims.emplace_back(dstDim);
     912             :                 }
     913             :                 else
     914             :                 {
     915          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     916          19 :                     if (oIter != mapExistingDstDims.end() &&
     917           9 :                         oIter->second->GetSize() == dim->GetSize())
     918             :                     {
     919           8 :                         dstArrayDims.emplace_back(oIter->second);
     920             :                     }
     921             :                     else
     922             :                     {
     923           2 :                         std::string newDimName;
     924           2 :                         if (oIter == mapExistingDstDims.end())
     925             :                         {
     926           1 :                             newDimName = dim->GetName();
     927             :                         }
     928             :                         else
     929             :                         {
     930           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     931           3 :                                                          '_' + dim->GetName());
     932           1 :                             newDimName = newDimNamePrefix;
     933           1 :                             int nIterCount = 2;
     934           0 :                             while (
     935           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     936             :                             {
     937           0 :                                 newDimName = newDimNamePrefix +
     938           0 :                                              CPLSPrintf("_%d", nIterCount);
     939           0 :                                 nIterCount++;
     940             :                             }
     941             :                         }
     942           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     943             :                                                  dim->GetDirection(),
     944           4 :                                                  dim->GetSize());
     945           2 :                         if (!dstDim)
     946           0 :                             return false;
     947           2 :                         mapExistingDstDims[newDimName] = dstDim;
     948           2 :                         dstArrayDims.emplace_back(dstDim);
     949             :                     }
     950             :                 }
     951             :             }
     952             : 
     953          92 :             CPLStringList aosArrayCO;
     954          46 :             bool bAutoScale = false;
     955          46 :             GDALDataType eAutoScaleType = GDT_UInt16;
     956          53 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     957             :             {
     958           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     959             :                 {
     960           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     961           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     962             :                     {
     963           1 :                         const char *pszNext = strchr(pszOption, ':');
     964           1 :                         if (pszNext != nullptr)
     965             :                         {
     966           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     967           1 :                             if (static_cast<size_t>(nDim) ==
     968           1 :                                 dstArrayDims.size())
     969             :                             {
     970           1 :                                 pszOption = pszNext + 1;
     971             :                             }
     972             :                             else
     973             :                             {
     974           0 :                                 pszOption = nullptr;
     975             :                             }
     976             :                         }
     977             :                     }
     978           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     979             :                     {
     980           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     981           2 :                         const char *pszNext = strchr(pszName, ':');
     982           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     983           2 :                             pszNext[-1] == ')')
     984             :                         {
     985           4 :                             CPLString osName;
     986           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     987           3 :                             if (osName == srcArray->GetName() ||
     988           1 :                                 osName == srcArray->GetFullName())
     989             :                             {
     990           2 :                                 pszOption = pszNext + 1;
     991             :                             }
     992             :                             else
     993             :                             {
     994           0 :                                 pszOption = nullptr;
     995             :                             }
     996             :                         }
     997             :                     }
     998           7 :                     if (pszOption)
     999             :                     {
    1000           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
    1001             :                         {
    1002             :                             bAutoScale =
    1003           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
    1004             :                         }
    1005           5 :                         else if (STARTS_WITH_CI(pszOption,
    1006             :                                                 "AUTOSCALE_DATA_TYPE="))
    1007             :                         {
    1008           1 :                             const char *pszDataType =
    1009             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
    1010           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
    1011           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
    1012           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
    1013             :                             {
    1014           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
    1015             :                                          "Unsupported value for "
    1016             :                                          "AUTOSCALE_DATA_TYPE");
    1017           0 :                                 return false;
    1018             :                             }
    1019             :                         }
    1020             :                         else
    1021             :                         {
    1022           4 :                             aosArrayCO.AddString(pszOption);
    1023             :                         }
    1024             :                     }
    1025             :                 }
    1026             :             }
    1027             : 
    1028             :             auto oIterDimName =
    1029          46 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
    1030          46 :             const auto &srcArrayType = srcArray->GetDataType();
    1031             : 
    1032          46 :             std::shared_ptr<GDALMDArray> dstArray;
    1033             : 
    1034             :             // Only autoscale non-indexing variables
    1035          46 :             bool bHasOffset = false;
    1036          46 :             bool bHasScale = false;
    1037           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
    1038           4 :                 (srcArrayType.GetNumericDataType() == GDT_Float16 ||
    1039           2 :                  srcArrayType.GetNumericDataType() == GDT_Float32 ||
    1040           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
    1041           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
    1042          50 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
    1043          48 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
    1044             :             {
    1045           2 :                 constexpr bool bApproxOK = false;
    1046           2 :                 constexpr bool bForce = true;
    1047           2 :                 double dfMin = 0.0;
    1048           2 :                 double dfMax = 0.0;
    1049           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
    1050             :                                             nullptr, nullptr, nullptr, nullptr,
    1051           2 :                                             nullptr) != CE_None)
    1052             :                 {
    1053           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1054             :                              "Could not retrieve statistics for array %s",
    1055           0 :                              srcArray->GetName().c_str());
    1056           0 :                     return false;
    1057             :                 }
    1058           2 :                 double dfDTMin = 0;
    1059           2 :                 double dfDTMax = 0;
    1060             : #define setDTMinMax(ctype)                                                     \
    1061             :     do                                                                         \
    1062             :     {                                                                          \
    1063             :         dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
    1064             :         dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
    1065             :     } while (0)
    1066             : 
    1067           2 :                 switch (eAutoScaleType)
    1068             :                 {
    1069           0 :                     case GDT_Byte:
    1070           0 :                         setDTMinMax(GByte);
    1071           0 :                         break;
    1072           0 :                     case GDT_Int8:
    1073           0 :                         setDTMinMax(GInt8);
    1074           0 :                         break;
    1075           1 :                     case GDT_UInt16:
    1076           1 :                         setDTMinMax(GUInt16);
    1077           1 :                         break;
    1078           1 :                     case GDT_Int16:
    1079           1 :                         setDTMinMax(GInt16);
    1080           1 :                         break;
    1081           0 :                     case GDT_UInt32:
    1082           0 :                         setDTMinMax(GUInt32);
    1083           0 :                         break;
    1084           0 :                     case GDT_Int32:
    1085           0 :                         setDTMinMax(GInt32);
    1086           0 :                         break;
    1087           0 :                     case GDT_UInt64:
    1088           0 :                         setDTMinMax(std::uint64_t);
    1089           0 :                         break;
    1090           0 :                     case GDT_Int64:
    1091           0 :                         setDTMinMax(std::int64_t);
    1092           0 :                         break;
    1093           0 :                     case GDT_Float16:
    1094             :                     case GDT_Float32:
    1095             :                     case GDT_Float64:
    1096             :                     case GDT_Unknown:
    1097             :                     case GDT_CInt16:
    1098             :                     case GDT_CInt32:
    1099             :                     case GDT_CFloat16:
    1100             :                     case GDT_CFloat32:
    1101             :                     case GDT_CFloat64:
    1102             :                     case GDT_TypeCount:
    1103           0 :                         CPLAssert(false);
    1104             :                 }
    1105             : 
    1106             :                 dstArray =
    1107           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1108           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1109           4 :                                   aosArrayCO.List());
    1110           2 :                 if (!dstArray)
    1111           0 :                     return !bStrict;
    1112             : 
    1113           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1114             :                 {
    1115             :                     // If there's a nodata value in the source array, reserve
    1116             :                     // DTMax for that purpose in the target scaled array
    1117           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1118             :                     {
    1119           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1120             :                                  "Cannot set nodata value");
    1121           0 :                         return false;
    1122             :                     }
    1123           1 :                     dfDTMax--;
    1124             :                 }
    1125           2 :                 const double dfScale =
    1126           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1127           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1128             : 
    1129           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1130           2 :                     !dstArray->SetScale(dfScale))
    1131             :                 {
    1132           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1133             :                              "Cannot set scale/offset");
    1134           0 :                     return false;
    1135             :                 }
    1136             : 
    1137           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1138           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1139             :                 {
    1140           1 :                     poUnscaled->SetNoDataValue(
    1141             :                         srcArray->GetNoDataValueAsDouble());
    1142             :                 }
    1143             : 
    1144             :                 // Copy source array into unscaled array
    1145           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1146             :                                           nCurCost, nTotalCost, pfnProgress,
    1147           2 :                                           pProgressData))
    1148             :                 {
    1149           0 :                     return false;
    1150             :                 }
    1151             :             }
    1152             :             else
    1153             :             {
    1154          88 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1155          88 :                                          srcArrayType, aosArrayCO.List());
    1156          44 :                 if (!dstArray)
    1157           0 :                     return !bStrict;
    1158             : 
    1159          88 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1160             :                                         nCurCost, nTotalCost, pfnProgress,
    1161          44 :                                         pProgressData))
    1162             :                 {
    1163           0 :                     return false;
    1164             :                 }
    1165             :             }
    1166             : 
    1167             :             // If this array is the indexing variable of a dimension, link them
    1168             :             // together.
    1169          46 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1170             :             {
    1171             :                 auto oCorrespondingDimIter =
    1172          20 :                     mapExistingDstDims.find(oIterDimName->second);
    1173          20 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1174             :                 {
    1175             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1176          20 :                         CPLQuietErrorHandler);
    1177          40 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1178          20 :                         std::move(dstArray));
    1179             :                 }
    1180             :             }
    1181             : 
    1182          46 :             return true;
    1183          25 :         };
    1184             : 
    1185          50 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1186             : 
    1187             :         // Start by copying arrays that are indexing variables of dimensions
    1188          71 :         for (const auto &name : arrayNames)
    1189             :         {
    1190          46 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1191          46 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1192             : 
    1193          46 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1194          46 :                               srcArray->GetName()))
    1195             :             {
    1196          20 :                 if (!CopyArray(srcArray))
    1197           0 :                     return false;
    1198             :             }
    1199             :         }
    1200             : 
    1201             :         // Then copy regular arrays
    1202          71 :         for (const auto &name : arrayNames)
    1203             :         {
    1204          46 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1205          46 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1206             : 
    1207          46 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1208          46 :                                srcArray->GetName()))
    1209             :             {
    1210          26 :                 if (!CopyArray(srcArray))
    1211           0 :                     return false;
    1212             :             }
    1213             :         }
    1214             : 
    1215          50 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1216          31 :         for (const auto &name : groupNames)
    1217             :         {
    1218           6 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1219           6 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1220           6 :             auto dstSubGroup = CreateGroup(name);
    1221           6 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1222          12 :             if (!dstSubGroup->CopyFrom(
    1223             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1224           6 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1225           0 :                 return false;
    1226             :         }
    1227             : 
    1228          25 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1229           0 :             return false;
    1230             : 
    1231          25 :         return true;
    1232             :     }
    1233           0 :     catch (const std::exception &e)
    1234             :     {
    1235           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1236           0 :         return false;
    1237             :     }
    1238             : }
    1239             : 
    1240             : /************************************************************************/
    1241             : /*                         GetInnerMostGroup()                          */
    1242             : /************************************************************************/
    1243             : 
    1244             : //! @cond Doxygen_Suppress
    1245             : const GDALGroup *
    1246        1285 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1247             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1248             :                              std::string &osLastPart) const
    1249             : {
    1250        1285 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1251           6 :         return nullptr;
    1252        1279 :     const GDALGroup *poCurGroup = this;
    1253             :     CPLStringList aosTokens(
    1254        2558 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1255        1279 :     if (aosTokens.size() == 0)
    1256             :     {
    1257           0 :         return nullptr;
    1258             :     }
    1259             : 
    1260        1620 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1261             :     {
    1262         353 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1263         353 :         if (!curGroupHolder)
    1264             :         {
    1265          12 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1266             :                      aosTokens[i]);
    1267          12 :             return nullptr;
    1268             :         }
    1269         341 :         poCurGroup = curGroupHolder.get();
    1270             :     }
    1271        1267 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1272        1267 :     return poCurGroup;
    1273             : }
    1274             : 
    1275             : //! @endcond
    1276             : 
    1277             : /************************************************************************/
    1278             : /*                      OpenMDArrayFromFullname()                       */
    1279             : /************************************************************************/
    1280             : 
    1281             : /** Get an array from its fully qualified name */
    1282             : std::shared_ptr<GDALMDArray>
    1283         541 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1284             :                                    CSLConstList papszOptions) const
    1285             : {
    1286        1082 :     std::string osName;
    1287         541 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1288         541 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1289         541 :     if (poGroup == nullptr)
    1290          12 :         return nullptr;
    1291         529 :     return poGroup->OpenMDArray(osName, papszOptions);
    1292             : }
    1293             : 
    1294             : /************************************************************************/
    1295             : /*                      OpenAttributeFromFullname()                     */
    1296             : /************************************************************************/
    1297             : 
    1298             : /** Get an attribute from its fully qualified name */
    1299             : std::shared_ptr<GDALAttribute>
    1300           9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
    1301             :                                      CSLConstList papszOptions) const
    1302             : {
    1303           9 :     const auto pos = osFullName.rfind('/');
    1304           9 :     if (pos == std::string::npos)
    1305           0 :         return nullptr;
    1306          18 :     const std::string attrName = osFullName.substr(pos + 1);
    1307           9 :     if (pos == 0)
    1308           2 :         return GetAttribute(attrName);
    1309          14 :     const std::string container = osFullName.substr(0, pos);
    1310          14 :     auto poArray = OpenMDArrayFromFullname(container, papszOptions);
    1311           7 :     if (poArray)
    1312           4 :         return poArray->GetAttribute(attrName);
    1313           6 :     auto poGroup = OpenGroupFromFullname(container, papszOptions);
    1314           3 :     if (poGroup)
    1315           1 :         return poGroup->GetAttribute(attrName);
    1316           2 :     return nullptr;
    1317             : }
    1318             : 
    1319             : /************************************************************************/
    1320             : /*                          ResolveMDArray()                            */
    1321             : /************************************************************************/
    1322             : 
    1323             : /** Locate an array in a group and its subgroups by name.
    1324             :  *
    1325             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1326             :  * used
    1327             :  * Otherwise the search will start from the group identified by osStartingPath,
    1328             :  * and an array whose name is osName will be looked for in this group (if
    1329             :  * osStartingPath is empty or "/", then the current group is used). If there
    1330             :  * is no match, then a recursive descendent search will be made in its
    1331             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1332             :  * existing) of the group pointed by osStartingPath will be used as the new
    1333             :  * starting point for the search.
    1334             :  *
    1335             :  * @param osName name, qualified or not
    1336             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1337             :  *                       the search should be started. If this is a non-empty
    1338             :  *                       string, the group on which this method is called should
    1339             :  *                       nominally be the root group (otherwise the path will
    1340             :  *                       be interpreted as from the current group)
    1341             :  * @param papszOptions options to pass to OpenMDArray()
    1342             :  * @since GDAL 3.2
    1343             :  */
    1344             : std::shared_ptr<GDALMDArray>
    1345          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1346             :                           const std::string &osStartingPath,
    1347             :                           CSLConstList papszOptions) const
    1348             : {
    1349          19 :     if (!osName.empty() && osName[0] == '/')
    1350             :     {
    1351           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1352           1 :         if (poArray)
    1353           1 :             return poArray;
    1354             :     }
    1355          36 :     std::string osPath(osStartingPath);
    1356          36 :     std::set<std::string> oSetAlreadyVisited;
    1357             : 
    1358             :     while (true)
    1359             :     {
    1360           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1361           0 :         std::shared_ptr<GDALGroup> poGroup;
    1362             : 
    1363          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1364          22 :         bool goOn = false;
    1365          22 :         if (osPath.empty() || osPath == "/")
    1366             :         {
    1367          11 :             goOn = true;
    1368             :         }
    1369             :         else
    1370             :         {
    1371          22 :             std::string osLastPart;
    1372             :             const GDALGroup *poGroupPtr =
    1373          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1374          11 :             if (poGroupPtr)
    1375          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1376          22 :             if (poGroup &&
    1377          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1378             :             {
    1379          11 :                 oQueue.push(poGroup);
    1380          11 :                 goOn = true;
    1381             :             }
    1382             :         }
    1383             : 
    1384          22 :         if (goOn)
    1385             :         {
    1386          17 :             do
    1387             :             {
    1388             :                 const GDALGroup *groupPtr;
    1389          39 :                 if (!oQueue.empty())
    1390             :                 {
    1391          28 :                     poGroup = oQueue.front();
    1392          28 :                     oQueue.pop();
    1393          28 :                     groupPtr = poGroup.get();
    1394             :                 }
    1395             :                 else
    1396             :                 {
    1397          11 :                     groupPtr = this;
    1398             :                 }
    1399             : 
    1400          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1401          39 :                 if (poArray)
    1402          16 :                     return poArray;
    1403             : 
    1404          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1405          47 :                 for (const auto &osGroupName : aosGroupNames)
    1406             :                 {
    1407          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1408          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1409          48 :                                                      poSubGroup->GetFullName()))
    1410             :                     {
    1411          24 :                         oQueue.push(poSubGroup);
    1412          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1413             :                     }
    1414             :                 }
    1415          23 :             } while (!oQueue.empty());
    1416             :         }
    1417             : 
    1418           6 :         if (osPath.empty() || osPath == "/")
    1419           2 :             break;
    1420             : 
    1421           4 :         const auto nPos = osPath.rfind('/');
    1422           4 :         if (nPos == 0)
    1423           1 :             osPath = "/";
    1424             :         else
    1425             :         {
    1426           3 :             if (nPos == std::string::npos)
    1427           0 :                 break;
    1428           3 :             osPath.resize(nPos);
    1429             :         }
    1430           4 :     }
    1431           2 :     return nullptr;
    1432             : }
    1433             : 
    1434             : /************************************************************************/
    1435             : /*                       OpenGroupFromFullname()                        */
    1436             : /************************************************************************/
    1437             : 
    1438             : /** Get a group from its fully qualified name.
    1439             :  * @since GDAL 3.2
    1440             :  */
    1441             : std::shared_ptr<GDALGroup>
    1442         566 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1443             :                                  CSLConstList papszOptions) const
    1444             : {
    1445        1132 :     std::string osName;
    1446         566 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1447         566 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1448         566 :     if (poGroup == nullptr)
    1449           4 :         return nullptr;
    1450         562 :     return poGroup->OpenGroup(osName, papszOptions);
    1451             : }
    1452             : 
    1453             : /************************************************************************/
    1454             : /*                      OpenDimensionFromFullname()                     */
    1455             : /************************************************************************/
    1456             : 
    1457             : /** Get a dimension from its fully qualified name */
    1458             : std::shared_ptr<GDALDimension>
    1459         167 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1460             : {
    1461         334 :     std::string osName;
    1462         167 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1463         167 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1464         167 :     if (poGroup == nullptr)
    1465           2 :         return nullptr;
    1466         330 :     auto dims(poGroup->GetDimensions());
    1467         286 :     for (auto &dim : dims)
    1468             :     {
    1469         241 :         if (dim->GetName() == osName)
    1470         120 :             return dim;
    1471             :     }
    1472          45 :     return nullptr;
    1473             : }
    1474             : 
    1475             : /************************************************************************/
    1476             : /*                           ClearStatistics()                          */
    1477             : /************************************************************************/
    1478             : 
    1479             : /**
    1480             :  * \brief Clear statistics.
    1481             :  *
    1482             :  * @since GDAL 3.4
    1483             :  */
    1484           0 : void GDALGroup::ClearStatistics()
    1485             : {
    1486           0 :     auto groupNames = GetGroupNames();
    1487           0 :     for (const auto &name : groupNames)
    1488             :     {
    1489           0 :         auto subGroup = OpenGroup(name);
    1490           0 :         if (subGroup)
    1491             :         {
    1492           0 :             subGroup->ClearStatistics();
    1493             :         }
    1494             :     }
    1495             : 
    1496           0 :     auto arrayNames = GetMDArrayNames();
    1497           0 :     for (const auto &name : arrayNames)
    1498             :     {
    1499           0 :         auto array = OpenMDArray(name);
    1500           0 :         if (array)
    1501             :         {
    1502           0 :             array->ClearStatistics();
    1503             :         }
    1504             :     }
    1505           0 : }
    1506             : 
    1507             : /************************************************************************/
    1508             : /*                            Rename()                                  */
    1509             : /************************************************************************/
    1510             : 
    1511             : /** Rename the group.
    1512             :  *
    1513             :  * This is not implemented by all drivers.
    1514             :  *
    1515             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1516             :  *
    1517             :  * This is the same as the C function GDALGroupRename().
    1518             :  *
    1519             :  * @param osNewName New name.
    1520             :  *
    1521             :  * @return true in case of success
    1522             :  * @since GDAL 3.8
    1523             :  */
    1524           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1525             : {
    1526           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1527           0 :     return false;
    1528             : }
    1529             : 
    1530             : /************************************************************************/
    1531             : /*                         BaseRename()                                 */
    1532             : /************************************************************************/
    1533             : 
    1534             : //! @cond Doxygen_Suppress
    1535           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1536             : {
    1537           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1538           8 :     m_osFullName += osNewName;
    1539           8 :     m_osName = osNewName;
    1540             : 
    1541           8 :     NotifyChildrenOfRenaming();
    1542           8 : }
    1543             : 
    1544             : //! @endcond
    1545             : 
    1546             : /************************************************************************/
    1547             : /*                        ParentRenamed()                               */
    1548             : /************************************************************************/
    1549             : 
    1550             : //! @cond Doxygen_Suppress
    1551           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1552             : {
    1553           7 :     m_osFullName = osNewParentFullName;
    1554           7 :     m_osFullName += "/";
    1555           7 :     m_osFullName += m_osName;
    1556             : 
    1557           7 :     NotifyChildrenOfRenaming();
    1558           7 : }
    1559             : 
    1560             : //! @endcond
    1561             : 
    1562             : /************************************************************************/
    1563             : /*                             Deleted()                                */
    1564             : /************************************************************************/
    1565             : 
    1566             : //! @cond Doxygen_Suppress
    1567          22 : void GDALGroup::Deleted()
    1568             : {
    1569          22 :     m_bValid = false;
    1570             : 
    1571          22 :     NotifyChildrenOfDeletion();
    1572          22 : }
    1573             : 
    1574             : //! @endcond
    1575             : 
    1576             : /************************************************************************/
    1577             : /*                        ParentDeleted()                               */
    1578             : /************************************************************************/
    1579             : 
    1580             : //! @cond Doxygen_Suppress
    1581           3 : void GDALGroup::ParentDeleted()
    1582             : {
    1583           3 :     Deleted();
    1584           3 : }
    1585             : 
    1586             : //! @endcond
    1587             : 
    1588             : /************************************************************************/
    1589             : /*                     CheckValidAndErrorOutIfNot()                     */
    1590             : /************************************************************************/
    1591             : 
    1592             : //! @cond Doxygen_Suppress
    1593       12487 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1594             : {
    1595       12487 :     if (!m_bValid)
    1596             :     {
    1597          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1598             :                  "This object has been deleted. No action on it is possible");
    1599             :     }
    1600       12487 :     return m_bValid;
    1601             : }
    1602             : 
    1603             : //! @endcond
    1604             : 
    1605             : /************************************************************************/
    1606             : /*                       ~GDALAbstractMDArray()                         */
    1607             : /************************************************************************/
    1608             : 
    1609             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1610             : 
    1611             : /************************************************************************/
    1612             : /*                        GDALAbstractMDArray()                         */
    1613             : /************************************************************************/
    1614             : 
    1615             : //! @cond Doxygen_Suppress
    1616       20969 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1617       20969 :                                          const std::string &osName)
    1618             :     : m_osName(osName),
    1619             :       m_osFullName(
    1620       20969 :           !osParentName.empty()
    1621       40235 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1622       82173 :               : osName)
    1623             : {
    1624       20969 : }
    1625             : 
    1626             : //! @endcond
    1627             : 
    1628             : /************************************************************************/
    1629             : /*                           GetDimensions()                            */
    1630             : /************************************************************************/
    1631             : 
    1632             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1633             :  * \brief Return the dimensions of an attribute/array.
    1634             :  *
    1635             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1636             :  * similar to GDALAttributeGetDimensionsSize().
    1637             :  */
    1638             : 
    1639             : /************************************************************************/
    1640             : /*                           GetDataType()                              */
    1641             : /************************************************************************/
    1642             : 
    1643             : /** \fn GDALAbstractMDArray::GetDataType() const
    1644             :  * \brief Return the data type of an attribute/array.
    1645             :  *
    1646             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1647             :  * GDALAttributeGetDataType()
    1648             :  */
    1649             : 
    1650             : /************************************************************************/
    1651             : /*                        GetDimensionCount()                           */
    1652             : /************************************************************************/
    1653             : 
    1654             : /** Return the number of dimensions.
    1655             :  *
    1656             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1657             :  * drivers if they have a faster / less expensive implementations.
    1658             :  *
    1659             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1660             :  * GDALAttributeGetDimensionCount().
    1661             :  *
    1662             :  */
    1663       23561 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1664             : {
    1665       23561 :     return GetDimensions().size();
    1666             : }
    1667             : 
    1668             : /************************************************************************/
    1669             : /*                            Rename()                                  */
    1670             : /************************************************************************/
    1671             : 
    1672             : /** Rename the attribute/array.
    1673             :  *
    1674             :  * This is not implemented by all drivers.
    1675             :  *
    1676             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1677             :  *
    1678             :  * This is the same as the C functions GDALMDArrayRename() or
    1679             :  * GDALAttributeRename().
    1680             :  *
    1681             :  * @param osNewName New name.
    1682             :  *
    1683             :  * @return true in case of success
    1684             :  * @since GDAL 3.8
    1685             :  */
    1686           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1687             : {
    1688           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1689           0 :     return false;
    1690             : }
    1691             : 
    1692             : /************************************************************************/
    1693             : /*                             CopyValue()                              */
    1694             : /************************************************************************/
    1695             : 
    1696             : /** Convert a value from a source type to a destination type.
    1697             :  *
    1698             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1699             :  * that must be freed with CPLFree().
    1700             :  */
    1701       80790 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1702             :                                      const GDALExtendedDataType &srcType,
    1703             :                                      void *pDst,
    1704             :                                      const GDALExtendedDataType &dstType)
    1705             : {
    1706      157666 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1707       76876 :         dstType.GetClass() == GEDTC_NUMERIC)
    1708             :     {
    1709       76411 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1710             :                         dstType.GetNumericDataType(), 0, 1);
    1711       76411 :         return true;
    1712             :     }
    1713        8077 :     if (srcType.GetClass() == GEDTC_STRING &&
    1714        3698 :         dstType.GetClass() == GEDTC_STRING)
    1715             :     {
    1716             :         const char *srcStrPtr;
    1717        3265 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1718        3265 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1719        3265 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1720        3265 :         return true;
    1721             :     }
    1722        1579 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1723         465 :         dstType.GetClass() == GEDTC_STRING)
    1724             :     {
    1725         465 :         const char *str = nullptr;
    1726         465 :         switch (srcType.GetNumericDataType())
    1727             :         {
    1728           0 :             case GDT_Unknown:
    1729           0 :                 break;
    1730           0 :             case GDT_Byte:
    1731           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1732           0 :                 break;
    1733           3 :             case GDT_Int8:
    1734           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1735           3 :                 break;
    1736          48 :             case GDT_UInt16:
    1737          48 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1738          48 :                 break;
    1739           0 :             case GDT_Int16:
    1740           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1741           0 :                 break;
    1742           8 :             case GDT_UInt32:
    1743           8 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1744           8 :                 break;
    1745          60 :             case GDT_Int32:
    1746          60 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1747          60 :                 break;
    1748           0 :             case GDT_UInt64:
    1749             :                 str =
    1750           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1751             :                                static_cast<GUIntBig>(
    1752             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1753           0 :                 break;
    1754          24 :             case GDT_Int64:
    1755          24 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1756             :                                  static_cast<GIntBig>(
    1757             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1758          24 :                 break;
    1759           0 :             case GDT_Float16:
    1760           0 :                 str = CPLSPrintf("%.5g",
    1761             :                                  double(*static_cast<const GFloat16 *>(pSrc)));
    1762           0 :                 break;
    1763          17 :             case GDT_Float32:
    1764          17 :                 str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
    1765          17 :                 break;
    1766         303 :             case GDT_Float64:
    1767         303 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1768         303 :                 break;
    1769           2 :             case GDT_CInt16:
    1770             :             {
    1771           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1772           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1773           2 :                 break;
    1774             :             }
    1775           0 :             case GDT_CInt32:
    1776             :             {
    1777           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1778           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1779           0 :                 break;
    1780             :             }
    1781           0 :             case GDT_CFloat16:
    1782             :             {
    1783           0 :                 const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
    1784           0 :                 str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
    1785           0 :                 break;
    1786             :             }
    1787           0 :             case GDT_CFloat32:
    1788             :             {
    1789           0 :                 const float *src = static_cast<const float *>(pSrc);
    1790           0 :                 str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
    1791           0 :                 break;
    1792             :             }
    1793           0 :             case GDT_CFloat64:
    1794             :             {
    1795           0 :                 const double *src = static_cast<const double *>(pSrc);
    1796           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1797           0 :                 break;
    1798             :             }
    1799           0 :             case GDT_TypeCount:
    1800           0 :                 CPLAssert(false);
    1801             :                 break;
    1802             :         }
    1803         465 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1804         465 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1805         465 :         return true;
    1806             :     }
    1807        1082 :     if (srcType.GetClass() == GEDTC_STRING &&
    1808         433 :         dstType.GetClass() == GEDTC_NUMERIC)
    1809             :     {
    1810             :         const char *srcStrPtr;
    1811         433 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1812         433 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1813             :         {
    1814           2 :             *(static_cast<int64_t *>(pDst)) =
    1815           2 :                 srcStrPtr == nullptr ? 0
    1816           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1817             :         }
    1818         431 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1819             :         {
    1820           2 :             *(static_cast<uint64_t *>(pDst)) =
    1821           2 :                 srcStrPtr == nullptr
    1822           2 :                     ? 0
    1823           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1824             :         }
    1825             :         else
    1826             :         {
    1827             :             // FIXME GDT_UInt64
    1828         429 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1829         429 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
    1830             :                             dstType.GetNumericDataType(), 0, 1);
    1831             :         }
    1832         433 :         return true;
    1833             :     }
    1834         432 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1835         216 :         dstType.GetClass() == GEDTC_COMPOUND)
    1836             :     {
    1837         216 :         const auto &srcComponents = srcType.GetComponents();
    1838         216 :         const auto &dstComponents = dstType.GetComponents();
    1839         216 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1840         216 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1841             : 
    1842             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1843         432 :             srcComponentMap;
    1844        1031 :         for (const auto &srcComp : srcComponents)
    1845             :         {
    1846         815 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1847             :         }
    1848         586 :         for (const auto &dstComp : dstComponents)
    1849             :         {
    1850         370 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1851         370 :             if (oIter == srcComponentMap.end())
    1852           0 :                 return false;
    1853         370 :             const auto &srcComp = *(oIter->second);
    1854        1110 :             if (!GDALExtendedDataType::CopyValue(
    1855         370 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1856         370 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1857             :             {
    1858           0 :                 return false;
    1859             :             }
    1860             :         }
    1861         216 :         return true;
    1862             :     }
    1863             : 
    1864           0 :     return false;
    1865             : }
    1866             : 
    1867             : /************************************************************************/
    1868             : /*                             CopyValues()                             */
    1869             : /************************************************************************/
    1870             : 
    1871             : /** Convert severals value from a source type to a destination type.
    1872             :  *
    1873             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1874             :  * that must be freed with CPLFree().
    1875             :  */
    1876         365 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1877             :                                       const GDALExtendedDataType &srcType,
    1878             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1879             :                                       const GDALExtendedDataType &dstType,
    1880             :                                       GPtrDiff_t nDstStrideInElts,
    1881             :                                       size_t nValues)
    1882             : {
    1883             :     const auto nSrcStrideInBytes =
    1884         365 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1885             :     const auto nDstStrideInBytes =
    1886         365 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1887         631 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1888         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1889         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1890         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1891         897 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1892         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1893             :     {
    1894         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1895             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1896             :                         dstType.GetNumericDataType(),
    1897             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1898             :     }
    1899             :     else
    1900             :     {
    1901          99 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1902          99 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1903         198 :         for (size_t i = 0; i < nValues; ++i)
    1904             :         {
    1905          99 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1906           0 :                 return false;
    1907          99 :             pabySrc += nSrcStrideInBytes;
    1908          99 :             pabyDst += nDstStrideInBytes;
    1909             :         }
    1910             :     }
    1911         365 :     return true;
    1912             : }
    1913             : 
    1914             : /************************************************************************/
    1915             : /*                       CheckReadWriteParams()                         */
    1916             : /************************************************************************/
    1917             : //! @cond Doxygen_Suppress
    1918        8216 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1919             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1920             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1921             :     const void *buffer, const void *buffer_alloc_start,
    1922             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1923             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1924             : {
    1925           0 :     const auto lamda_error = []()
    1926             :     {
    1927           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1928             :                  "Not all elements pointed by buffer will fit in "
    1929             :                  "[buffer_alloc_start, "
    1930             :                  "buffer_alloc_start + buffer_alloc_size]");
    1931           0 :     };
    1932             : 
    1933        8216 :     const auto &dims = GetDimensions();
    1934        8216 :     if (dims.empty())
    1935             :     {
    1936        3151 :         if (buffer_alloc_start)
    1937             :         {
    1938        2774 :             const size_t elementSize = bufferDataType.GetSize();
    1939        2774 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1940        2774 :             const GByte *paby_buffer_alloc_start =
    1941             :                 static_cast<const GByte *>(buffer_alloc_start);
    1942        2774 :             const GByte *paby_buffer_alloc_end =
    1943             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1944             : 
    1945        2774 :             if (paby_buffer < paby_buffer_alloc_start ||
    1946        2774 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1947             :             {
    1948           0 :                 lamda_error();
    1949           0 :                 return false;
    1950             :             }
    1951             :         }
    1952        3151 :         return true;
    1953             :     }
    1954             : 
    1955        5065 :     if (arrayStep == nullptr)
    1956             :     {
    1957        1364 :         tmp_arrayStep.resize(dims.size(), 1);
    1958        1364 :         arrayStep = tmp_arrayStep.data();
    1959             :     }
    1960       14207 :     for (size_t i = 0; i < dims.size(); i++)
    1961             :     {
    1962        9142 :         assert(count);
    1963        9142 :         if (count[i] == 0)
    1964             :         {
    1965           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1966             :                      static_cast<unsigned>(i));
    1967           0 :             return false;
    1968             :         }
    1969             :     }
    1970        5065 :     bool bufferStride_all_positive = true;
    1971        5065 :     if (bufferStride == nullptr)
    1972             :     {
    1973        1066 :         GPtrDiff_t stride = 1;
    1974        1066 :         assert(dims.empty() || count != nullptr);
    1975             :         // To compute strides we must proceed from the fastest varying dimension
    1976             :         // (the last one), and then reverse the result
    1977        2427 :         for (size_t i = dims.size(); i != 0;)
    1978             :         {
    1979        1361 :             --i;
    1980        1361 :             tmp_bufferStride.push_back(stride);
    1981        1361 :             GUInt64 newStride = 0;
    1982             :             bool bOK;
    1983             :             try
    1984             :             {
    1985        1361 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
    1986        2722 :                              CPLSM(static_cast<uint64_t>(count[i])))
    1987        1361 :                                 .v();
    1988        1361 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    1989        1361 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    1990             :             }
    1991           0 :             catch (...)
    1992             :             {
    1993           0 :                 bOK = false;
    1994             :             }
    1995        1361 :             if (!bOK)
    1996             :             {
    1997           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    1998           0 :                 return false;
    1999             :             }
    2000        1361 :             stride = static_cast<GPtrDiff_t>(newStride);
    2001             :         }
    2002        1066 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    2003        1066 :         bufferStride = tmp_bufferStride.data();
    2004             :     }
    2005             :     else
    2006             :     {
    2007       11778 :         for (size_t i = 0; i < dims.size(); i++)
    2008             :         {
    2009        7780 :             if (bufferStride[i] < 0)
    2010             :             {
    2011           1 :                 bufferStride_all_positive = false;
    2012           1 :                 break;
    2013             :             }
    2014             :         }
    2015             :     }
    2016       14178 :     for (size_t i = 0; i < dims.size(); i++)
    2017             :     {
    2018        9123 :         assert(arrayStartIdx);
    2019        9123 :         assert(count);
    2020        9123 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    2021             :         {
    2022           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2023             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    2024             :                      static_cast<unsigned>(i),
    2025           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    2026           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    2027           2 :             return false;
    2028             :         }
    2029             :         bool bOverflow;
    2030        9121 :         if (arrayStep[i] >= 0)
    2031             :         {
    2032             :             try
    2033             :             {
    2034        8526 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
    2035        8528 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2036       34107 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
    2037        8526 :                                 .v() >= dims[i]->GetSize();
    2038             :             }
    2039           1 :             catch (...)
    2040             :             {
    2041           1 :                 bOverflow = true;
    2042             :             }
    2043        8527 :             if (bOverflow)
    2044             :             {
    2045           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2046             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    2047             :                          ">= " CPL_FRMT_GUIB,
    2048             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    2049             :                          static_cast<unsigned>(i),
    2050           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    2051           5 :                 return false;
    2052             :             }
    2053             :         }
    2054             :         else
    2055             :         {
    2056             :             try
    2057             :             {
    2058         594 :                 bOverflow =
    2059         594 :                     arrayStartIdx[i] <
    2060         594 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2061        1188 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    2062             :                                ? (static_cast<uint64_t>(1) << 63)
    2063        1188 :                                : static_cast<uint64_t>(-arrayStep[i])))
    2064         594 :                         .v();
    2065             :             }
    2066           0 :             catch (...)
    2067             :             {
    2068           0 :                 bOverflow = true;
    2069             :             }
    2070         594 :             if (bOverflow)
    2071             :             {
    2072           3 :                 CPLError(
    2073             :                     CE_Failure, CPLE_AppDefined,
    2074             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    2075             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    2076             :                     static_cast<unsigned>(i));
    2077           3 :                 return false;
    2078             :             }
    2079             :         }
    2080             :     }
    2081             : 
    2082        5055 :     if (buffer_alloc_start)
    2083             :     {
    2084        2593 :         const size_t elementSize = bufferDataType.GetSize();
    2085        2593 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    2086        2593 :         const GByte *paby_buffer_alloc_start =
    2087             :             static_cast<const GByte *>(buffer_alloc_start);
    2088        2593 :         const GByte *paby_buffer_alloc_end =
    2089             :             paby_buffer_alloc_start + buffer_alloc_size;
    2090        2593 :         if (bufferStride_all_positive)
    2091             :         {
    2092        2593 :             if (paby_buffer < paby_buffer_alloc_start)
    2093             :             {
    2094           0 :                 lamda_error();
    2095           0 :                 return false;
    2096             :             }
    2097        2593 :             GUInt64 nOffset = elementSize;
    2098        7436 :             for (size_t i = 0; i < dims.size(); i++)
    2099             :             {
    2100             :                 try
    2101             :                 {
    2102        4843 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
    2103        4843 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
    2104        9686 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2105       19372 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
    2106        4843 :                                   .v();
    2107             :                 }
    2108           0 :                 catch (...)
    2109             :                 {
    2110           0 :                     lamda_error();
    2111           0 :                     return false;
    2112             :                 }
    2113             :             }
    2114             : #if SIZEOF_VOIDP == 4
    2115             :             if (static_cast<size_t>(nOffset) != nOffset)
    2116             :             {
    2117             :                 lamda_error();
    2118             :                 return false;
    2119             :             }
    2120             : #endif
    2121        2593 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2122             :             {
    2123           0 :                 lamda_error();
    2124           0 :                 return false;
    2125             :             }
    2126             :         }
    2127           0 :         else if (dims.size() < 31)
    2128             :         {
    2129             :             // Check all corners of the hypercube
    2130           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2131           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2132             :             {
    2133           0 :                 const GByte *paby = paby_buffer;
    2134           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2135             :                      i++)
    2136             :                 {
    2137           0 :                     if (iCornerCode & (1U << i))
    2138             :                     {
    2139             :                         // We should check for integer overflows
    2140           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2141             :                     }
    2142             :                 }
    2143           0 :                 if (paby < paby_buffer_alloc_start ||
    2144           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2145             :                 {
    2146           0 :                     lamda_error();
    2147           0 :                     return false;
    2148             :                 }
    2149             :             }
    2150             :         }
    2151             :     }
    2152             : 
    2153        5055 :     return true;
    2154             : }
    2155             : 
    2156             : //! @endcond
    2157             : 
    2158             : /************************************************************************/
    2159             : /*                               Read()                                 */
    2160             : /************************************************************************/
    2161             : 
    2162             : /** Read part or totality of a multidimensional array or attribute.
    2163             :  *
    2164             :  * This will extract the content of a hyper-rectangle from the array into
    2165             :  * a user supplied buffer.
    2166             :  *
    2167             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2168             :  * will be char* pointers and the strings should be freed with CPLFree().
    2169             :  *
    2170             :  * This is the same as the C function GDALMDArrayRead().
    2171             :  *
    2172             :  * @param arrayStartIdx Values representing the starting index to read
    2173             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2174             :  *                      Array of GetDimensionCount() values. Must not be
    2175             :  *                      nullptr, unless for a zero-dimensional array.
    2176             :  *
    2177             :  * @param count         Values representing the number of values to extract in
    2178             :  *                      each dimension.
    2179             :  *                      Array of GetDimensionCount() values. Must not be
    2180             :  *                      nullptr, unless for a zero-dimensional array.
    2181             :  *
    2182             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2183             :  *                      The spacing is in number of array elements, not bytes.
    2184             :  *                      If provided, must contain GetDimensionCount() values.
    2185             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2186             :  * default to indicate consecutive elements.
    2187             :  *
    2188             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2189             :  *                      The spacing is in number of array elements, not bytes.
    2190             :  *                      If provided, must contain GetDimensionCount() values.
    2191             :  *                      Negative values are possible (for example to reorder
    2192             :  *                      from bottom-to-top to top-to-bottom).
    2193             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2194             :  *                      written in a compact way, with elements of the last /
    2195             :  *                      fastest varying dimension being consecutive.
    2196             :  *
    2197             :  * @param bufferDataType Data type of values in pDstBuffer.
    2198             :  *
    2199             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2200             :  *                      enough to store the number of values indicated by
    2201             :  * count[] and with the spacing of bufferStride[].
    2202             :  *
    2203             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2204             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2205             :  * should be the pointer returned by the malloc() or equivalent call used to
    2206             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2207             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2208             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2209             :  * validation is needed, nullptr can be passed.
    2210             :  *
    2211             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2212             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2213             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2214             :  *                             set to the appropriate value.
    2215             :  *                             If no validation is needed, 0 can be passed.
    2216             :  *
    2217             :  * @return true in case of success.
    2218             :  */
    2219        2469 : bool GDALAbstractMDArray::Read(
    2220             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2221             :     const GInt64 *arrayStep,         // step in elements
    2222             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2223             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2224             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2225             : {
    2226        2469 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2227             :     {
    2228           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2229             :                  "Array data type is not convertible to buffer data type");
    2230           0 :         return false;
    2231             :     }
    2232             : 
    2233        4938 :     std::vector<GInt64> tmp_arrayStep;
    2234        4938 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2235        2469 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2236             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2237             :                               nDstBufferAllocSize, tmp_arrayStep,
    2238             :                               tmp_bufferStride))
    2239             :     {
    2240           0 :         return false;
    2241             :     }
    2242             : 
    2243        2469 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2244        2469 :                  pDstBuffer);
    2245             : }
    2246             : 
    2247             : /************************************************************************/
    2248             : /*                                IWrite()                              */
    2249             : /************************************************************************/
    2250             : 
    2251             : //! @cond Doxygen_Suppress
    2252           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2253             :                                  const GInt64 *, const GPtrDiff_t *,
    2254             :                                  const GDALExtendedDataType &, const void *)
    2255             : {
    2256           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2257           1 :     return false;
    2258             : }
    2259             : 
    2260             : //! @endcond
    2261             : 
    2262             : /************************************************************************/
    2263             : /*                               Write()                                 */
    2264             : /************************************************************************/
    2265             : 
    2266             : /** Write part or totality of a multidimensional array or attribute.
    2267             :  *
    2268             :  * This will set the content of a hyper-rectangle into the array from
    2269             :  * a user supplied buffer.
    2270             :  *
    2271             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2272             :  * will be char* pointers.
    2273             :  *
    2274             :  * This is the same as the C function GDALMDArrayWrite().
    2275             :  *
    2276             :  * @param arrayStartIdx Values representing the starting index to write
    2277             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2278             :  *                      Array of GetDimensionCount() values. Must not be
    2279             :  *                      nullptr, unless for a zero-dimensional array.
    2280             :  *
    2281             :  * @param count         Values representing the number of values to write in
    2282             :  *                      each dimension.
    2283             :  *                      Array of GetDimensionCount() values. Must not be
    2284             :  *                      nullptr, unless for a zero-dimensional array.
    2285             :  *
    2286             :  * @param arrayStep     Spacing between values to write in each dimension.
    2287             :  *                      The spacing is in number of array elements, not bytes.
    2288             :  *                      If provided, must contain GetDimensionCount() values.
    2289             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2290             :  * default to indicate consecutive elements.
    2291             :  *
    2292             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2293             :  *                      The spacing is in number of array elements, not bytes.
    2294             :  *                      If provided, must contain GetDimensionCount() values.
    2295             :  *                      Negative values are possible (for example to reorder
    2296             :  *                      from bottom-to-top to top-to-bottom).
    2297             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2298             :  *                      written in a compact way, with elements of the last /
    2299             :  *                      fastest varying dimension being consecutive.
    2300             :  *
    2301             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2302             :  *
    2303             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2304             :  *                      enough to store the number of values indicated by
    2305             :  * count[] and with the spacing of bufferStride[].
    2306             :  *
    2307             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2308             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2309             :  * should be the pointer returned by the malloc() or equivalent call used to
    2310             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2311             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2312             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2313             :  * validation is needed, nullptr can be passed.
    2314             :  *
    2315             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2316             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2317             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2318             :  *                             set to the appropriate value.
    2319             :  *                             If no validation is needed, 0 can be passed.
    2320             :  *
    2321             :  * @return true in case of success.
    2322             :  */
    2323        1811 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2324             :                                 const size_t *count, const GInt64 *arrayStep,
    2325             :                                 const GPtrDiff_t *bufferStride,
    2326             :                                 const GDALExtendedDataType &bufferDataType,
    2327             :                                 const void *pSrcBuffer,
    2328             :                                 const void *pSrcBufferAllocStart,
    2329             :                                 size_t nSrcBufferAllocSize)
    2330             : {
    2331        1811 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2332             :     {
    2333           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2334             :                  "Buffer data type is not convertible to array data type");
    2335           0 :         return false;
    2336             :     }
    2337             : 
    2338        3622 :     std::vector<GInt64> tmp_arrayStep;
    2339        3622 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2340        1811 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2341             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2342             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2343             :                               tmp_bufferStride))
    2344             :     {
    2345           0 :         return false;
    2346             :     }
    2347             : 
    2348        1811 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2349        1811 :                   pSrcBuffer);
    2350             : }
    2351             : 
    2352             : /************************************************************************/
    2353             : /*                          GetTotalElementsCount()                     */
    2354             : /************************************************************************/
    2355             : 
    2356             : /** Return the total number of values in the array.
    2357             :  *
    2358             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2359             :  * and GDALAttributeGetTotalElementsCount().
    2360             :  *
    2361             :  */
    2362        1124 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2363             : {
    2364        1124 :     const auto &dims = GetDimensions();
    2365        1124 :     if (dims.empty())
    2366         558 :         return 1;
    2367         566 :     GUInt64 nElts = 1;
    2368        1254 :     for (const auto &dim : dims)
    2369             :     {
    2370             :         try
    2371             :         {
    2372         688 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
    2373        2064 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
    2374         688 :                         .v();
    2375             :         }
    2376           0 :         catch (...)
    2377             :         {
    2378           0 :             return 0;
    2379             :         }
    2380             :     }
    2381         566 :     return nElts;
    2382             : }
    2383             : 
    2384             : /************************************************************************/
    2385             : /*                           GetBlockSize()                             */
    2386             : /************************************************************************/
    2387             : 
    2388             : /** Return the "natural" block size of the array along all dimensions.
    2389             :  *
    2390             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2391             :  * aligned on those tile/block boundaries will be more efficient.
    2392             :  *
    2393             :  * The returned number of elements in the vector is the same as
    2394             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2395             :  * the natural block size along the considered dimension.
    2396             :  * "Flat" arrays will typically return a vector of values set to 0.
    2397             :  *
    2398             :  * The default implementation will return a vector of values set to 0.
    2399             :  *
    2400             :  * This method is used by GetProcessingChunkSize().
    2401             :  *
    2402             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
    2403             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2404             :  * allocation capabilities.
    2405             :  *
    2406             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2407             :  *
    2408             :  * @return the block size, in number of elements along each dimension.
    2409             :  */
    2410         279 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2411             : {
    2412         279 :     return std::vector<GUInt64>(GetDimensionCount());
    2413             : }
    2414             : 
    2415             : /************************************************************************/
    2416             : /*                       GetProcessingChunkSize()                       */
    2417             : /************************************************************************/
    2418             : 
    2419             : /** \brief Return an optimal chunk size for read/write operations, given the
    2420             :  * natural block size and memory constraints specified.
    2421             :  *
    2422             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2423             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2424             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2425             :  * returned by this method).
    2426             :  *
    2427             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2428             :  *
    2429             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2430             :  * chunk.
    2431             :  *
    2432             :  * @return the chunk size, in number of elements along each dimension.
    2433             :  */
    2434             : std::vector<size_t>
    2435          70 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2436             : {
    2437          70 :     const auto &dims = GetDimensions();
    2438          70 :     const auto &nDTSize = GetDataType().GetSize();
    2439          70 :     std::vector<size_t> anChunkSize;
    2440         140 :     auto blockSize = GetBlockSize();
    2441          70 :     CPLAssert(blockSize.size() == dims.size());
    2442          70 :     size_t nChunkSize = nDTSize;
    2443          70 :     bool bOverflow = false;
    2444          70 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2445             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2446             :     // [1, min(sizet_max, dim_size[i])]
    2447             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2448         199 :     for (size_t i = 0; i < dims.size(); i++)
    2449             :     {
    2450             :         const auto sizeDimI =
    2451         258 :             std::max(static_cast<size_t>(1),
    2452         258 :                      static_cast<size_t>(
    2453         258 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2454         129 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2455         129 :         anChunkSize.push_back(sizeDimI);
    2456         129 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2457             :         {
    2458           4 :             bOverflow = true;
    2459             :         }
    2460             :         else
    2461             :         {
    2462         125 :             nChunkSize *= sizeDimI;
    2463             :         }
    2464             :     }
    2465          70 :     if (nChunkSize == 0)
    2466           0 :         return anChunkSize;
    2467             : 
    2468             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2469             :     // set lowest anChunkSize[i] to 1.
    2470          70 :     if (bOverflow)
    2471             :     {
    2472           2 :         nChunkSize = nDTSize;
    2473           2 :         bOverflow = false;
    2474           8 :         for (size_t i = dims.size(); i > 0;)
    2475             :         {
    2476           6 :             --i;
    2477           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2478             :             {
    2479           4 :                 bOverflow = true;
    2480           4 :                 anChunkSize[i] = 1;
    2481             :             }
    2482             :             else
    2483             :             {
    2484           2 :                 nChunkSize *= anChunkSize[i];
    2485             :             }
    2486             :         }
    2487             :     }
    2488             : 
    2489          70 :     nChunkSize = nDTSize;
    2490         140 :     std::vector<size_t> anAccBlockSizeFromStart;
    2491         199 :     for (size_t i = 0; i < dims.size(); i++)
    2492             :     {
    2493         129 :         nChunkSize *= anChunkSize[i];
    2494         129 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2495             :     }
    2496          70 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2497             :     {
    2498          66 :         size_t nVoxelsFromEnd = 1;
    2499         187 :         for (size_t i = dims.size(); i > 0;)
    2500             :         {
    2501         121 :             --i;
    2502             :             const auto nCurBlockSize =
    2503         121 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2504         121 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2505         121 :             if (nMul >= 2)
    2506             :             {
    2507         113 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2508             :                 const auto nBlocksThisDim =
    2509         113 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2510         113 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2511         113 :                     anChunkSize[i] *
    2512         226 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2513         113 :                     nSizeThisDim));
    2514             :             }
    2515         121 :             nVoxelsFromEnd *= anChunkSize[i];
    2516             :         }
    2517             :     }
    2518          70 :     return anChunkSize;
    2519             : }
    2520             : 
    2521             : /************************************************************************/
    2522             : /*                         BaseRename()                                 */
    2523             : /************************************************************************/
    2524             : 
    2525             : //! @cond Doxygen_Suppress
    2526          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2527             : {
    2528          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2529          18 :     m_osFullName += osNewName;
    2530          18 :     m_osName = osNewName;
    2531             : 
    2532          18 :     NotifyChildrenOfRenaming();
    2533          18 : }
    2534             : 
    2535             : //! @endcond
    2536             : 
    2537             : //! @cond Doxygen_Suppress
    2538             : /************************************************************************/
    2539             : /*                          ParentRenamed()                             */
    2540             : /************************************************************************/
    2541             : 
    2542          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2543             : {
    2544          50 :     m_osFullName = osNewParentFullName;
    2545          50 :     m_osFullName += "/";
    2546          50 :     m_osFullName += m_osName;
    2547             : 
    2548          50 :     NotifyChildrenOfRenaming();
    2549          50 : }
    2550             : 
    2551             : //! @endcond
    2552             : 
    2553             : /************************************************************************/
    2554             : /*                             Deleted()                                */
    2555             : /************************************************************************/
    2556             : 
    2557             : //! @cond Doxygen_Suppress
    2558          52 : void GDALAbstractMDArray::Deleted()
    2559             : {
    2560          52 :     m_bValid = false;
    2561             : 
    2562          52 :     NotifyChildrenOfDeletion();
    2563          52 : }
    2564             : 
    2565             : //! @endcond
    2566             : 
    2567             : /************************************************************************/
    2568             : /*                        ParentDeleted()                               */
    2569             : /************************************************************************/
    2570             : 
    2571             : //! @cond Doxygen_Suppress
    2572          28 : void GDALAbstractMDArray::ParentDeleted()
    2573             : {
    2574          28 :     Deleted();
    2575          28 : }
    2576             : 
    2577             : //! @endcond
    2578             : 
    2579             : /************************************************************************/
    2580             : /*                     CheckValidAndErrorOutIfNot()                     */
    2581             : /************************************************************************/
    2582             : 
    2583             : //! @cond Doxygen_Suppress
    2584        5860 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2585             : {
    2586        5860 :     if (!m_bValid)
    2587             :     {
    2588          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2589             :                  "This object has been deleted. No action on it is possible");
    2590             :     }
    2591        5860 :     return m_bValid;
    2592             : }
    2593             : 
    2594             : //! @endcond
    2595             : 
    2596             : /************************************************************************/
    2597             : /*                             SetUnit()                                */
    2598             : /************************************************************************/
    2599             : 
    2600             : /** Set the variable unit.
    2601             :  *
    2602             :  * Values should conform as much as possible with those allowed by
    2603             :  * the NetCDF CF conventions:
    2604             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2605             :  * but others might be returned.
    2606             :  *
    2607             :  * Few examples are "meter", "degrees", "second", ...
    2608             :  * Empty value means unknown.
    2609             :  *
    2610             :  * This is the same as the C function GDALMDArraySetUnit()
    2611             :  *
    2612             :  * @note Driver implementation: optionally implemented.
    2613             :  *
    2614             :  * @param osUnit unit name.
    2615             :  * @return true in case of success.
    2616             :  */
    2617           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2618             : {
    2619           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2620           0 :     return false;
    2621             : }
    2622             : 
    2623             : /************************************************************************/
    2624             : /*                             GetUnit()                                */
    2625             : /************************************************************************/
    2626             : 
    2627             : /** Return the array unit.
    2628             :  *
    2629             :  * Values should conform as much as possible with those allowed by
    2630             :  * the NetCDF CF conventions:
    2631             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2632             :  * but others might be returned.
    2633             :  *
    2634             :  * Few examples are "meter", "degrees", "second", ...
    2635             :  * Empty value means unknown.
    2636             :  *
    2637             :  * This is the same as the C function GDALMDArrayGetUnit()
    2638             :  */
    2639           5 : const std::string &GDALMDArray::GetUnit() const
    2640             : {
    2641           5 :     static const std::string emptyString;
    2642           5 :     return emptyString;
    2643             : }
    2644             : 
    2645             : /************************************************************************/
    2646             : /*                          SetSpatialRef()                             */
    2647             : /************************************************************************/
    2648             : 
    2649             : /** Assign a spatial reference system object to the array.
    2650             :  *
    2651             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2652             :  */
    2653           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2654             : {
    2655           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2656           0 :     return false;
    2657             : }
    2658             : 
    2659             : /************************************************************************/
    2660             : /*                          GetSpatialRef()                             */
    2661             : /************************************************************************/
    2662             : 
    2663             : /** Return the spatial reference system object associated with the array.
    2664             :  *
    2665             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2666             :  */
    2667           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2668             : {
    2669           4 :     return nullptr;
    2670             : }
    2671             : 
    2672             : /************************************************************************/
    2673             : /*                        GetRawNoDataValue()                           */
    2674             : /************************************************************************/
    2675             : 
    2676             : /** Return the nodata value as a "raw" value.
    2677             :  *
    2678             :  * The value returned might be nullptr in case of no nodata value. When
    2679             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2680             :  * bytes is GetDataType().GetSize().
    2681             :  *
    2682             :  * The returned value should not be modified or freed. It is valid until
    2683             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2684             :  * SetRawNoDataValue(), or any similar methods.
    2685             :  *
    2686             :  * @note Driver implementation: this method shall be implemented if nodata
    2687             :  * is supported.
    2688             :  *
    2689             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2690             :  *
    2691             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2692             :  */
    2693           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2694             : {
    2695           5 :     return nullptr;
    2696             : }
    2697             : 
    2698             : /************************************************************************/
    2699             : /*                        GetNoDataValueAsDouble()                      */
    2700             : /************************************************************************/
    2701             : 
    2702             : /** Return the nodata value as a double.
    2703             :  *
    2704             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2705             :  *
    2706             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2707             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2708             :  *
    2709             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2710             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2711             :  * set to false then).
    2712             :  */
    2713       22478 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2714             : {
    2715       22478 :     const void *pNoData = GetRawNoDataValue();
    2716       22478 :     double dfNoData = 0.0;
    2717       22478 :     const auto &eDT = GetDataType();
    2718       22478 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2719       22478 :     if (ok)
    2720             :     {
    2721       22188 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2722             :                         GDT_Float64, 0, 1);
    2723             :     }
    2724       22478 :     if (pbHasNoData)
    2725         439 :         *pbHasNoData = ok;
    2726       22478 :     return dfNoData;
    2727             : }
    2728             : 
    2729             : /************************************************************************/
    2730             : /*                        GetNoDataValueAsInt64()                       */
    2731             : /************************************************************************/
    2732             : 
    2733             : /** Return the nodata value as a Int64.
    2734             :  *
    2735             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2736             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2737             :  *
    2738             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2739             :  *
    2740             :  * @return the nodata value as a Int64
    2741             :  *
    2742             :  * @since GDAL 3.5
    2743             :  */
    2744          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2745             : {
    2746          12 :     const void *pNoData = GetRawNoDataValue();
    2747          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2748          12 :     const auto &eDT = GetDataType();
    2749          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2750          12 :     if (ok)
    2751             :     {
    2752           8 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2753             :                         GDT_Int64, 0, 1);
    2754             :     }
    2755          12 :     if (pbHasNoData)
    2756          12 :         *pbHasNoData = ok;
    2757          12 :     return nNoData;
    2758             : }
    2759             : 
    2760             : /************************************************************************/
    2761             : /*                       GetNoDataValueAsUInt64()                       */
    2762             : /************************************************************************/
    2763             : 
    2764             : /** Return the nodata value as a UInt64.
    2765             :  *
    2766             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2767             : 
    2768             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2769             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2770             :  *
    2771             :  * @return the nodata value as a UInt64
    2772             :  *
    2773             :  * @since GDAL 3.5
    2774             :  */
    2775           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2776             : {
    2777           8 :     const void *pNoData = GetRawNoDataValue();
    2778           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2779           8 :     const auto &eDT = GetDataType();
    2780           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2781           8 :     if (ok)
    2782             :     {
    2783           6 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2784             :                         GDT_UInt64, 0, 1);
    2785             :     }
    2786           8 :     if (pbHasNoData)
    2787           8 :         *pbHasNoData = ok;
    2788           8 :     return nNoData;
    2789             : }
    2790             : 
    2791             : /************************************************************************/
    2792             : /*                        SetRawNoDataValue()                           */
    2793             : /************************************************************************/
    2794             : 
    2795             : /** Set the nodata value as a "raw" value.
    2796             :  *
    2797             :  * The value passed might be nullptr in case of no nodata value. When
    2798             :  * a nodata value is registered, a non-nullptr whose size in
    2799             :  * bytes is GetDataType().GetSize() must be passed.
    2800             :  *
    2801             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2802             :  *
    2803             :  * @note Driver implementation: this method shall be implemented if setting
    2804             :  nodata
    2805             :  * is supported.
    2806             : 
    2807             :  * @return true in case of success.
    2808             :  */
    2809           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2810             : {
    2811           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2812             :              "SetRawNoDataValue() not implemented");
    2813           0 :     return false;
    2814             : }
    2815             : 
    2816             : /************************************************************************/
    2817             : /*                           SetNoDataValue()                           */
    2818             : /************************************************************************/
    2819             : 
    2820             : /** Set the nodata value as a double.
    2821             :  *
    2822             :  * If the natural data type of the attribute/array is not double, type
    2823             :  * conversion will occur to the type returned by GetDataType().
    2824             :  *
    2825             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2826             :  *
    2827             :  * @return true in case of success.
    2828             :  */
    2829          57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2830             : {
    2831          57 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2832          57 :     bool bRet = false;
    2833          57 :     if (GDALExtendedDataType::CopyValue(
    2834         114 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2835          57 :             GetDataType()))
    2836             :     {
    2837          57 :         bRet = SetRawNoDataValue(pRawNoData);
    2838             :     }
    2839          57 :     CPLFree(pRawNoData);
    2840          57 :     return bRet;
    2841             : }
    2842             : 
    2843             : /************************************************************************/
    2844             : /*                           SetNoDataValue()                           */
    2845             : /************************************************************************/
    2846             : 
    2847             : /** Set the nodata value as a Int64.
    2848             :  *
    2849             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2850             :  * will occur to the type returned by GetDataType().
    2851             :  *
    2852             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2853             :  *
    2854             :  * @return true in case of success.
    2855             :  *
    2856             :  * @since GDAL 3.5
    2857             :  */
    2858           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2859             : {
    2860           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2861           3 :     bool bRet = false;
    2862           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2863           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2864           3 :                                         pRawNoData, GetDataType()))
    2865             :     {
    2866           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2867             :     }
    2868           3 :     CPLFree(pRawNoData);
    2869           3 :     return bRet;
    2870             : }
    2871             : 
    2872             : /************************************************************************/
    2873             : /*                           SetNoDataValue()                           */
    2874             : /************************************************************************/
    2875             : 
    2876             : /** Set the nodata value as a Int64.
    2877             :  *
    2878             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2879             :  * will occur to the type returned by GetDataType().
    2880             :  *
    2881             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2882             :  *
    2883             :  * @return true in case of success.
    2884             :  *
    2885             :  * @since GDAL 3.5
    2886             :  */
    2887           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2888             : {
    2889           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2890           1 :     bool bRet = false;
    2891           1 :     if (GDALExtendedDataType::CopyValue(
    2892           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2893           1 :             GetDataType()))
    2894             :     {
    2895           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2896             :     }
    2897           1 :     CPLFree(pRawNoData);
    2898           1 :     return bRet;
    2899             : }
    2900             : 
    2901             : /************************************************************************/
    2902             : /*                            Resize()                                  */
    2903             : /************************************************************************/
    2904             : 
    2905             : /** Resize an array to new dimensions.
    2906             :  *
    2907             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2908             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2909             :  *
    2910             :  * Resizing a dimension used in other arrays will cause those other arrays
    2911             :  * to be resized.
    2912             :  *
    2913             :  * This is the same as the C function GDALMDArrayResize().
    2914             :  *
    2915             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2916             :  *                      new size of each indexing dimension.
    2917             :  * @param papszOptions Options. (Driver specific)
    2918             :  * @return true in case of success.
    2919             :  * @since GDAL 3.7
    2920             :  */
    2921           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2922             :                          CPL_UNUSED CSLConstList papszOptions)
    2923             : {
    2924           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2925             :              "Resize() is not supported for this array");
    2926           0 :     return false;
    2927             : }
    2928             : 
    2929             : /************************************************************************/
    2930             : /*                               SetScale()                             */
    2931             : /************************************************************************/
    2932             : 
    2933             : /** Set the scale value to apply to raw values.
    2934             :  *
    2935             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2936             :  *
    2937             :  * This is the same as the C function GDALMDArraySetScale() /
    2938             :  * GDALMDArraySetScaleEx().
    2939             :  *
    2940             :  * @note Driver implementation: this method shall be implemented if setting
    2941             :  * scale is supported.
    2942             :  *
    2943             :  * @param dfScale scale
    2944             :  * @param eStorageType Data type to which create the potential attribute that
    2945             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2946             :  * implementation will decide automatically the data type. Note that changing
    2947             :  * the data type after initial setting might not be supported.
    2948             :  * @return true in case of success.
    2949             :  */
    2950           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2951             :                            CPL_UNUSED GDALDataType eStorageType)
    2952             : {
    2953           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2954           0 :     return false;
    2955             : }
    2956             : 
    2957             : /************************************************************************/
    2958             : /*                               SetOffset)                             */
    2959             : /************************************************************************/
    2960             : 
    2961             : /** Set the offset value to apply to raw values.
    2962             :  *
    2963             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2964             :  *
    2965             :  * This is the same as the C function GDALMDArraySetOffset() /
    2966             :  * GDALMDArraySetOffsetEx().
    2967             :  *
    2968             :  * @note Driver implementation: this method shall be implemented if setting
    2969             :  * offset is supported.
    2970             :  *
    2971             :  * @param dfOffset Offset
    2972             :  * @param eStorageType Data type to which create the potential attribute that
    2973             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2974             :  * implementation will decide automatically the data type. Note that changing
    2975             :  * the data type after initial setting might not be supported.
    2976             :  * @return true in case of success.
    2977             :  */
    2978           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    2979             :                             CPL_UNUSED GDALDataType eStorageType)
    2980             : {
    2981           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    2982           0 :     return false;
    2983             : }
    2984             : 
    2985             : /************************************************************************/
    2986             : /*                               GetScale()                             */
    2987             : /************************************************************************/
    2988             : 
    2989             : /** Get the scale value to apply to raw values.
    2990             :  *
    2991             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2992             :  *
    2993             :  * This is the same as the C function GDALMDArrayGetScale().
    2994             :  *
    2995             :  * @note Driver implementation: this method shall be implemented if gettings
    2996             :  * scale is supported.
    2997             :  *
    2998             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    2999             :  * a scale value exists. Might be nullptr.
    3000             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3001             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    3002             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3003             :  *
    3004             :  * @return the scale value. A 1.0 value might also indicate the
    3005             :  * absence of a scale value.
    3006             :  */
    3007          20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    3008             :                              CPL_UNUSED GDALDataType *peStorageType) const
    3009             : {
    3010          20 :     if (pbHasScale)
    3011          20 :         *pbHasScale = false;
    3012          20 :     return 1.0;
    3013             : }
    3014             : 
    3015             : /************************************************************************/
    3016             : /*                               GetOffset()                            */
    3017             : /************************************************************************/
    3018             : 
    3019             : /** Get the offset value to apply to raw values.
    3020             :  *
    3021             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    3022             :  *
    3023             :  * This is the same as the C function GDALMDArrayGetOffset().
    3024             :  *
    3025             :  * @note Driver implementation: this method shall be implemented if gettings
    3026             :  * offset is supported.
    3027             :  *
    3028             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    3029             :  * a offset value exists. Might be nullptr.
    3030             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3031             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    3032             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3033             :  *
    3034             :  * @return the offset value. A 0.0 value might also indicate the
    3035             :  * absence of a offset value.
    3036             :  */
    3037          20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    3038             :                               CPL_UNUSED GDALDataType *peStorageType) const
    3039             : {
    3040          20 :     if (pbHasOffset)
    3041          20 :         *pbHasOffset = false;
    3042          20 :     return 0.0;
    3043             : }
    3044             : 
    3045             : /************************************************************************/
    3046             : /*                         ProcessPerChunk()                            */
    3047             : /************************************************************************/
    3048             : 
    3049             : namespace
    3050             : {
    3051             : enum class Caller
    3052             : {
    3053             :     CALLER_END_OF_LOOP,
    3054             :     CALLER_IN_LOOP,
    3055             : };
    3056             : }
    3057             : 
    3058             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    3059             :  *
    3060             :  * This method is to be used when doing operations on an array, or a subset of
    3061             :  * it, in a chunk by chunk way.
    3062             :  *
    3063             :  * @param arrayStartIdx Values representing the starting index to use
    3064             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    3065             :  *                      Array of GetDimensionCount() values. Must not be
    3066             :  *                      nullptr, unless for a zero-dimensional array.
    3067             :  *
    3068             :  * @param count         Values representing the number of values to use in
    3069             :  *                      each dimension.
    3070             :  *                      Array of GetDimensionCount() values. Must not be
    3071             :  *                      nullptr, unless for a zero-dimensional array.
    3072             :  *
    3073             :  * @param chunkSize     Values representing the chunk size in each dimension.
    3074             :  *                      Might typically the output of GetProcessingChunkSize().
    3075             :  *                      Array of GetDimensionCount() values. Must not be
    3076             :  *                      nullptr, unless for a zero-dimensional array.
    3077             :  *
    3078             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    3079             :  *                      Must NOT be nullptr.
    3080             :  *
    3081             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    3082             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    3083             :  *
    3084             :  * @return true in case of success.
    3085             :  */
    3086          68 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    3087             :                                           const GUInt64 *count,
    3088             :                                           const size_t *chunkSize,
    3089             :                                           FuncProcessPerChunkType pfnFunc,
    3090             :                                           void *pUserData)
    3091             : {
    3092          68 :     const auto &dims = GetDimensions();
    3093          68 :     if (dims.empty())
    3094             :     {
    3095           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3096             :     }
    3097             : 
    3098             :     // Sanity check
    3099          66 :     size_t nTotalChunkSize = 1;
    3100         172 :     for (size_t i = 0; i < dims.size(); i++)
    3101             :     {
    3102         113 :         const auto nSizeThisDim(dims[i]->GetSize());
    3103         113 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3104         111 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3105             :         {
    3106           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3107             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3108             :                      "regarding array size");
    3109           4 :             return false;
    3110             :         }
    3111         216 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3112         107 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3113             :         {
    3114           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3115             :                      "Inconsistent chunkSize[] values");
    3116           3 :             return false;
    3117             :         }
    3118         106 :         nTotalChunkSize *= chunkSize[i];
    3119             :     }
    3120             : 
    3121          59 :     size_t dimIdx = 0;
    3122         118 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3123         118 :     std::vector<size_t> chunkCount(dims.size());
    3124             : 
    3125             :     struct Stack
    3126             :     {
    3127             :         GUInt64 nBlockCounter = 0;
    3128             :         GUInt64 nBlocksMinusOne = 0;
    3129             :         size_t first_count = 0;  // only used if nBlocks > 1
    3130             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3131             :     };
    3132             : 
    3133         118 :     std::vector<Stack> stack(dims.size());
    3134          59 :     GUInt64 iCurChunk = 0;
    3135          59 :     GUInt64 nChunkCount = 1;
    3136         164 :     for (size_t i = 0; i < dims.size(); i++)
    3137             :     {
    3138         105 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3139         105 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3140         105 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3141         105 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3142         105 :         if (stack[i].nBlocksMinusOne == 0)
    3143             :         {
    3144         100 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3145         100 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3146             :         }
    3147             :         else
    3148             :         {
    3149           5 :             stack[i].first_count = static_cast<size_t>(
    3150           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3151             :         }
    3152             :     }
    3153             : 
    3154          59 : lbl_next_depth:
    3155         274 :     if (dimIdx == dims.size())
    3156             :     {
    3157          92 :         ++iCurChunk;
    3158          92 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3159             :                      iCurChunk, nChunkCount, pUserData))
    3160             :         {
    3161           0 :             return false;
    3162             :         }
    3163             :     }
    3164             :     else
    3165             :     {
    3166         182 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3167             :         {
    3168          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3169          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3170          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3171          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3172             :             while (true)
    3173             :             {
    3174          33 :                 dimIdx++;
    3175          33 :                 goto lbl_next_depth;
    3176          33 :             lbl_return_to_caller_in_loop:
    3177          33 :                 --stack[dimIdx].nBlockCounter;
    3178          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3179          11 :                     break;
    3180          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3181          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3182             :             }
    3183             : 
    3184          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3185          22 :             chunkCount[dimIdx] =
    3186          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3187          11 :                                     chunkArrayStartIdx[dimIdx]);
    3188          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3189             :         }
    3190         182 :         dimIdx++;
    3191         182 :         goto lbl_next_depth;
    3192         182 :     lbl_return_to_caller_end_of_loop:
    3193         182 :         if (dimIdx == 0)
    3194          59 :             goto end;
    3195             :     }
    3196             : 
    3197         215 :     assert(dimIdx > 0);
    3198         215 :     dimIdx--;
    3199             :     // cppcheck-suppress negativeContainerIndex
    3200         215 :     switch (stack[dimIdx].return_point)
    3201             :     {
    3202         182 :         case Caller::CALLER_END_OF_LOOP:
    3203         182 :             goto lbl_return_to_caller_end_of_loop;
    3204          33 :         case Caller::CALLER_IN_LOOP:
    3205          33 :             goto lbl_return_to_caller_in_loop;
    3206             :     }
    3207          59 : end:
    3208          59 :     return true;
    3209             : }
    3210             : 
    3211             : /************************************************************************/
    3212             : /*                          GDALAttribute()                             */
    3213             : /************************************************************************/
    3214             : 
    3215             : //! @cond Doxygen_Suppress
    3216       14399 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3217           0 :                              CPL_UNUSED const std::string &osName)
    3218             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3219       14399 :     : GDALAbstractMDArray(osParentName, osName)
    3220             : #endif
    3221             : {
    3222       14399 : }
    3223             : 
    3224             : GDALAttribute::~GDALAttribute() = default;
    3225             : 
    3226             : //! @endcond
    3227             : 
    3228             : /************************************************************************/
    3229             : /*                        GetDimensionSize()                            */
    3230             : /************************************************************************/
    3231             : 
    3232             : /** Return the size of the dimensions of the attribute.
    3233             :  *
    3234             :  * This will be an empty array for a scalar (single value) attribute.
    3235             :  *
    3236             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3237             :  */
    3238         380 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3239             : {
    3240         380 :     const auto &dims = GetDimensions();
    3241         380 :     std::vector<GUInt64> ret;
    3242         380 :     ret.reserve(dims.size());
    3243         493 :     for (const auto &dim : dims)
    3244         113 :         ret.push_back(dim->GetSize());
    3245         380 :     return ret;
    3246             : }
    3247             : 
    3248             : /************************************************************************/
    3249             : /*                            GDALRawResult()                           */
    3250             : /************************************************************************/
    3251             : 
    3252             : //! @cond Doxygen_Suppress
    3253         155 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3254         155 :                              size_t nEltCount)
    3255         310 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3256         155 :       m_raw(raw)
    3257             : {
    3258         155 : }
    3259             : 
    3260             : //! @endcond
    3261             : 
    3262             : /************************************************************************/
    3263             : /*                            GDALRawResult()                           */
    3264             : /************************************************************************/
    3265             : 
    3266             : /** Move constructor. */
    3267           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3268           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3269           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3270             : {
    3271           0 :     other.m_nEltCount = 0;
    3272           0 :     other.m_nSize = 0;
    3273           0 :     other.m_raw = nullptr;
    3274           0 : }
    3275             : 
    3276             : /************************************************************************/
    3277             : /*                               FreeMe()                               */
    3278             : /************************************************************************/
    3279             : 
    3280         155 : void GDALRawResult::FreeMe()
    3281             : {
    3282         155 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3283             :     {
    3284          50 :         GByte *pabyPtr = m_raw;
    3285          50 :         const auto nDTSize(m_dt.GetSize());
    3286         100 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3287             :         {
    3288          50 :             m_dt.FreeDynamicMemory(pabyPtr);
    3289          50 :             pabyPtr += nDTSize;
    3290             :         }
    3291             :     }
    3292         155 :     VSIFree(m_raw);
    3293         155 : }
    3294             : 
    3295             : /************************************************************************/
    3296             : /*                             operator=()                              */
    3297             : /************************************************************************/
    3298             : 
    3299             : /** Move assignment. */
    3300           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3301             : {
    3302           0 :     FreeMe();
    3303           0 :     m_dt = std::move(other.m_dt);
    3304           0 :     m_nEltCount = other.m_nEltCount;
    3305           0 :     m_nSize = other.m_nSize;
    3306           0 :     m_raw = other.m_raw;
    3307           0 :     other.m_nEltCount = 0;
    3308           0 :     other.m_nSize = 0;
    3309           0 :     other.m_raw = nullptr;
    3310           0 :     return *this;
    3311             : }
    3312             : 
    3313             : /************************************************************************/
    3314             : /*                         ~GDALRawResult()                             */
    3315             : /************************************************************************/
    3316             : 
    3317             : /** Destructor. */
    3318         155 : GDALRawResult::~GDALRawResult()
    3319             : {
    3320         155 :     FreeMe();
    3321         155 : }
    3322             : 
    3323             : /************************************************************************/
    3324             : /*                            StealData()                               */
    3325             : /************************************************************************/
    3326             : 
    3327             : //! @cond Doxygen_Suppress
    3328             : /** Return buffer to caller which becomes owner of it.
    3329             :  * Only to be used by GDALAttributeReadAsRaw().
    3330             :  */
    3331           6 : GByte *GDALRawResult::StealData()
    3332             : {
    3333           6 :     GByte *ret = m_raw;
    3334           6 :     m_raw = nullptr;
    3335           6 :     m_nEltCount = 0;
    3336           6 :     m_nSize = 0;
    3337           6 :     return ret;
    3338             : }
    3339             : 
    3340             : //! @endcond
    3341             : 
    3342             : /************************************************************************/
    3343             : /*                             ReadAsRaw()                              */
    3344             : /************************************************************************/
    3345             : 
    3346             : /** Return the raw value of an attribute.
    3347             :  *
    3348             :  *
    3349             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3350             :  */
    3351         155 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3352             : {
    3353         155 :     const auto nEltCount(GetTotalElementsCount());
    3354         155 :     const auto &dt(GetDataType());
    3355         155 :     const auto nDTSize(dt.GetSize());
    3356             :     GByte *res = static_cast<GByte *>(
    3357         155 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3358         155 :     if (!res)
    3359           0 :         return GDALRawResult(nullptr, dt, 0);
    3360         155 :     const auto &dims = GetDimensions();
    3361         155 :     const auto nDims = GetDimensionCount();
    3362         310 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3363         310 :     std::vector<size_t> count(1 + nDims);
    3364         177 :     for (size_t i = 0; i < nDims; i++)
    3365             :     {
    3366          22 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3367             :     }
    3368         155 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3369         155 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3370             :     {
    3371           0 :         VSIFree(res);
    3372           0 :         return GDALRawResult(nullptr, dt, 0);
    3373             :     }
    3374         155 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3375             : }
    3376             : 
    3377             : /************************************************************************/
    3378             : /*                            ReadAsString()                            */
    3379             : /************************************************************************/
    3380             : 
    3381             : /** Return the value of an attribute as a string.
    3382             :  *
    3383             :  * The returned string should not be freed, and its lifetime does not
    3384             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3385             :  * of the object itself.
    3386             :  *
    3387             :  * This function will only return the first element if there are several.
    3388             :  *
    3389             :  * This is the same as the C function GDALAttributeReadAsString()
    3390             :  *
    3391             :  * @return a string, or nullptr.
    3392             :  */
    3393        1370 : const char *GDALAttribute::ReadAsString() const
    3394             : {
    3395        1370 :     const auto nDims = GetDimensionCount();
    3396        2740 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3397        2740 :     std::vector<size_t> count(1 + nDims, 1);
    3398        1370 :     char *szRet = nullptr;
    3399        1370 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3400        1370 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3401        4109 :               sizeof(szRet)) ||
    3402        1369 :         szRet == nullptr)
    3403             :     {
    3404           4 :         return nullptr;
    3405             :     }
    3406        1366 :     m_osCachedVal = szRet;
    3407        1366 :     CPLFree(szRet);
    3408        1366 :     return m_osCachedVal.c_str();
    3409             : }
    3410             : 
    3411             : /************************************************************************/
    3412             : /*                            ReadAsInt()                               */
    3413             : /************************************************************************/
    3414             : 
    3415             : /** Return the value of an attribute as a integer.
    3416             :  *
    3417             :  * This function will only return the first element if there are several.
    3418             :  *
    3419             :  * It can fail if its value can not be converted to integer.
    3420             :  *
    3421             :  * This is the same as the C function GDALAttributeReadAsInt()
    3422             :  *
    3423             :  * @return a integer, or INT_MIN in case of error.
    3424             :  */
    3425         226 : int GDALAttribute::ReadAsInt() const
    3426             : {
    3427         226 :     const auto nDims = GetDimensionCount();
    3428         452 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3429         226 :     std::vector<size_t> count(1 + nDims, 1);
    3430         226 :     int nRet = INT_MIN;
    3431         226 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3432         452 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3433         452 :     return nRet;
    3434             : }
    3435             : 
    3436             : /************************************************************************/
    3437             : /*                            ReadAsInt64()                             */
    3438             : /************************************************************************/
    3439             : 
    3440             : /** Return the value of an attribute as an int64_t.
    3441             :  *
    3442             :  * This function will only return the first element if there are several.
    3443             :  *
    3444             :  * It can fail if its value can not be converted to long.
    3445             :  *
    3446             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3447             :  *
    3448             :  * @return an int64_t, or INT64_MIN in case of error.
    3449             :  */
    3450          54 : int64_t GDALAttribute::ReadAsInt64() const
    3451             : {
    3452          54 :     const auto nDims = GetDimensionCount();
    3453         108 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3454          54 :     std::vector<size_t> count(1 + nDims, 1);
    3455          54 :     int64_t nRet = INT64_MIN;
    3456          54 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3457         108 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3458         108 :     return nRet;
    3459             : }
    3460             : 
    3461             : /************************************************************************/
    3462             : /*                            ReadAsDouble()                            */
    3463             : /************************************************************************/
    3464             : 
    3465             : /** Return the value of an attribute as a double.
    3466             :  *
    3467             :  * This function will only return the first element if there are several.
    3468             :  *
    3469             :  * It can fail if its value can not be converted to double.
    3470             :  *
    3471             :  * This is the same as the C function GDALAttributeReadAsInt()
    3472             :  *
    3473             :  * @return a double value.
    3474             :  */
    3475         349 : double GDALAttribute::ReadAsDouble() const
    3476             : {
    3477         349 :     const auto nDims = GetDimensionCount();
    3478         698 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3479         349 :     std::vector<size_t> count(1 + nDims, 1);
    3480         349 :     double dfRet = 0;
    3481         349 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3482         349 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3483         349 :          sizeof(dfRet));
    3484         698 :     return dfRet;
    3485             : }
    3486             : 
    3487             : /************************************************************************/
    3488             : /*                          ReadAsStringArray()                         */
    3489             : /************************************************************************/
    3490             : 
    3491             : /** Return the value of an attribute as an array of strings.
    3492             :  *
    3493             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3494             :  */
    3495         132 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3496             : {
    3497         132 :     const auto nElts = GetTotalElementsCount();
    3498         132 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3499           0 :         return CPLStringList();
    3500             :     char **papszList = static_cast<char **>(
    3501         132 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3502         132 :     const auto &dims = GetDimensions();
    3503         132 :     const auto nDims = GetDimensionCount();
    3504         264 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3505         264 :     std::vector<size_t> count(1 + nDims);
    3506         209 :     for (size_t i = 0; i < nDims; i++)
    3507             :     {
    3508          77 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3509             :     }
    3510         132 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3511         132 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3512         132 :          sizeof(char *) * static_cast<int>(nElts));
    3513         551 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3514             :     {
    3515         419 :         if (papszList[i] == nullptr)
    3516          13 :             papszList[i] = CPLStrdup("");
    3517             :     }
    3518         132 :     return CPLStringList(papszList);
    3519             : }
    3520             : 
    3521             : /************************************************************************/
    3522             : /*                          ReadAsIntArray()                            */
    3523             : /************************************************************************/
    3524             : 
    3525             : /** Return the value of an attribute as an array of integers.
    3526             :  *
    3527             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3528             :  */
    3529          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3530             : {
    3531          15 :     const auto nElts = GetTotalElementsCount();
    3532             : #if SIZEOF_VOIDP == 4
    3533             :     if (nElts > static_cast<size_t>(nElts))
    3534             :         return {};
    3535             : #endif
    3536          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3537          15 :     const auto &dims = GetDimensions();
    3538          15 :     const auto nDims = GetDimensionCount();
    3539          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3540          30 :     std::vector<size_t> count(1 + nDims);
    3541          32 :     for (size_t i = 0; i < nDims; i++)
    3542             :     {
    3543          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3544             :     }
    3545          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3546          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3547          15 :          res.size() * sizeof(res[0]));
    3548          30 :     return res;
    3549             : }
    3550             : 
    3551             : /************************************************************************/
    3552             : /*                          ReadAsInt64Array()                          */
    3553             : /************************************************************************/
    3554             : 
    3555             : /** Return the value of an attribute as an array of int64_t.
    3556             :  *
    3557             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3558             :  */
    3559          38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3560             : {
    3561          38 :     const auto nElts = GetTotalElementsCount();
    3562             : #if SIZEOF_VOIDP == 4
    3563             :     if (nElts > static_cast<size_t>(nElts))
    3564             :         return {};
    3565             : #endif
    3566          38 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3567          38 :     const auto &dims = GetDimensions();
    3568          38 :     const auto nDims = GetDimensionCount();
    3569          76 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3570          76 :     std::vector<size_t> count(1 + nDims);
    3571          76 :     for (size_t i = 0; i < nDims; i++)
    3572             :     {
    3573          38 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3574             :     }
    3575          38 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3576          76 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3577          38 :          res.size() * sizeof(res[0]));
    3578          76 :     return res;
    3579             : }
    3580             : 
    3581             : /************************************************************************/
    3582             : /*                         ReadAsDoubleArray()                          */
    3583             : /************************************************************************/
    3584             : 
    3585             : /** Return the value of an attribute as an array of double.
    3586             :  *
    3587             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3588             :  */
    3589          88 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3590             : {
    3591          88 :     const auto nElts = GetTotalElementsCount();
    3592             : #if SIZEOF_VOIDP == 4
    3593             :     if (nElts > static_cast<size_t>(nElts))
    3594             :         return {};
    3595             : #endif
    3596          88 :     std::vector<double> res(static_cast<size_t>(nElts));
    3597          88 :     const auto &dims = GetDimensions();
    3598          88 :     const auto nDims = GetDimensionCount();
    3599         176 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3600         176 :     std::vector<size_t> count(1 + nDims);
    3601         160 :     for (size_t i = 0; i < nDims; i++)
    3602             :     {
    3603          72 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3604             :     }
    3605          88 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3606         176 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3607          88 :          res.size() * sizeof(res[0]));
    3608         176 :     return res;
    3609             : }
    3610             : 
    3611             : /************************************************************************/
    3612             : /*                               Write()                                */
    3613             : /************************************************************************/
    3614             : 
    3615             : /** Write an attribute from raw values expressed in GetDataType()
    3616             :  *
    3617             :  * The values should be provided in the type of GetDataType() and there should
    3618             :  * be exactly GetTotalElementsCount() of them.
    3619             :  * If GetDataType() is a string, each value should be a char* pointer.
    3620             :  *
    3621             :  * This is the same as the C function GDALAttributeWriteRaw().
    3622             :  *
    3623             :  * @param pabyValue Buffer of nLen bytes.
    3624             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3625             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3626             :  * @return true in case of success.
    3627             :  */
    3628          96 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3629             : {
    3630          96 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3631             :     {
    3632           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3633             :                  "Length is not of expected value");
    3634           0 :         return false;
    3635             :     }
    3636          96 :     const auto &dims = GetDimensions();
    3637          96 :     const auto nDims = GetDimensionCount();
    3638         192 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3639         192 :     std::vector<size_t> count(1 + nDims);
    3640         119 :     for (size_t i = 0; i < nDims; i++)
    3641             :     {
    3642          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3643             :     }
    3644          96 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3645          96 :                  pabyValue, pabyValue, nLen);
    3646             : }
    3647             : 
    3648             : /************************************************************************/
    3649             : /*                               Write()                                */
    3650             : /************************************************************************/
    3651             : 
    3652             : /** Write an attribute from a string value.
    3653             :  *
    3654             :  * Type conversion will be performed if needed. If the attribute contains
    3655             :  * multiple values, only the first one will be updated.
    3656             :  *
    3657             :  * This is the same as the C function GDALAttributeWriteString().
    3658             :  *
    3659             :  * @param pszValue Pointer to a string.
    3660             :  * @return true in case of success.
    3661             :  */
    3662         306 : bool GDALAttribute::Write(const char *pszValue)
    3663             : {
    3664         306 :     const auto nDims = GetDimensionCount();
    3665         612 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3666         306 :     std::vector<size_t> count(1 + nDims, 1);
    3667         306 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3668         612 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3669         612 :                  sizeof(pszValue));
    3670             : }
    3671             : 
    3672             : /************************************************************************/
    3673             : /*                              WriteInt()                              */
    3674             : /************************************************************************/
    3675             : 
    3676             : /** Write an attribute from a integer value.
    3677             :  *
    3678             :  * Type conversion will be performed if needed. If the attribute contains
    3679             :  * multiple values, only the first one will be updated.
    3680             :  *
    3681             :  * This is the same as the C function GDALAttributeWriteInt().
    3682             :  *
    3683             :  * @param nVal Value.
    3684             :  * @return true in case of success.
    3685             :  */
    3686          22 : bool GDALAttribute::WriteInt(int nVal)
    3687             : {
    3688          22 :     const auto nDims = GetDimensionCount();
    3689          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3690          22 :     std::vector<size_t> count(1 + nDims, 1);
    3691          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3692          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3693          44 :                  sizeof(nVal));
    3694             : }
    3695             : 
    3696             : /************************************************************************/
    3697             : /*                              WriteInt64()                             */
    3698             : /************************************************************************/
    3699             : 
    3700             : /** Write an attribute from an int64_t value.
    3701             :  *
    3702             :  * Type conversion will be performed if needed. If the attribute contains
    3703             :  * multiple values, only the first one will be updated.
    3704             :  *
    3705             :  * This is the same as the C function GDALAttributeWriteInt().
    3706             :  *
    3707             :  * @param nVal Value.
    3708             :  * @return true in case of success.
    3709             :  */
    3710          11 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3711             : {
    3712          11 :     const auto nDims = GetDimensionCount();
    3713          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3714          11 :     std::vector<size_t> count(1 + nDims, 1);
    3715          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3716          22 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3717          22 :                  sizeof(nVal));
    3718             : }
    3719             : 
    3720             : /************************************************************************/
    3721             : /*                                Write()                               */
    3722             : /************************************************************************/
    3723             : 
    3724             : /** Write an attribute from a double value.
    3725             :  *
    3726             :  * Type conversion will be performed if needed. If the attribute contains
    3727             :  * multiple values, only the first one will be updated.
    3728             :  *
    3729             :  * This is the same as the C function GDALAttributeWriteDouble().
    3730             :  *
    3731             :  * @param dfVal Value.
    3732             :  * @return true in case of success.
    3733             :  */
    3734          38 : bool GDALAttribute::Write(double dfVal)
    3735             : {
    3736          38 :     const auto nDims = GetDimensionCount();
    3737          76 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3738          38 :     std::vector<size_t> count(1 + nDims, 1);
    3739          38 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3740          76 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3741          76 :                  sizeof(dfVal));
    3742             : }
    3743             : 
    3744             : /************************************************************************/
    3745             : /*                                Write()                               */
    3746             : /************************************************************************/
    3747             : 
    3748             : /** Write an attribute from an array of strings.
    3749             :  *
    3750             :  * Type conversion will be performed if needed.
    3751             :  *
    3752             :  * Exactly GetTotalElementsCount() strings must be provided
    3753             :  *
    3754             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3755             :  *
    3756             :  * @param vals Array of strings.
    3757             :  * @return true in case of success.
    3758             :  */
    3759           8 : bool GDALAttribute::Write(CSLConstList vals)
    3760             : {
    3761           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3762             :     {
    3763           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3764           1 :         return false;
    3765             :     }
    3766           7 :     const auto nDims = GetDimensionCount();
    3767          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3768           7 :     std::vector<size_t> count(1 + nDims);
    3769           7 :     const auto &dims = GetDimensions();
    3770          15 :     for (size_t i = 0; i < nDims; i++)
    3771           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3772           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3773           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3774          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3775             : }
    3776             : 
    3777             : /************************************************************************/
    3778             : /*                                Write()                               */
    3779             : /************************************************************************/
    3780             : 
    3781             : /** Write an attribute from an array of int.
    3782             :  *
    3783             :  * Type conversion will be performed if needed.
    3784             :  *
    3785             :  * Exactly GetTotalElementsCount() strings must be provided
    3786             :  *
    3787             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3788             :  *
    3789             :  * @param vals Array of int.
    3790             :  * @param nVals Should be equal to GetTotalElementsCount().
    3791             :  * @return true in case of success.
    3792             :  */
    3793          11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3794             : {
    3795          11 :     if (nVals != GetTotalElementsCount())
    3796             :     {
    3797           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3798           1 :         return false;
    3799             :     }
    3800          10 :     const auto nDims = GetDimensionCount();
    3801          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3802          10 :     std::vector<size_t> count(1 + nDims);
    3803          10 :     const auto &dims = GetDimensions();
    3804          20 :     for (size_t i = 0; i < nDims; i++)
    3805          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3806          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3807          10 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3808          20 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3809             : }
    3810             : 
    3811             : /************************************************************************/
    3812             : /*                                Write()                               */
    3813             : /************************************************************************/
    3814             : 
    3815             : /** Write an attribute from an array of int64_t.
    3816             :  *
    3817             :  * Type conversion will be performed if needed.
    3818             :  *
    3819             :  * Exactly GetTotalElementsCount() strings must be provided
    3820             :  *
    3821             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3822             :  *
    3823             :  * @param vals Array of int64_t.
    3824             :  * @param nVals Should be equal to GetTotalElementsCount().
    3825             :  * @return true in case of success.
    3826             :  */
    3827          10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3828             : {
    3829          10 :     if (nVals != GetTotalElementsCount())
    3830             :     {
    3831           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3832           0 :         return false;
    3833             :     }
    3834          10 :     const auto nDims = GetDimensionCount();
    3835          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3836          10 :     std::vector<size_t> count(1 + nDims);
    3837          10 :     const auto &dims = GetDimensions();
    3838          20 :     for (size_t i = 0; i < nDims; i++)
    3839          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3840          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3841          10 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3842          10 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3843          10 :                      sizeof(int64_t));
    3844             : }
    3845             : 
    3846             : /************************************************************************/
    3847             : /*                                Write()                               */
    3848             : /************************************************************************/
    3849             : 
    3850             : /** Write an attribute from an array of double.
    3851             :  *
    3852             :  * Type conversion will be performed if needed.
    3853             :  *
    3854             :  * Exactly GetTotalElementsCount() strings must be provided
    3855             :  *
    3856             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3857             :  *
    3858             :  * @param vals Array of double.
    3859             :  * @param nVals Should be equal to GetTotalElementsCount().
    3860             :  * @return true in case of success.
    3861             :  */
    3862           7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3863             : {
    3864           7 :     if (nVals != GetTotalElementsCount())
    3865             :     {
    3866           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3867           1 :         return false;
    3868             :     }
    3869           6 :     const auto nDims = GetDimensionCount();
    3870          12 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3871           6 :     std::vector<size_t> count(1 + nDims);
    3872           6 :     const auto &dims = GetDimensions();
    3873          13 :     for (size_t i = 0; i < nDims; i++)
    3874           7 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3875           6 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3876           6 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3877          12 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3878             : }
    3879             : 
    3880             : /************************************************************************/
    3881             : /*                           GDALMDArray()                              */
    3882             : /************************************************************************/
    3883             : 
    3884             : //! @cond Doxygen_Suppress
    3885        6570 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3886             :                          CPL_UNUSED const std::string &osName,
    3887           0 :                          const std::string &osContext)
    3888             :     :
    3889             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3890             :       GDALAbstractMDArray(osParentName, osName),
    3891             : #endif
    3892        6570 :       m_osContext(osContext)
    3893             : {
    3894        6570 : }
    3895             : 
    3896             : //! @endcond
    3897             : 
    3898             : /************************************************************************/
    3899             : /*                           GetTotalCopyCost()                         */
    3900             : /************************************************************************/
    3901             : 
    3902             : /** Return a total "cost" to copy the array.
    3903             :  *
    3904             :  * Used as a parameter for CopyFrom()
    3905             :  */
    3906          50 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3907             : {
    3908         100 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3909         100 :            GetTotalElementsCount() * GetDataType().GetSize();
    3910             : }
    3911             : 
    3912             : /************************************************************************/
    3913             : /*                       CopyFromAllExceptValues()                      */
    3914             : /************************************************************************/
    3915             : 
    3916             : //! @cond Doxygen_Suppress
    3917             : 
    3918         175 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3919             :                                           bool bStrict, GUInt64 &nCurCost,
    3920             :                                           const GUInt64 nTotalCost,
    3921             :                                           GDALProgressFunc pfnProgress,
    3922             :                                           void *pProgressData)
    3923             : {
    3924             :     // Nodata setting must be one of the first things done for TileDB
    3925         175 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3926         175 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3927             :     {
    3928          13 :         SetRawNoDataValue(pNoData);
    3929             :     }
    3930             : 
    3931         175 :     const bool bThisIsUnscaledArray =
    3932         175 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3933         350 :     auto attrs = poSrcArray->GetAttributes();
    3934         222 :     for (const auto &attr : attrs)
    3935             :     {
    3936          47 :         const auto &osAttrName = attr->GetName();
    3937          47 :         if (bThisIsUnscaledArray)
    3938             :         {
    3939           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3940           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3941           1 :                 osAttrName == "valid_range")
    3942             :             {
    3943           1 :                 continue;
    3944             :             }
    3945             :         }
    3946             : 
    3947          46 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3948          92 :                                        attr->GetDataType());
    3949          46 :         if (!dstAttr)
    3950             :         {
    3951           0 :             if (bStrict)
    3952           0 :                 return false;
    3953           0 :             continue;
    3954             :         }
    3955          46 :         auto raw = attr->ReadAsRaw();
    3956          46 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3957           0 :             return false;
    3958             :     }
    3959         175 :     if (!attrs.empty())
    3960             :     {
    3961          26 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3962          46 :         if (pfnProgress &&
    3963          20 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3964           0 :             return false;
    3965             :     }
    3966             : 
    3967         175 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3968         175 :     if (srcSRS)
    3969             :     {
    3970          11 :         SetSpatialRef(srcSRS.get());
    3971             :     }
    3972             : 
    3973         175 :     const std::string &osUnit(poSrcArray->GetUnit());
    3974         175 :     if (!osUnit.empty())
    3975             :     {
    3976          18 :         SetUnit(osUnit);
    3977             :     }
    3978             : 
    3979         175 :     bool bGotValue = false;
    3980         175 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    3981             :     const double dfOffset =
    3982         175 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    3983         175 :     if (bGotValue)
    3984             :     {
    3985           3 :         SetOffset(dfOffset, eOffsetStorageType);
    3986             :     }
    3987             : 
    3988         175 :     bGotValue = false;
    3989         175 :     GDALDataType eScaleStorageType = GDT_Unknown;
    3990         175 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    3991         175 :     if (bGotValue)
    3992             :     {
    3993           3 :         SetScale(dfScale, eScaleStorageType);
    3994             :     }
    3995             : 
    3996         175 :     return true;
    3997             : }
    3998             : 
    3999             : //! @endcond
    4000             : 
    4001             : /************************************************************************/
    4002             : /*                               CopyFrom()                             */
    4003             : /************************************************************************/
    4004             : 
    4005             : /** Copy the content of an array into a new (generally empty) array.
    4006             :  *
    4007             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    4008             :  *                   of some output drivers this is not recommended)
    4009             :  * @param poSrcArray Source array. Should NOT be nullptr.
    4010             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    4011             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    4012             :  *                be pursued.
    4013             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    4014             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    4015             :  * @param pfnProgress Progress callback, or nullptr.
    4016             :  * @param pProgressData Progress user data, or nulptr.
    4017             :  *
    4018             :  * @return true in case of success (or partial success if bStrict == false).
    4019             :  */
    4020          48 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    4021             :                            const GDALMDArray *poSrcArray, bool bStrict,
    4022             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    4023             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    4024             : {
    4025          48 :     if (pfnProgress == nullptr)
    4026           4 :         pfnProgress = GDALDummyProgress;
    4027             : 
    4028          48 :     nCurCost += GDALMDArray::COPY_COST;
    4029             : 
    4030          48 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    4031             :                                  pfnProgress, pProgressData))
    4032             :     {
    4033           0 :         return false;
    4034             :     }
    4035             : 
    4036          48 :     const auto &dims = poSrcArray->GetDimensions();
    4037          48 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    4038          48 :     if (dims.empty())
    4039             :     {
    4040           2 :         std::vector<GByte> abyTmp(nDTSize);
    4041           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    4042           2 :                                GetDataType(), &abyTmp[0]) &&
    4043           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    4044           4 :                     &abyTmp[0])) &&
    4045             :             bStrict)
    4046             :         {
    4047           0 :             return false;
    4048             :         }
    4049           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    4050           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    4051           0 :             return false;
    4052             :     }
    4053             :     else
    4054             :     {
    4055          46 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    4056          46 :         std::vector<GUInt64> count(dims.size());
    4057         125 :         for (size_t i = 0; i < dims.size(); i++)
    4058             :         {
    4059          79 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    4060             :         }
    4061             : 
    4062             :         struct CopyFunc
    4063             :         {
    4064             :             GDALMDArray *poDstArray = nullptr;
    4065             :             std::vector<GByte> abyTmp{};
    4066             :             GDALProgressFunc pfnProgress = nullptr;
    4067             :             void *pProgressData = nullptr;
    4068             :             GUInt64 nCurCost = 0;
    4069             :             GUInt64 nTotalCost = 0;
    4070             :             GUInt64 nTotalBytesThisArray = 0;
    4071             :             bool bStop = false;
    4072             : 
    4073          64 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    4074             :                           const GUInt64 *chunkArrayStartIdx,
    4075             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    4076             :                           GUInt64 nChunkCount, void *pUserData)
    4077             :             {
    4078          64 :                 const auto &dt(l_poSrcArray->GetDataType());
    4079          64 :                 auto data = static_cast<CopyFunc *>(pUserData);
    4080          64 :                 auto poDstArray = data->poDstArray;
    4081          64 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    4082          64 :                                         nullptr, dt, &data->abyTmp[0]))
    4083             :                 {
    4084           0 :                     return false;
    4085             :                 }
    4086             :                 bool bRet =
    4087          64 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    4088          64 :                                       nullptr, dt, &data->abyTmp[0]);
    4089          64 :                 if (dt.NeedsFreeDynamicMemory())
    4090             :                 {
    4091           4 :                     const auto l_nDTSize = dt.GetSize();
    4092           4 :                     GByte *ptr = &data->abyTmp[0];
    4093           4 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    4094           4 :                     size_t nEltCount = 1;
    4095           8 :                     for (size_t i = 0; i < l_nDims; ++i)
    4096             :                     {
    4097           4 :                         nEltCount *= chunkCount[i];
    4098             :                     }
    4099          20 :                     for (size_t i = 0; i < nEltCount; i++)
    4100             :                     {
    4101          16 :                         dt.FreeDynamicMemory(ptr);
    4102          16 :                         ptr += l_nDTSize;
    4103             :                     }
    4104             :                 }
    4105          64 :                 if (!bRet)
    4106             :                 {
    4107           0 :                     return false;
    4108             :                 }
    4109             : 
    4110          64 :                 double dfCurCost =
    4111          64 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4112          64 :                                                  data->nTotalBytesThisArray;
    4113          64 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4114             :                                        data->pProgressData))
    4115             :                 {
    4116           0 :                     data->bStop = true;
    4117           0 :                     return false;
    4118             :                 }
    4119             : 
    4120          64 :                 return true;
    4121             :             }
    4122             :         };
    4123             : 
    4124          46 :         CopyFunc copyFunc;
    4125          46 :         copyFunc.poDstArray = this;
    4126          46 :         copyFunc.nCurCost = nCurCost;
    4127          46 :         copyFunc.nTotalCost = nTotalCost;
    4128          46 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4129          46 :         copyFunc.pfnProgress = pfnProgress;
    4130          46 :         copyFunc.pProgressData = pProgressData;
    4131             :         const char *pszSwathSize =
    4132          46 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4133             :         const size_t nMaxChunkSize =
    4134             :             pszSwathSize
    4135          46 :                 ? static_cast<size_t>(
    4136           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4137           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4138             :                 : static_cast<size_t>(
    4139          45 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4140          45 :                                GDALGetCacheMax64() / 4));
    4141          46 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4142          46 :         size_t nRealChunkSize = nDTSize;
    4143         125 :         for (const auto &nChunkSize : anChunkSizes)
    4144             :         {
    4145          79 :             nRealChunkSize *= nChunkSize;
    4146             :         }
    4147             :         try
    4148             :         {
    4149          46 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4150             :         }
    4151           0 :         catch (const std::exception &)
    4152             :         {
    4153           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4154             :                      "Cannot allocate temporary buffer");
    4155           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4156           0 :             return false;
    4157             :         }
    4158         137 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4159          45 :             !const_cast<GDALMDArray *>(poSrcArray)
    4160          45 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4161             :                                    anChunkSizes.data(), CopyFunc::f,
    4162          91 :                                    &copyFunc) &&
    4163           0 :             (bStrict || copyFunc.bStop))
    4164             :         {
    4165           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4166           0 :             return false;
    4167             :         }
    4168          46 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4169             :     }
    4170             : 
    4171          48 :     return true;
    4172             : }
    4173             : 
    4174             : /************************************************************************/
    4175             : /*                         GetStructuralInfo()                          */
    4176             : /************************************************************************/
    4177             : 
    4178             : /** Return structural information on the array.
    4179             :  *
    4180             :  * This may be the compression, etc..
    4181             :  *
    4182             :  * The return value should not be freed and is valid until GDALMDArray is
    4183             :  * released or this function called again.
    4184             :  *
    4185             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4186             :  */
    4187          95 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4188             : {
    4189          95 :     return nullptr;
    4190             : }
    4191             : 
    4192             : /************************************************************************/
    4193             : /*                          AdviseRead()                                */
    4194             : /************************************************************************/
    4195             : 
    4196             : /** Advise driver of upcoming read requests.
    4197             :  *
    4198             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4199             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4200             :  * an application to notify the driver of the region of interest.
    4201             :  *
    4202             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4203             :  * accelerate access via some drivers. One such case is when reading through
    4204             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4205             :  * with the region of interest defined by AdviseRead())
    4206             :  *
    4207             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4208             :  *
    4209             :  * @param arrayStartIdx Values representing the starting index to read
    4210             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4211             :  *                      Array of GetDimensionCount() values.
    4212             :  *                      Can be nullptr as a synonymous for [0 for i in
    4213             :  * range(GetDimensionCount() ]
    4214             :  *
    4215             :  * @param count         Values representing the number of values to extract in
    4216             :  *                      each dimension.
    4217             :  *                      Array of GetDimensionCount() values.
    4218             :  *                      Can be nullptr as a synonymous for
    4219             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4220             :  * range(GetDimensionCount() ]
    4221             :  *
    4222             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4223             :  * documentation.
    4224             :  *
    4225             :  * @return true in case of success (ignoring the advice is a success)
    4226             :  *
    4227             :  * @since GDAL 3.2
    4228             :  */
    4229          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4230             :                              CSLConstList papszOptions) const
    4231             : {
    4232          25 :     const auto nDimCount = GetDimensionCount();
    4233          25 :     if (nDimCount == 0)
    4234           2 :         return true;
    4235             : 
    4236          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4237          23 :     if (arrayStartIdx == nullptr)
    4238             :     {
    4239           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4240           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4241             :     }
    4242             : 
    4243          46 :     std::vector<size_t> tmp_count;
    4244          23 :     if (count == nullptr)
    4245             :     {
    4246           0 :         tmp_count.resize(nDimCount);
    4247           0 :         const auto &dims = GetDimensions();
    4248           0 :         for (size_t i = 0; i < nDimCount; i++)
    4249             :         {
    4250           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4251             : #if SIZEOF_VOIDP < 8
    4252             :             if (nSize != static_cast<size_t>(nSize))
    4253             :             {
    4254             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4255             :                 return false;
    4256             :             }
    4257             : #endif
    4258           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4259             :         }
    4260           0 :         count = tmp_count.data();
    4261             :     }
    4262             : 
    4263          46 :     std::vector<GInt64> tmp_arrayStep;
    4264          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4265          23 :     const GInt64 *arrayStep = nullptr;
    4266          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4267          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4268          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4269             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4270             :                               tmp_bufferStride))
    4271             :     {
    4272           1 :         return false;
    4273             :     }
    4274             : 
    4275          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4276             : }
    4277             : 
    4278             : /************************************************************************/
    4279             : /*                             IAdviseRead()                            */
    4280             : /************************************************************************/
    4281             : 
    4282             : //! @cond Doxygen_Suppress
    4283           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4284             :                               CSLConstList /* papszOptions*/) const
    4285             : {
    4286           3 :     return true;
    4287             : }
    4288             : 
    4289             : //! @endcond
    4290             : 
    4291             : /************************************************************************/
    4292             : /*                            MassageName()                             */
    4293             : /************************************************************************/
    4294             : 
    4295             : //! @cond Doxygen_Suppress
    4296          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4297             : {
    4298          32 :     std::string ret;
    4299         604 :     for (const char ch : inputName)
    4300             :     {
    4301         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4302         138 :             ret += '_';
    4303             :         else
    4304         434 :             ret += ch;
    4305             :     }
    4306          32 :     return ret;
    4307             : }
    4308             : 
    4309             : //! @endcond
    4310             : 
    4311             : /************************************************************************/
    4312             : /*                         GetCacheRootGroup()                          */
    4313             : /************************************************************************/
    4314             : 
    4315             : //! @cond Doxygen_Suppress
    4316             : std::shared_ptr<GDALGroup>
    4317        1442 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4318             :                                std::string &osCacheFilenameOut) const
    4319             : {
    4320        1442 :     const auto &osFilename = GetFilename();
    4321        1442 :     if (osFilename.empty())
    4322             :     {
    4323           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4324             :                  "Cannot cache an array with an empty filename");
    4325           1 :         return nullptr;
    4326             :     }
    4327             : 
    4328        1441 :     osCacheFilenameOut = osFilename + ".gmac";
    4329        1441 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4330             :     {
    4331           0 :         const auto nPosQuestionMark = osFilename.find('?');
    4332           0 :         if (nPosQuestionMark != std::string::npos)
    4333             :         {
    4334             :             osCacheFilenameOut =
    4335           0 :                 osFilename.substr(0, nPosQuestionMark)
    4336           0 :                     .append(".gmac")
    4337           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4338             :         }
    4339             :     }
    4340        1441 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4341        1441 :     if (pszProxy != nullptr)
    4342           7 :         osCacheFilenameOut = pszProxy;
    4343             : 
    4344        1441 :     std::unique_ptr<GDALDataset> poDS;
    4345             :     VSIStatBufL sStat;
    4346        1441 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4347             :     {
    4348          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4349             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4350             :                                      nullptr, nullptr, nullptr));
    4351             :     }
    4352        1441 :     if (poDS)
    4353             :     {
    4354          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4355          28 :         return poDS->GetRootGroup();
    4356             :     }
    4357             : 
    4358        1413 :     if (bCanCreate)
    4359             :     {
    4360           4 :         const char *pszDrvName = "netCDF";
    4361           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4362           4 :         if (poDrv == nullptr)
    4363             :         {
    4364           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4365             :                      pszDrvName);
    4366           0 :             return nullptr;
    4367             :         }
    4368             :         {
    4369           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4370           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4371           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4372             :                                                      nullptr, nullptr));
    4373             :         }
    4374           4 :         if (!poDS)
    4375             :         {
    4376           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4377           1 :             if (pszProxy)
    4378             :             {
    4379           1 :                 osCacheFilenameOut = pszProxy;
    4380           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4381             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4382             :             }
    4383             :         }
    4384           4 :         if (poDS)
    4385             :         {
    4386           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4387           4 :             return poDS->GetRootGroup();
    4388             :         }
    4389             :         else
    4390             :         {
    4391           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4392             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4393             :                      "configuration option to write the cache in "
    4394             :                      "another directory",
    4395             :                      osCacheFilenameOut.c_str());
    4396             :         }
    4397             :     }
    4398             : 
    4399        1409 :     return nullptr;
    4400             : }
    4401             : 
    4402             : //! @endcond
    4403             : 
    4404             : /************************************************************************/
    4405             : /*                              Cache()                                 */
    4406             : /************************************************************************/
    4407             : 
    4408             : /** Cache the content of the array into an auxiliary filename.
    4409             :  *
    4410             :  * The main purpose of this method is to be able to cache views that are
    4411             :  * expensive to compute, such as transposed arrays.
    4412             :  *
    4413             :  * The array will be stored in a file whose name is the one of
    4414             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4415             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4416             :  *
    4417             :  * If the .gmac file cannot be written next to the dataset, the
    4418             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4419             :  * directory.
    4420             :  *
    4421             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4422             :  * exists. There is no timestamp checks between the source array and the cached
    4423             :  * array. If the source arrays changes, the cache must be manually deleted.
    4424             :  *
    4425             :  * This is the same as the C function GDALMDArrayCache()
    4426             :  *
    4427             :  * @note Driver implementation: optionally implemented.
    4428             :  *
    4429             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4430             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4431             :  *                     to specify the block size of the cached array.
    4432             :  * @return true in case of success.
    4433             :  */
    4434           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4435             : {
    4436          14 :     std::string osCacheFilename;
    4437          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4438           7 :     if (!poRG)
    4439           1 :         return false;
    4440             : 
    4441          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4442           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4443             :     {
    4444           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4445             :                  "An array with same name %s already exists in %s",
    4446             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4447           2 :         return false;
    4448             :     }
    4449             : 
    4450           8 :     CPLStringList aosOptions;
    4451           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4452           4 :     const auto &aoDims = GetDimensions();
    4453           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4454           4 :     if (!aoDims.empty())
    4455             :     {
    4456             :         std::string osBlockSize(
    4457           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4458           4 :         if (osBlockSize.empty())
    4459             :         {
    4460           6 :             const auto anBlockSize = GetBlockSize();
    4461           3 :             int idxDim = 0;
    4462          10 :             for (auto nBlockSize : anBlockSize)
    4463             :             {
    4464           7 :                 if (idxDim > 0)
    4465           4 :                     osBlockSize += ',';
    4466           7 :                 if (nBlockSize == 0)
    4467           7 :                     nBlockSize = 256;
    4468           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4469             :                 osBlockSize +=
    4470           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4471           7 :                 idxDim++;
    4472             :             }
    4473             :         }
    4474           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4475             : 
    4476           4 :         int idxDim = 0;
    4477          13 :         for (const auto &poDim : aoDims)
    4478             :         {
    4479           9 :             auto poNewDim = poRG->CreateDimension(
    4480          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4481          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4482           9 :             if (!poNewDim)
    4483           0 :                 return false;
    4484           9 :             aoNewDims.emplace_back(poNewDim);
    4485           9 :             idxDim++;
    4486             :         }
    4487             :     }
    4488             : 
    4489           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4490           8 :                                              GetDataType(), aosOptions.List());
    4491           4 :     if (!poCachedArray)
    4492             :     {
    4493           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4494             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4495           0 :         return false;
    4496             :     }
    4497             : 
    4498           4 :     GUInt64 nCost = 0;
    4499           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4500             :                                    false,  // strict
    4501           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4502             : }
    4503             : 
    4504             : /************************************************************************/
    4505             : /*                               Read()                                 */
    4506             : /************************************************************************/
    4507             : 
    4508        3913 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4509             :                        const GInt64 *arrayStep,         // step in elements
    4510             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4511             :                        const GDALExtendedDataType &bufferDataType,
    4512             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4513             :                        size_t nDstBufferAllocSize) const
    4514             : {
    4515        3913 :     if (!m_bHasTriedCachedArray)
    4516             :     {
    4517        1748 :         m_bHasTriedCachedArray = true;
    4518        1748 :         if (IsCacheable())
    4519             :         {
    4520        1748 :             const auto &osFilename = GetFilename();
    4521        2986 :             if (!osFilename.empty() &&
    4522        2986 :                 !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
    4523             :             {
    4524        2456 :                 std::string osCacheFilename;
    4525        2456 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4526        1228 :                 if (poRG)
    4527             :                 {
    4528             :                     const std::string osCachedArrayName(
    4529          32 :                         MassageName(GetFullName()));
    4530          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4531          16 :                     if (m_poCachedArray)
    4532             :                     {
    4533           6 :                         const auto &dims = GetDimensions();
    4534             :                         const auto &cachedDims =
    4535           6 :                             m_poCachedArray->GetDimensions();
    4536           6 :                         const size_t nDims = dims.size();
    4537             :                         bool ok =
    4538          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4539           6 :                             cachedDims.size() == nDims;
    4540          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4541             :                         {
    4542          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4543             :                         }
    4544           6 :                         if (ok)
    4545             :                         {
    4546           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4547             :                                      osCachedArrayName.c_str(),
    4548             :                                      osCacheFilename.c_str());
    4549             :                         }
    4550             :                         else
    4551             :                         {
    4552           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4553             :                                      "Cached array %s in %s has incompatible "
    4554             :                                      "characteristics with current array.",
    4555             :                                      osCachedArrayName.c_str(),
    4556             :                                      osCacheFilename.c_str());
    4557           0 :                             m_poCachedArray.reset();
    4558             :                         }
    4559             :                     }
    4560             :                 }
    4561             :             }
    4562             :         }
    4563             :     }
    4564             : 
    4565        3913 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4566        3913 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4567             :     {
    4568           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4569             :                  "Array data type is not convertible to buffer data type");
    4570           0 :         return false;
    4571             :     }
    4572             : 
    4573        7826 :     std::vector<GInt64> tmp_arrayStep;
    4574        7826 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4575        3913 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4576             :                                      bufferStride, bufferDataType, pDstBuffer,
    4577             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4578             :                                      tmp_arrayStep, tmp_bufferStride))
    4579             :     {
    4580           9 :         return false;
    4581             :     }
    4582             : 
    4583        3904 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4584        3904 :                         bufferDataType, pDstBuffer);
    4585             : }
    4586             : 
    4587             : /************************************************************************/
    4588             : /*                          GetRootGroup()                              */
    4589             : /************************************************************************/
    4590             : 
    4591             : /** Return the root group to which this arrays belongs too.
    4592             :  *
    4593             :  * Note that arrays may be free standing and some drivers may not implement
    4594             :  * this method, hence nullptr may be returned.
    4595             :  *
    4596             :  * It is used internally by the GetResampled() method to detect if GLT
    4597             :  * orthorectification is available.
    4598             :  *
    4599             :  * @return the root group, or nullptr.
    4600             :  * @since GDAL 3.8
    4601             :  */
    4602           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4603             : {
    4604           0 :     return nullptr;
    4605             : }
    4606             : 
    4607             : //! @cond Doxygen_Suppress
    4608             : 
    4609             : /************************************************************************/
    4610             : /*                       IsTransposedRequest()                          */
    4611             : /************************************************************************/
    4612             : 
    4613         788 : bool GDALMDArray::IsTransposedRequest(
    4614             :     const size_t *count,
    4615             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4616             : {
    4617             :     /*
    4618             :     For example:
    4619             :     count = [2,3,4]
    4620             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4621             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4622             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4623             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4624             :     */
    4625         788 :     const size_t nDims(GetDimensionCount());
    4626         788 :     size_t nCurStrideForRowMajorStrides = 1;
    4627         788 :     bool bRowMajorStrides = true;
    4628         788 :     size_t nElts = 1;
    4629         788 :     size_t nLastIdx = 0;
    4630        2169 :     for (size_t i = nDims; i > 0;)
    4631             :     {
    4632        1381 :         --i;
    4633        1381 :         if (bufferStride[i] < 0)
    4634           0 :             return false;
    4635        1381 :         if (static_cast<size_t>(bufferStride[i]) !=
    4636             :             nCurStrideForRowMajorStrides)
    4637             :         {
    4638         258 :             bRowMajorStrides = false;
    4639             :         }
    4640             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4641        1381 :         nCurStrideForRowMajorStrides *= count[i];
    4642        1381 :         nElts *= count[i];
    4643        1381 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4644             :     }
    4645         788 :     if (bRowMajorStrides)
    4646         602 :         return false;
    4647         186 :     return nLastIdx == nElts - 1;
    4648             : }
    4649             : 
    4650             : /************************************************************************/
    4651             : /*                   CopyToFinalBufferSameDataType()                    */
    4652             : /************************************************************************/
    4653             : 
    4654             : template <size_t N>
    4655          60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4656             :                                    size_t nDims, const size_t *count,
    4657             :                                    const GPtrDiff_t *bufferStride)
    4658             : {
    4659         120 :     std::vector<size_t> anStackCount(nDims);
    4660         120 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4661          60 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4662             : #if defined(__GNUC__)
    4663             : #pragma GCC diagnostic push
    4664             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4665             : #endif
    4666          60 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4667             : #if defined(__GNUC__)
    4668             : #pragma GCC diagnostic pop
    4669             : #endif
    4670          60 :     size_t iDim = 0;
    4671             : 
    4672         749 : lbl_next_depth:
    4673         749 :     if (iDim == nDims - 1)
    4674             :     {
    4675         661 :         size_t n = count[iDim];
    4676         661 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4677         661 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4678       29186 :         while (n > 0)
    4679             :         {
    4680       28525 :             --n;
    4681       28525 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4682       28525 :             pabyDstBuffer += bufferStrideLastDim;
    4683       28525 :             pabySrcBuffer += N;
    4684             :         }
    4685             :     }
    4686             :     else
    4687             :     {
    4688          88 :         anStackCount[iDim] = count[iDim];
    4689             :         while (true)
    4690             :         {
    4691         689 :             ++iDim;
    4692         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4693         689 :             goto lbl_next_depth;
    4694         689 :         lbl_return_to_caller_in_loop:
    4695         689 :             --iDim;
    4696         689 :             --anStackCount[iDim];
    4697         689 :             if (anStackCount[iDim] == 0)
    4698          88 :                 break;
    4699         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4700             :         }
    4701             :     }
    4702         749 :     if (iDim > 0)
    4703         689 :         goto lbl_return_to_caller_in_loop;
    4704          60 : }
    4705             : 
    4706             : /************************************************************************/
    4707             : /*                        CopyToFinalBuffer()                           */
    4708             : /************************************************************************/
    4709             : 
    4710         166 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4711             :                               const GDALExtendedDataType &eSrcDataType,
    4712             :                               void *pDstBuffer,
    4713             :                               const GDALExtendedDataType &eDstDataType,
    4714             :                               size_t nDims, const size_t *count,
    4715             :                               const GPtrDiff_t *bufferStride)
    4716             : {
    4717         166 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4718             :     // Use specialized implementation for well-known data types when no
    4719             :     // type conversion is needed
    4720         166 :     if (eSrcDataType == eDstDataType)
    4721             :     {
    4722         110 :         if (nSrcDataTypeSize == 1)
    4723             :         {
    4724          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4725             :                                              count, bufferStride);
    4726          60 :             return;
    4727             :         }
    4728          69 :         else if (nSrcDataTypeSize == 2)
    4729             :         {
    4730           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4731             :                                              count, bufferStride);
    4732           1 :             return;
    4733             :         }
    4734          68 :         else if (nSrcDataTypeSize == 4)
    4735             :         {
    4736          14 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4737             :                                              count, bufferStride);
    4738          14 :             return;
    4739             :         }
    4740          54 :         else if (nSrcDataTypeSize == 8)
    4741             :         {
    4742           4 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4743             :                                              count, bufferStride);
    4744           4 :             return;
    4745             :         }
    4746             :     }
    4747             : 
    4748         106 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4749         212 :     std::vector<size_t> anStackCount(nDims);
    4750         212 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4751         106 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4752         106 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4753         106 :     size_t iDim = 0;
    4754             : 
    4755         375 : lbl_next_depth:
    4756         375 :     if (iDim == nDims - 1)
    4757             :     {
    4758         365 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4759         365 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4760         365 :                                          bufferStride[iDim], count[iDim]);
    4761         365 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4762             :     }
    4763             :     else
    4764             :     {
    4765          10 :         anStackCount[iDim] = count[iDim];
    4766             :         while (true)
    4767             :         {
    4768         269 :             ++iDim;
    4769         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4770         269 :             goto lbl_next_depth;
    4771         269 :         lbl_return_to_caller_in_loop:
    4772         269 :             --iDim;
    4773         269 :             --anStackCount[iDim];
    4774         269 :             if (anStackCount[iDim] == 0)
    4775          10 :                 break;
    4776         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4777             :         }
    4778             :     }
    4779         375 :     if (iDim > 0)
    4780         269 :         goto lbl_return_to_caller_in_loop;
    4781             : }
    4782             : 
    4783             : /************************************************************************/
    4784             : /*                      TransposeLast2Dims()                            */
    4785             : /************************************************************************/
    4786             : 
    4787          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4788             :                                const GDALExtendedDataType &eDT,
    4789             :                                const size_t nDims, const size_t *count,
    4790             :                                const size_t nEltsNonLast2Dims)
    4791             : {
    4792          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4793          19 :     const auto nDTSize = eDT.GetSize();
    4794             :     void *pTempBufferForLast2DimsTranspose =
    4795          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4796          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4797           0 :         return false;
    4798             : 
    4799          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4800          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4801             :     {
    4802          39 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
    4803             :                         pTempBufferForLast2DimsTranspose,
    4804          39 :                         eDT.GetNumericDataType(), count[nDims - 1],
    4805          39 :                         count[nDims - 2]);
    4806          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4807             :                nDTSize * nEltsLast2Dims);
    4808          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4809             :     }
    4810             : 
    4811          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4812             : 
    4813          19 :     return true;
    4814             : }
    4815             : 
    4816             : /************************************************************************/
    4817             : /*                      ReadForTransposedRequest()                      */
    4818             : /************************************************************************/
    4819             : 
    4820             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4821             : // transposed view yield to extremely poor/unusable performance. This fixes
    4822             : // this by using temporary memory to read in a contiguous buffer in a
    4823             : // row-major order, and then do the transposition to the final buffer.
    4824             : 
    4825         185 : bool GDALMDArray::ReadForTransposedRequest(
    4826             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4827             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4828             :     void *pDstBuffer) const
    4829             : {
    4830         185 :     const size_t nDims(GetDimensionCount());
    4831         185 :     if (nDims == 0)
    4832             :     {
    4833           0 :         CPLAssert(false);
    4834             :         return false;  // shouldn't happen
    4835             :     }
    4836         185 :     size_t nElts = 1;
    4837         492 :     for (size_t i = 0; i < nDims; ++i)
    4838         307 :         nElts *= count[i];
    4839             : 
    4840         370 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4841         185 :     tmpBufferStrides.back() = 1;
    4842         307 :     for (size_t i = nDims - 1; i > 0;)
    4843             :     {
    4844         122 :         --i;
    4845         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4846             :     }
    4847             : 
    4848         185 :     const auto &eDT = GetDataType();
    4849         185 :     const auto nDTSize = eDT.GetSize();
    4850         314 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4851         330 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4852          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4853             :     {
    4854             :         // Optimization of the optimization if only the last 2 dims are
    4855             :         // transposed that saves on temporary buffer allocation
    4856          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4857          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4858          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4859          23 :         size_t nEltsNonLast2Dims = 1;
    4860          40 :         for (size_t i = nDims - 2; i > 0;)
    4861             :         {
    4862          17 :             --i;
    4863          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4864             :                 nCurStrideForRowMajorStrides)
    4865             :             {
    4866           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4867             :             }
    4868             :             // Integer overflows have already been checked in
    4869             :             // CheckReadWriteParams()
    4870          17 :             nCurStrideForRowMajorStrides *= count[i];
    4871          17 :             nEltsNonLast2Dims *= count[i];
    4872             :         }
    4873          23 :         if (bRowMajorStridesForNonLast2Dims)
    4874             :         {
    4875             :             // We read in the final buffer!
    4876          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4877          19 :                        eDT, pDstBuffer))
    4878             :             {
    4879           0 :                 return false;
    4880             :             }
    4881             : 
    4882          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4883          19 :                                       nEltsNonLast2Dims);
    4884             :         }
    4885             :     }
    4886             : 
    4887         166 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4888         166 :     if (pTempBuffer == nullptr)
    4889           0 :         return false;
    4890             : 
    4891         166 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4892         166 :                pTempBuffer))
    4893             :     {
    4894           0 :         VSIFree(pTempBuffer);
    4895           0 :         return false;
    4896             :     }
    4897         166 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4898             :                       count, bufferStride);
    4899             : 
    4900         166 :     if (eDT.NeedsFreeDynamicMemory())
    4901             :     {
    4902          95 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4903         190 :         for (size_t i = 0; i < nElts; ++i)
    4904             :         {
    4905          95 :             eDT.FreeDynamicMemory(pabyPtr);
    4906          95 :             pabyPtr += nDTSize;
    4907             :         }
    4908             :     }
    4909             : 
    4910         166 :     VSIFree(pTempBuffer);
    4911         166 :     return true;
    4912             : }
    4913             : 
    4914             : /************************************************************************/
    4915             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4916             : /************************************************************************/
    4917             : 
    4918             : // Returns true if at all following conditions are met:
    4919             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4920             : // defines a row-major ordered contiguous buffer.
    4921          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4922             :     const size_t *count, const GInt64 *arrayStep,
    4923             :     const GPtrDiff_t *bufferStride,
    4924             :     const GDALExtendedDataType &bufferDataType) const
    4925             : {
    4926          78 :     if (bufferDataType != GetDataType())
    4927           5 :         return false;
    4928          73 :     size_t nExpectedStride = 1;
    4929         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4930             :     {
    4931          96 :         --i;
    4932          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4933          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4934             :         {
    4935           3 :             return false;
    4936             :         }
    4937          93 :         nExpectedStride *= count[i];
    4938             :     }
    4939          70 :     return true;
    4940             : }
    4941             : 
    4942             : /************************************************************************/
    4943             : /*                      ReadUsingContiguousIRead()                      */
    4944             : /************************************************************************/
    4945             : 
    4946             : // Used for example by the TileDB driver when requesting it with
    4947             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4948             : // not defining a row-major ordered contiguous buffer.
    4949             : // Should only be called when at least one of the above conditions are true,
    4950             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4951             : // returning none.
    4952             : // This method will call IRead() again with arrayStep[] == 1,
    4953             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4954             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4955             : // content of that temporary buffer onto pDstBuffer.
    4956           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4957             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4958             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4959             :     void *pDstBuffer) const
    4960             : {
    4961           7 :     const size_t nDims(GetDimensionCount());
    4962          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4963          14 :     std::vector<size_t> anTmpCount(nDims);
    4964           7 :     const auto &oType = GetDataType();
    4965           7 :     size_t nMemArraySize = oType.GetSize();
    4966          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4967           7 :     GPtrDiff_t nStride = 1;
    4968          18 :     for (size_t i = nDims; i > 0;)
    4969             :     {
    4970          11 :         --i;
    4971          11 :         if (arrayStep[i] > 0)
    4972           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4973             :         else
    4974           2 :             anTmpStartIdx[i] =
    4975           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4976             :         const uint64_t nCount =
    4977          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4978          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    4979             :         {
    4980           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4981             :                      "Read() failed due to too large memory requirement");
    4982           0 :             return false;
    4983             :         }
    4984          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    4985          11 :         nMemArraySize *= anTmpCount[i];
    4986          11 :         anTmpStride[i] = nStride;
    4987          11 :         nStride *= anTmpCount[i];
    4988             :     }
    4989             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    4990          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    4991           7 :     if (!pTmpBuffer)
    4992           0 :         return false;
    4993           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    4994          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    4995           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    4996             :     {
    4997           0 :         return false;
    4998             :     }
    4999          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    5000          18 :     for (size_t i = 0; i < nDims; ++i)
    5001             :     {
    5002          11 :         if (arrayStep[i] > 0)
    5003           9 :             anTmpStartIdx[i] = 0;
    5004             :         else
    5005           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    5006          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    5007          22 :             std::string(), std::string(), std::string(), std::string(),
    5008          22 :             anTmpCount[i]);
    5009             :     }
    5010             :     auto poMEMArray =
    5011          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    5012          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    5013           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    5014           7 :                             bufferStride, bufferDataType, pDstBuffer);
    5015             : }
    5016             : 
    5017             : //! @endcond
    5018             : 
    5019             : /************************************************************************/
    5020             : /*                       GDALSlicedMDArray                              */
    5021             : /************************************************************************/
    5022             : 
    5023             : class GDALSlicedMDArray final : public GDALPamMDArray
    5024             : {
    5025             :   private:
    5026             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5027             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5028             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    5029             :     std::vector<Range>
    5030             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    5031             : 
    5032             :     mutable std::vector<GUInt64> m_parentStart;
    5033             :     mutable std::vector<size_t> m_parentCount;
    5034             :     mutable std::vector<GInt64> m_parentStep;
    5035             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5036             : 
    5037             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5038             :                              const GInt64 *arrayStep,
    5039             :                              const GPtrDiff_t *bufferStride) const;
    5040             : 
    5041             :   protected:
    5042         582 :     explicit GDALSlicedMDArray(
    5043             :         const std::shared_ptr<GDALMDArray> &poParent,
    5044             :         const std::string &viewExpr,
    5045             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5046             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5047             :         std::vector<Range> &&parentRanges)
    5048        1746 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    5049        1746 :                                                  poParent->GetFullName() +
    5050        1164 :                                                  " (" + viewExpr + ")"),
    5051        1164 :           GDALPamMDArray(std::string(),
    5052        1164 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    5053        1164 :                              viewExpr + ")",
    5054        1164 :                          GDALPamMultiDim::GetPAM(poParent),
    5055             :                          poParent->GetContext()),
    5056        1164 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    5057         582 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    5058         582 :           m_parentRanges(std::move(parentRanges)),
    5059         582 :           m_parentStart(m_poParent->GetDimensionCount()),
    5060         582 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    5061         582 :           m_parentStep(m_poParent->GetDimensionCount()),
    5062        4656 :           m_parentStride(m_poParent->GetDimensionCount())
    5063             :     {
    5064         582 :     }
    5065             : 
    5066             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5067             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5068             :                const GDALExtendedDataType &bufferDataType,
    5069             :                void *pDstBuffer) const override;
    5070             : 
    5071             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5072             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5073             :                 const GDALExtendedDataType &bufferDataType,
    5074             :                 const void *pSrcBuffer) override;
    5075             : 
    5076             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5077             :                      CSLConstList papszOptions) const override;
    5078             : 
    5079             :   public:
    5080             :     static std::shared_ptr<GDALSlicedMDArray>
    5081         582 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5082             :            const std::string &viewExpr,
    5083             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5084             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5085             :            std::vector<Range> &&parentRanges)
    5086             :     {
    5087         582 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    5088         582 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    5089             : 
    5090             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    5091         582 :             poParent, viewExpr, std::move(dims),
    5092         582 :             std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
    5093         582 :         newAr->SetSelf(newAr);
    5094         582 :         return newAr;
    5095             :     }
    5096             : 
    5097          56 :     bool IsWritable() const override
    5098             :     {
    5099          56 :         return m_poParent->IsWritable();
    5100             :     }
    5101             : 
    5102         990 :     const std::string &GetFilename() const override
    5103             :     {
    5104         990 :         return m_poParent->GetFilename();
    5105             :     }
    5106             : 
    5107             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5108        3687 :     GetDimensions() const override
    5109             :     {
    5110        3687 :         return m_dims;
    5111             :     }
    5112             : 
    5113        1402 :     const GDALExtendedDataType &GetDataType() const override
    5114             :     {
    5115        1402 :         return m_poParent->GetDataType();
    5116             :     }
    5117             : 
    5118           2 :     const std::string &GetUnit() const override
    5119             :     {
    5120           2 :         return m_poParent->GetUnit();
    5121             :     }
    5122             : 
    5123             :     // bool SetUnit(const std::string& osUnit) override  { return
    5124             :     // m_poParent->SetUnit(osUnit); }
    5125             : 
    5126           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5127             :     {
    5128           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5129           2 :         if (!poSrcSRS)
    5130           1 :             return nullptr;
    5131           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5132           2 :         std::vector<int> dstMapping;
    5133           3 :         for (int srcAxis : srcMapping)
    5134             :         {
    5135           2 :             bool bFound = false;
    5136           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5137             :             {
    5138           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5139           3 :                     srcAxis - 1)
    5140             :                 {
    5141           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5142           2 :                     bFound = true;
    5143           2 :                     break;
    5144             :                 }
    5145             :             }
    5146           2 :             if (!bFound)
    5147             :             {
    5148           0 :                 dstMapping.push_back(0);
    5149             :             }
    5150             :         }
    5151           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5152           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5153           1 :         return poClone;
    5154             :     }
    5155             : 
    5156          59 :     const void *GetRawNoDataValue() const override
    5157             :     {
    5158          59 :         return m_poParent->GetRawNoDataValue();
    5159             :     }
    5160             : 
    5161             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5162             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5163             : 
    5164           2 :     double GetOffset(bool *pbHasOffset,
    5165             :                      GDALDataType *peStorageType) const override
    5166             :     {
    5167           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5168             :     }
    5169             : 
    5170           2 :     double GetScale(bool *pbHasScale,
    5171             :                     GDALDataType *peStorageType) const override
    5172             :     {
    5173           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5174             :     }
    5175             : 
    5176             :     // bool SetOffset(double dfOffset) override { return
    5177             :     // m_poParent->SetOffset(dfOffset); }
    5178             : 
    5179             :     // bool SetScale(double dfScale) override { return
    5180             :     // m_poParent->SetScale(dfScale); }
    5181             : 
    5182         198 :     std::vector<GUInt64> GetBlockSize() const override
    5183             :     {
    5184         198 :         std::vector<GUInt64> ret(GetDimensionCount());
    5185         396 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5186         598 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5187             :         {
    5188         400 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5189         400 :             if (iOldAxis != static_cast<size_t>(-1))
    5190             :             {
    5191         400 :                 ret[i] = parentBlockSize[iOldAxis];
    5192             :             }
    5193             :         }
    5194         396 :         return ret;
    5195             :     }
    5196             : 
    5197             :     std::shared_ptr<GDALAttribute>
    5198           6 :     GetAttribute(const std::string &osName) const override
    5199             :     {
    5200           6 :         return m_poParent->GetAttribute(osName);
    5201             :     }
    5202             : 
    5203             :     std::vector<std::shared_ptr<GDALAttribute>>
    5204          24 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5205             :     {
    5206          24 :         return m_poParent->GetAttributes(papszOptions);
    5207             :     }
    5208             : };
    5209             : 
    5210             : /************************************************************************/
    5211             : /*                        PrepareParentArrays()                         */
    5212             : /************************************************************************/
    5213             : 
    5214         480 : void GDALSlicedMDArray::PrepareParentArrays(
    5215             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5216             :     const GPtrDiff_t *bufferStride) const
    5217             : {
    5218         480 :     const size_t nParentDimCount = m_parentRanges.size();
    5219        1496 :     for (size_t i = 0; i < nParentDimCount; i++)
    5220             :     {
    5221             :         // For dimensions in parent that have no existence in sliced array
    5222        1016 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5223             :     }
    5224             : 
    5225        1265 :     for (size_t i = 0; i < m_dims.size(); i++)
    5226             :     {
    5227         785 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5228         785 :         if (iParent != static_cast<size_t>(-1))
    5229             :         {
    5230         783 :             m_parentStart[iParent] =
    5231         783 :                 m_parentRanges[iParent].m_nIncr >= 0
    5232         783 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5233         749 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5234          34 :                     : m_parentRanges[iParent].m_nStartIdx -
    5235          68 :                           arrayStartIdx[i] *
    5236          34 :                               static_cast<GUInt64>(
    5237          34 :                                   -m_parentRanges[iParent].m_nIncr);
    5238         783 :             m_parentCount[iParent] = count[i];
    5239         783 :             if (arrayStep)
    5240             :             {
    5241         782 :                 m_parentStep[iParent] =
    5242         782 :                     count[i] == 1 ? 1 :
    5243             :                                   // other checks should have ensured this does
    5244             :                         // not overflow
    5245         592 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5246             :             }
    5247         783 :             if (bufferStride)
    5248             :             {
    5249         782 :                 m_parentStride[iParent] = bufferStride[i];
    5250             :             }
    5251             :         }
    5252             :     }
    5253         480 : }
    5254             : 
    5255             : /************************************************************************/
    5256             : /*                             IRead()                                  */
    5257             : /************************************************************************/
    5258             : 
    5259         447 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5260             :                               const GInt64 *arrayStep,
    5261             :                               const GPtrDiff_t *bufferStride,
    5262             :                               const GDALExtendedDataType &bufferDataType,
    5263             :                               void *pDstBuffer) const
    5264             : {
    5265         447 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5266         894 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5267         447 :                             m_parentStep.data(), m_parentStride.data(),
    5268         447 :                             bufferDataType, pDstBuffer);
    5269             : }
    5270             : 
    5271             : /************************************************************************/
    5272             : /*                             IWrite()                                  */
    5273             : /************************************************************************/
    5274             : 
    5275          32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5276             :                                const size_t *count, const GInt64 *arrayStep,
    5277             :                                const GPtrDiff_t *bufferStride,
    5278             :                                const GDALExtendedDataType &bufferDataType,
    5279             :                                const void *pSrcBuffer)
    5280             : {
    5281          32 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5282          64 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5283          32 :                              m_parentStep.data(), m_parentStride.data(),
    5284          32 :                              bufferDataType, pSrcBuffer);
    5285             : }
    5286             : 
    5287             : /************************************************************************/
    5288             : /*                             IAdviseRead()                            */
    5289             : /************************************************************************/
    5290             : 
    5291           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5292             :                                     const size_t *count,
    5293             :                                     CSLConstList papszOptions) const
    5294             : {
    5295           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5296           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5297           1 :                                   papszOptions);
    5298             : }
    5299             : 
    5300             : /************************************************************************/
    5301             : /*                        CreateSlicedArray()                           */
    5302             : /************************************************************************/
    5303             : 
    5304             : static std::shared_ptr<GDALMDArray>
    5305         600 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5306             :                   const std::string &viewExpr, const std::string &activeSlice,
    5307             :                   bool bRenameDimensions,
    5308             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5309             : {
    5310         600 :     const auto &srcDims(self->GetDimensions());
    5311         600 :     if (srcDims.empty())
    5312             :     {
    5313           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5314           2 :         return nullptr;
    5315             :     }
    5316             : 
    5317        1196 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5318         598 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5319             : 
    5320        1196 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5321        1196 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5322        1196 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5323         598 :     newDims.reserve(nTokens);
    5324         598 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5325         598 :     parentRanges.reserve(nTokens);
    5326             : 
    5327         598 :     bool bGotEllipsis = false;
    5328         598 :     size_t nCurSrcDim = 0;
    5329        1768 :     for (size_t i = 0; i < nTokens; i++)
    5330             :     {
    5331        1186 :         const char *pszIdxSpec = aosTokens[i];
    5332        1186 :         if (EQUAL(pszIdxSpec, "..."))
    5333             :         {
    5334          38 :             if (bGotEllipsis)
    5335             :             {
    5336           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5337             :                          "Only one single ellipsis is supported");
    5338           2 :                 return nullptr;
    5339             :             }
    5340          36 :             bGotEllipsis = true;
    5341          36 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5342          79 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5343             :             {
    5344          43 :                 parentRanges.emplace_back(0, 1);
    5345          43 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5346          43 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5347             :             }
    5348          36 :             continue;
    5349             :         }
    5350        1148 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5351        1145 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5352             :         {
    5353           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5354           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5355           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5356           3 :             continue;
    5357             :         }
    5358        1145 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5359             :         {
    5360         325 :             if (nCurSrcDim >= srcDims.size())
    5361             :             {
    5362           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5363             :                          activeSlice.c_str());
    5364           7 :                 return nullptr;
    5365             :             }
    5366             : 
    5367         323 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5368         323 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5369         323 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5370         319 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5371             :             {
    5372           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5373             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5374           5 :                 return nullptr;
    5375             :             }
    5376         318 :             if (nVal < 0)
    5377           0 :                 nVal += nDimSize;
    5378         318 :             parentRanges.emplace_back(nVal, 0);
    5379             :         }
    5380             :         else
    5381             :         {
    5382         820 :             if (nCurSrcDim >= srcDims.size())
    5383             :             {
    5384           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5385             :                          activeSlice.c_str());
    5386           7 :                 return nullptr;
    5387             :             }
    5388             : 
    5389             :             CPLStringList aosRangeTokens(
    5390         819 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5391         819 :             int nRangeTokens = aosRangeTokens.size();
    5392         819 :             if (nRangeTokens > 3)
    5393             :             {
    5394           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5395             :                          pszIdxSpec);
    5396           1 :                 return nullptr;
    5397             :             }
    5398         818 :             if (nRangeTokens <= 1)
    5399             :             {
    5400           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5401             :                          pszIdxSpec);
    5402           1 :                 return nullptr;
    5403             :             }
    5404         817 :             const char *pszStart = aosRangeTokens[0];
    5405         817 :             const char *pszEnd = aosRangeTokens[1];
    5406         817 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5407         817 :             GDALSlicedMDArray::Range range;
    5408         817 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5409         817 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5410        1633 :             if (range.m_nIncr == 0 ||
    5411         816 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5412             :             {
    5413           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5414           1 :                 return nullptr;
    5415             :             }
    5416         816 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5417         816 :             if (startIdx < 0)
    5418             :             {
    5419           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5420           0 :                     startIdx = 0;
    5421             :                 else
    5422           0 :                     startIdx = nDimSize + startIdx;
    5423             :             }
    5424         816 :             const bool bPosIncr = range.m_nIncr > 0;
    5425         816 :             range.m_nStartIdx = startIdx;
    5426        1632 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5427         816 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5428             :                                     : range.m_nStartIdx;
    5429         816 :             if (range.m_nStartIdx >= nDimSize - 1)
    5430         186 :                 range.m_nStartIdx = nDimSize - 1;
    5431         816 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5432         816 :             if (endIdx < 0)
    5433             :             {
    5434           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5435           1 :                 if (nDimSize < positiveEndIdx)
    5436           0 :                     endIdx = 0;
    5437             :                 else
    5438           1 :                     endIdx = nDimSize - positiveEndIdx;
    5439             :             }
    5440         816 :             GUInt64 nEndIdx = endIdx;
    5441         816 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5442         816 :             if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5443         814 :                 (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5444             :             {
    5445           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5446             :                          "Output dimension of size 0 is not allowed");
    5447           3 :                 return nullptr;
    5448             :             }
    5449         813 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5450         813 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5451         813 :             const GUInt64 newSize =
    5452             :                 bPosIncr
    5453         847 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5454          34 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5455        1341 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5456         528 :                 newSize == srcDims[nCurSrcDim]->GetSize())
    5457             :             {
    5458         159 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5459             :             }
    5460             :             else
    5461             :             {
    5462         654 :                 std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
    5463         654 :                 if (bRenameDimensions)
    5464             :                 {
    5465             :                     osNewDimName =
    5466        1212 :                         "subset_" + srcDims[nCurSrcDim]->GetName() +
    5467             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5468             :                                    "_" CPL_FRMT_GUIB,
    5469         606 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5470         606 :                                    static_cast<GIntBig>(range.m_nIncr),
    5471         606 :                                    static_cast<GUIntBig>(newSize));
    5472             :                 }
    5473         654 :                 newDims.push_back(std::make_shared<GDALDimension>(
    5474        1308 :                     std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
    5475        1308 :                     range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
    5476             :                                       : std::string(),
    5477             :                     newSize));
    5478             :             }
    5479         813 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5480         813 :             parentRanges.emplace_back(range);
    5481             :         }
    5482             : 
    5483        1131 :         nCurSrcDim++;
    5484             :     }
    5485         655 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5486             :     {
    5487          73 :         parentRanges.emplace_back(0, 1);
    5488          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5489          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5490             :     }
    5491             : 
    5492         582 :     GDALMDArray::ViewSpec viewSpec;
    5493         582 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5494         582 :     viewSpec.m_parentRanges = parentRanges;
    5495         582 :     viewSpecs.emplace_back(std::move(viewSpec));
    5496             : 
    5497        1164 :     return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
    5498         582 :                                      std::move(mapDimIdxToParentDimIdx),
    5499        1164 :                                      std::move(parentRanges));
    5500             : }
    5501             : 
    5502             : /************************************************************************/
    5503             : /*                       GDALExtractFieldMDArray                        */
    5504             : /************************************************************************/
    5505             : 
    5506             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5507             : {
    5508             :   private:
    5509             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5510             :     GDALExtendedDataType m_dt;
    5511             :     std::string m_srcCompName;
    5512             :     mutable std::vector<GByte> m_pabyNoData{};
    5513             : 
    5514             :   protected:
    5515          82 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5516             :                             const std::string &fieldName,
    5517             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5518         328 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5519         164 :                                                  " of " +
    5520          82 :                                                  poParent->GetFullName()),
    5521             :           GDALPamMDArray(
    5522         164 :               std::string(),
    5523         164 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5524         164 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5525             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5526         410 :           m_srcCompName(srcComp->GetName())
    5527             :     {
    5528          82 :         m_pabyNoData.resize(m_dt.GetSize());
    5529          82 :     }
    5530             : 
    5531             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5532             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5533             :                const GDALExtendedDataType &bufferDataType,
    5534             :                void *pDstBuffer) const override;
    5535             : 
    5536           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5537             :                      CSLConstList papszOptions) const override
    5538             :     {
    5539           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5540             :     }
    5541             : 
    5542             :   public:
    5543             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5544          82 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5545             :            const std::string &fieldName,
    5546             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5547             :     {
    5548             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5549          82 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5550          82 :         newAr->SetSelf(newAr);
    5551          82 :         return newAr;
    5552             :     }
    5553             : 
    5554         164 :     ~GDALExtractFieldMDArray()
    5555          82 :     {
    5556          82 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5557         164 :     }
    5558             : 
    5559          41 :     bool IsWritable() const override
    5560             :     {
    5561          41 :         return m_poParent->IsWritable();
    5562             :     }
    5563             : 
    5564         247 :     const std::string &GetFilename() const override
    5565             :     {
    5566         247 :         return m_poParent->GetFilename();
    5567             :     }
    5568             : 
    5569             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5570         351 :     GetDimensions() const override
    5571             :     {
    5572         351 :         return m_poParent->GetDimensions();
    5573             :     }
    5574             : 
    5575         295 :     const GDALExtendedDataType &GetDataType() const override
    5576             :     {
    5577         295 :         return m_dt;
    5578             :     }
    5579             : 
    5580           2 :     const std::string &GetUnit() const override
    5581             :     {
    5582           2 :         return m_poParent->GetUnit();
    5583             :     }
    5584             : 
    5585           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5586             :     {
    5587           2 :         return m_poParent->GetSpatialRef();
    5588             :     }
    5589             : 
    5590          60 :     const void *GetRawNoDataValue() const override
    5591             :     {
    5592          60 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5593          60 :         if (parentNoData == nullptr)
    5594           1 :             return nullptr;
    5595             : 
    5596          59 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5597          59 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5598             : 
    5599         118 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5600         118 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5601         118 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5602          59 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5603         177 :                                                 std::move(comps)));
    5604             : 
    5605          59 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5606          59 :                                         &m_pabyNoData[0], tmpDT);
    5607             : 
    5608          59 :         return &m_pabyNoData[0];
    5609             :     }
    5610             : 
    5611           2 :     double GetOffset(bool *pbHasOffset,
    5612             :                      GDALDataType *peStorageType) const override
    5613             :     {
    5614           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5615             :     }
    5616             : 
    5617           2 :     double GetScale(bool *pbHasScale,
    5618             :                     GDALDataType *peStorageType) const override
    5619             :     {
    5620           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5621             :     }
    5622             : 
    5623          42 :     std::vector<GUInt64> GetBlockSize() const override
    5624             :     {
    5625          42 :         return m_poParent->GetBlockSize();
    5626             :     }
    5627             : };
    5628             : 
    5629             : /************************************************************************/
    5630             : /*                             IRead()                                  */
    5631             : /************************************************************************/
    5632             : 
    5633          88 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5634             :                                     const size_t *count,
    5635             :                                     const GInt64 *arrayStep,
    5636             :                                     const GPtrDiff_t *bufferStride,
    5637             :                                     const GDALExtendedDataType &bufferDataType,
    5638             :                                     void *pDstBuffer) const
    5639             : {
    5640         176 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5641         176 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5642         176 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5643             :     auto tmpDT(GDALExtendedDataType::Create(
    5644         176 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5645             : 
    5646          88 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5647         176 :                             tmpDT, pDstBuffer);
    5648             : }
    5649             : 
    5650             : /************************************************************************/
    5651             : /*                      CreateFieldNameExtractArray()                   */
    5652             : /************************************************************************/
    5653             : 
    5654             : static std::shared_ptr<GDALMDArray>
    5655          83 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5656             :                             const std::string &fieldName)
    5657             : {
    5658          83 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5659          83 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5660         202 :     for (const auto &comp : self->GetDataType().GetComponents())
    5661             :     {
    5662         201 :         if (comp->GetName() == fieldName)
    5663             :         {
    5664          82 :             srcComp = &comp;
    5665          82 :             break;
    5666             :         }
    5667             :     }
    5668          83 :     if (srcComp == nullptr)
    5669             :     {
    5670           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5671             :                  fieldName.c_str());
    5672           1 :         return nullptr;
    5673             :     }
    5674          82 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5675             : }
    5676             : 
    5677             : /************************************************************************/
    5678             : /*                             GetView()                                */
    5679             : /************************************************************************/
    5680             : 
    5681             : // clang-format off
    5682             : /** Return a view of the array using slicing or field access.
    5683             :  *
    5684             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5685             :  * indexing. See
    5686             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5687             :  * Or it can use field access by name. See
    5688             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5689             :  *
    5690             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5691             :  * or field name inside each.
    5692             :  *
    5693             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5694             :  * indexes that apply to successive source dimensions, can be specified, using
    5695             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5696             :  * or newaxis, using a comma separator.
    5697             :  *
    5698             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5699             :  * <ul>
    5700             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5701             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5702             :  *     from the source array. That is 5</li>
    5703             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5704             :  * implemented internally doing this intermediate slicing approach.</li>
    5705             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5706             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5707             :  *     first dimension. That is [4,5,6,7].</li>
    5708             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5709             :  *     second dimension. That is [2,6].</li>
    5710             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5711             :  * the second dimension. That is [[2],[6]].</li>
    5712             :  * <li>GetView("[::,2]"): Same as
    5713             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5714             :  * ellipsis only expands to one dimension here.</li>
    5715             :  * <li>GetView("[:,::2]"):
    5716             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5717             :  * dimension. That is [[0,2],[4,6]].</li>
    5718             :  * <li>GetView("[:,1::2]"): returns a
    5719             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5720             :  * is [[1,3],[5,7]].</li>
    5721             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5722             :  * array, with elements of the second dimension with index in the range [1,3[.
    5723             :  * That is [[1,2],[5,6]].</li>
    5724             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5725             :  * array, with the values in first dimension reversed. That is
    5726             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5727             :  * <li>GetView("[newaxis,...]"): returns a
    5728             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5729             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5730             :  * </ul>
    5731             :  *
    5732             :  * One difference with NumPy behavior is that ranges that would result in
    5733             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5734             :  * GDAL multidimensional model).
    5735             :  *
    5736             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5737             :  * Multiple field specification is not supported currently.
    5738             :  *
    5739             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5740             :  *
    5741             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5742             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5743             :  * ar.GetView("[0,::,1]['foo']")
    5744             :  * \note When using the C++ API and integer indexing only, you may use the
    5745             :  * at(idx0, idx1, ...) method.
    5746             :  *
    5747             :  * The returned array holds a reference to the original one, and thus is
    5748             :  * a view of it (not a copy). If the content of the original array changes,
    5749             :  * the content of the view array too. When using basic slicing and indexing,
    5750             :  * the view can be written if the underlying array is writable.
    5751             :  *
    5752             :  * This is the same as the C function GDALMDArrayGetView()
    5753             :  *
    5754             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5755             :  * access.
    5756             :  * @return a new array, that holds a reference to the original one, and thus is
    5757             :  * a view of it (not a copy), or nullptr in case of error.
    5758             :  */
    5759             : // clang-format on
    5760             : 
    5761             : std::shared_ptr<GDALMDArray>
    5762         619 : GDALMDArray::GetView(const std::string &viewExpr) const
    5763             : {
    5764        1238 :     std::vector<ViewSpec> viewSpecs;
    5765        1238 :     return GetView(viewExpr, true, viewSpecs);
    5766             : }
    5767             : 
    5768             : //! @cond Doxygen_Suppress
    5769             : std::shared_ptr<GDALMDArray>
    5770         689 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5771             :                      std::vector<ViewSpec> &viewSpecs) const
    5772             : {
    5773        1378 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5774         689 :     if (!self)
    5775             :     {
    5776           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5777             :                  "Driver implementation issue: m_pSelf not set !");
    5778           1 :         return nullptr;
    5779             :     }
    5780         688 :     std::string curExpr(viewExpr);
    5781             :     while (true)
    5782             :     {
    5783         691 :         if (curExpr.empty() || curExpr[0] != '[')
    5784             :         {
    5785           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5786             :                      "Slice string should start with ['");
    5787         688 :             return nullptr;
    5788             :         }
    5789             : 
    5790         689 :         std::string fieldName;
    5791             :         size_t endExpr;
    5792         689 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5793             :         {
    5794          87 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5795             :             {
    5796           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5797             :                          "Field access not allowed on non-compound data type");
    5798           2 :                 return nullptr;
    5799             :             }
    5800          85 :             size_t idx = 2;
    5801         768 :             for (; idx < curExpr.size(); idx++)
    5802             :             {
    5803         767 :                 const char ch = curExpr[idx];
    5804         767 :                 if (ch == curExpr[1])
    5805          84 :                     break;
    5806         683 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5807             :                 {
    5808           1 :                     fieldName += curExpr[idx + 1];
    5809           1 :                     idx++;
    5810             :                 }
    5811             :                 else
    5812             :                 {
    5813         682 :                     fieldName += ch;
    5814             :                 }
    5815             :             }
    5816          85 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5817             :             {
    5818           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5819             :                          "Invalid field access specification");
    5820           2 :                 return nullptr;
    5821             :             }
    5822          83 :             endExpr = idx + 1;
    5823             :         }
    5824             :         else
    5825             :         {
    5826         602 :             endExpr = curExpr.find(']');
    5827             :         }
    5828         685 :         if (endExpr == std::string::npos)
    5829             :         {
    5830           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5831           1 :             return nullptr;
    5832             :         }
    5833         684 :         if (endExpr == 1)
    5834             :         {
    5835           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5836           1 :             return nullptr;
    5837             :         }
    5838         683 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5839             : 
    5840         683 :         if (!fieldName.empty())
    5841             :         {
    5842         166 :             ViewSpec viewSpec;
    5843          83 :             viewSpec.m_osFieldName = fieldName;
    5844          83 :             viewSpecs.emplace_back(std::move(viewSpec));
    5845             :         }
    5846             : 
    5847         683 :         auto newArray = !fieldName.empty()
    5848             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5849             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5850         683 :                                                 bRenameDimensions, viewSpecs);
    5851             : 
    5852         683 :         if (endExpr == curExpr.size() - 1)
    5853             :         {
    5854         680 :             return newArray;
    5855             :         }
    5856           3 :         self = std::move(newArray);
    5857           3 :         curExpr = curExpr.substr(endExpr + 1);
    5858           3 :     }
    5859             : }
    5860             : 
    5861             : //! @endcond
    5862             : 
    5863             : std::shared_ptr<GDALMDArray>
    5864          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5865             : {
    5866          19 :     std::string osExpr("[");
    5867          19 :     bool bFirst = true;
    5868          45 :     for (const auto &idx : indices)
    5869             :     {
    5870          26 :         if (!bFirst)
    5871           7 :             osExpr += ',';
    5872          26 :         bFirst = false;
    5873          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5874             :     }
    5875          57 :     return GetView(osExpr + ']');
    5876             : }
    5877             : 
    5878             : /************************************************************************/
    5879             : /*                            operator[]                                */
    5880             : /************************************************************************/
    5881             : 
    5882             : /** Return a view of the array using field access
    5883             :  *
    5884             :  * Equivalent of GetView("['fieldName']")
    5885             :  *
    5886             :  * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
    5887             :  */
    5888             : std::shared_ptr<GDALMDArray>
    5889           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5890             : {
    5891           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5892           4 :                                             .replaceAll('\\', "\\\\")
    5893           4 :                                             .replaceAll('\'', "\\\'")
    5894           6 :                                             .c_str()));
    5895             : }
    5896             : 
    5897             : /************************************************************************/
    5898             : /*                      GDALMDArrayTransposed                           */
    5899             : /************************************************************************/
    5900             : 
    5901             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5902             : {
    5903             :   private:
    5904             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5905             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5906             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5907             : 
    5908             :     mutable std::vector<GUInt64> m_parentStart;
    5909             :     mutable std::vector<size_t> m_parentCount;
    5910             :     mutable std::vector<GInt64> m_parentStep;
    5911             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5912             : 
    5913             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5914             :                              const GInt64 *arrayStep,
    5915             :                              const GPtrDiff_t *bufferStride) const;
    5916             : 
    5917             :     static std::string
    5918          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5919             :     {
    5920          84 :         std::string ret;
    5921          84 :         ret += '[';
    5922         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5923             :         {
    5924         228 :             if (i > 0)
    5925         144 :                 ret += ',';
    5926         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5927             :         }
    5928          84 :         ret += ']';
    5929          84 :         return ret;
    5930             :     }
    5931             : 
    5932             :   protected:
    5933          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5934             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5935             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5936          84 :         : GDALAbstractMDArray(std::string(),
    5937          84 :                               "Transposed view of " + poParent->GetFullName() +
    5938          84 :                                   " along " +
    5939          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5940          84 :           GDALPamMDArray(std::string(),
    5941          84 :                          "Transposed view of " + poParent->GetFullName() +
    5942         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    5943          84 :                          GDALPamMultiDim::GetPAM(poParent),
    5944             :                          poParent->GetContext()),
    5945          42 :           m_poParent(std::move(poParent)),
    5946             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    5947          42 :           m_dims(std::move(dims)),
    5948          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    5949          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    5950          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    5951         336 :           m_parentStride(m_poParent->GetDimensionCount())
    5952             :     {
    5953          42 :     }
    5954             : 
    5955             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5956             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5957             :                const GDALExtendedDataType &bufferDataType,
    5958             :                void *pDstBuffer) const override;
    5959             : 
    5960             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5961             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5962             :                 const GDALExtendedDataType &bufferDataType,
    5963             :                 const void *pSrcBuffer) override;
    5964             : 
    5965             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5966             :                      CSLConstList papszOptions) const override;
    5967             : 
    5968             :   public:
    5969             :     static std::shared_ptr<GDALMDArrayTransposed>
    5970          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5971             :            const std::vector<int> &anMapNewAxisToOldAxis)
    5972             :     {
    5973          42 :         const auto &parentDims(poParent->GetDimensions());
    5974          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    5975         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    5976             :         {
    5977         114 :             if (iOldAxis < 0)
    5978             :             {
    5979           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    5980           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    5981             :             }
    5982             :             else
    5983             :             {
    5984         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    5985             :             }
    5986             :         }
    5987             : 
    5988             :         auto newAr(
    5989             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    5990          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    5991          42 :         newAr->SetSelf(newAr);
    5992          84 :         return newAr;
    5993             :     }
    5994             : 
    5995           1 :     bool IsWritable() const override
    5996             :     {
    5997           1 :         return m_poParent->IsWritable();
    5998             :     }
    5999             : 
    6000          84 :     const std::string &GetFilename() const override
    6001             :     {
    6002          84 :         return m_poParent->GetFilename();
    6003             :     }
    6004             : 
    6005             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6006         358 :     GetDimensions() const override
    6007             :     {
    6008         358 :         return m_dims;
    6009             :     }
    6010             : 
    6011         141 :     const GDALExtendedDataType &GetDataType() const override
    6012             :     {
    6013         141 :         return m_poParent->GetDataType();
    6014             :     }
    6015             : 
    6016           4 :     const std::string &GetUnit() const override
    6017             :     {
    6018           4 :         return m_poParent->GetUnit();
    6019             :     }
    6020             : 
    6021           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6022             :     {
    6023          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    6024           5 :         if (!poSrcSRS)
    6025           2 :             return nullptr;
    6026           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    6027           6 :         std::vector<int> dstMapping;
    6028           9 :         for (int srcAxis : srcMapping)
    6029             :         {
    6030           6 :             bool bFound = false;
    6031          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    6032             :             {
    6033          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    6034             :                 {
    6035           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    6036           6 :                     bFound = true;
    6037           6 :                     break;
    6038             :                 }
    6039             :             }
    6040           6 :             if (!bFound)
    6041             :             {
    6042           0 :                 dstMapping.push_back(0);
    6043             :             }
    6044             :         }
    6045           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    6046           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    6047           3 :         return poClone;
    6048             :     }
    6049             : 
    6050           4 :     const void *GetRawNoDataValue() const override
    6051             :     {
    6052           4 :         return m_poParent->GetRawNoDataValue();
    6053             :     }
    6054             : 
    6055             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    6056             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    6057             : 
    6058           4 :     double GetOffset(bool *pbHasOffset,
    6059             :                      GDALDataType *peStorageType) const override
    6060             :     {
    6061           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    6062             :     }
    6063             : 
    6064           4 :     double GetScale(bool *pbHasScale,
    6065             :                     GDALDataType *peStorageType) const override
    6066             :     {
    6067           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    6068             :     }
    6069             : 
    6070             :     // bool SetOffset(double dfOffset) override { return
    6071             :     // m_poParent->SetOffset(dfOffset); }
    6072             : 
    6073             :     // bool SetScale(double dfScale) override { return
    6074             :     // m_poParent->SetScale(dfScale); }
    6075             : 
    6076           3 :     std::vector<GUInt64> GetBlockSize() const override
    6077             :     {
    6078           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    6079           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    6080          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6081             :         {
    6082           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6083           8 :             if (iOldAxis >= 0)
    6084             :             {
    6085           7 :                 ret[i] = parentBlockSize[iOldAxis];
    6086             :             }
    6087             :         }
    6088           6 :         return ret;
    6089             :     }
    6090             : 
    6091             :     std::shared_ptr<GDALAttribute>
    6092           1 :     GetAttribute(const std::string &osName) const override
    6093             :     {
    6094           1 :         return m_poParent->GetAttribute(osName);
    6095             :     }
    6096             : 
    6097             :     std::vector<std::shared_ptr<GDALAttribute>>
    6098           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6099             :     {
    6100           6 :         return m_poParent->GetAttributes(papszOptions);
    6101             :     }
    6102             : };
    6103             : 
    6104             : /************************************************************************/
    6105             : /*                         PrepareParentArrays()                        */
    6106             : /************************************************************************/
    6107             : 
    6108          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6109             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6110             :     const GPtrDiff_t *bufferStride) const
    6111             : {
    6112         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6113             :     {
    6114         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6115         129 :         if (iOldAxis >= 0)
    6116             :         {
    6117         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6118         128 :             m_parentCount[iOldAxis] = count[i];
    6119         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6120             :             {
    6121         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6122             :             }
    6123         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6124             :             {
    6125         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6126             :             }
    6127             :         }
    6128             :     }
    6129          47 : }
    6130             : 
    6131             : /************************************************************************/
    6132             : /*                             IRead()                                  */
    6133             : /************************************************************************/
    6134             : 
    6135          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6136             :                                   const size_t *count, const GInt64 *arrayStep,
    6137             :                                   const GPtrDiff_t *bufferStride,
    6138             :                                   const GDALExtendedDataType &bufferDataType,
    6139             :                                   void *pDstBuffer) const
    6140             : {
    6141          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6142          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6143          44 :                             m_parentStep.data(), m_parentStride.data(),
    6144          44 :                             bufferDataType, pDstBuffer);
    6145             : }
    6146             : 
    6147             : /************************************************************************/
    6148             : /*                            IWrite()                                  */
    6149             : /************************************************************************/
    6150             : 
    6151           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6152             :                                    const size_t *count, const GInt64 *arrayStep,
    6153             :                                    const GPtrDiff_t *bufferStride,
    6154             :                                    const GDALExtendedDataType &bufferDataType,
    6155             :                                    const void *pSrcBuffer)
    6156             : {
    6157           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6158           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6159           2 :                              m_parentStep.data(), m_parentStride.data(),
    6160           2 :                              bufferDataType, pSrcBuffer);
    6161             : }
    6162             : 
    6163             : /************************************************************************/
    6164             : /*                             IAdviseRead()                            */
    6165             : /************************************************************************/
    6166             : 
    6167           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6168             :                                         const size_t *count,
    6169             :                                         CSLConstList papszOptions) const
    6170             : {
    6171           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6172           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6173           1 :                                   papszOptions);
    6174             : }
    6175             : 
    6176             : /************************************************************************/
    6177             : /*                           Transpose()                                */
    6178             : /************************************************************************/
    6179             : 
    6180             : /** Return a view of the array whose axis have been reordered.
    6181             :  *
    6182             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6183             :  * and GetDimensionCount() - 1, and each only once.
    6184             :  * -1 can be used as a special index value to ask for the insertion of a new
    6185             :  * axis of size 1.
    6186             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6187             :  * index of one of its dimension, it corresponds to the axis of index
    6188             :  * anMapNewAxisToOldAxis[i] from the current array.
    6189             :  *
    6190             :  * This is similar to the numpy.transpose() method
    6191             :  *
    6192             :  * The returned array holds a reference to the original one, and thus is
    6193             :  * a view of it (not a copy). If the content of the original array changes,
    6194             :  * the content of the view array too. The view can be written if the underlying
    6195             :  * array is writable.
    6196             :  *
    6197             :  * Note that I/O performance in such a transposed view might be poor.
    6198             :  *
    6199             :  * This is the same as the C function GDALMDArrayTranspose().
    6200             :  *
    6201             :  * @return a new array, that holds a reference to the original one, and thus is
    6202             :  * a view of it (not a copy), or nullptr in case of error.
    6203             :  */
    6204             : std::shared_ptr<GDALMDArray>
    6205          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6206             : {
    6207         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6208          50 :     if (!self)
    6209             :     {
    6210           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6211             :                  "Driver implementation issue: m_pSelf not set !");
    6212           0 :         return nullptr;
    6213             :     }
    6214          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6215         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6216          50 :     int nCountOldAxis = 0;
    6217         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6218             :     {
    6219         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6220             :         {
    6221           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6222           4 :             return nullptr;
    6223             :         }
    6224         130 :         if (iOldAxis >= 0)
    6225             :         {
    6226         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6227             :             {
    6228           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6229             :                          iOldAxis);
    6230           1 :                 return nullptr;
    6231             :             }
    6232         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6233         127 :             nCountOldAxis++;
    6234             :         }
    6235             :     }
    6236          46 :     if (nCountOldAxis != nDims)
    6237             :     {
    6238           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6239             :                  "One or several original axis missing");
    6240           4 :         return nullptr;
    6241             :     }
    6242          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6243             : }
    6244             : 
    6245             : /************************************************************************/
    6246             : /*                             IRead()                                  */
    6247             : /************************************************************************/
    6248             : 
    6249          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6250             :                                 const size_t *count, const GInt64 *arrayStep,
    6251             :                                 const GPtrDiff_t *bufferStride,
    6252             :                                 const GDALExtendedDataType &bufferDataType,
    6253             :                                 void *pDstBuffer) const
    6254             : {
    6255          16 :     const double dfScale = m_dfScale;
    6256          16 :     const double dfOffset = m_dfOffset;
    6257          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6258             :     const auto dtDouble =
    6259          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6260          16 :     const size_t nDTSize = dtDouble.GetSize();
    6261          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6262             : 
    6263          16 :     double adfSrcNoData[2] = {0, 0};
    6264          16 :     if (m_bHasNoData)
    6265             :     {
    6266           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6267           9 :                                         m_poParent->GetDataType(),
    6268             :                                         &adfSrcNoData[0], dtDouble);
    6269             :     }
    6270             : 
    6271          16 :     const auto nDims = GetDimensions().size();
    6272          16 :     if (nDims == 0)
    6273             :     {
    6274             :         double adfVal[2];
    6275           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6276             :                               dtDouble, &adfVal[0]))
    6277             :         {
    6278           0 :             return false;
    6279             :         }
    6280           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6281             :         {
    6282           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6283           6 :             if (bDTIsComplex)
    6284             :             {
    6285           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6286             :             }
    6287           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6288             :                                             bufferDataType);
    6289             :         }
    6290             :         else
    6291             :         {
    6292           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6293             :                                             pDstBuffer, bufferDataType);
    6294             :         }
    6295           9 :         return true;
    6296             :     }
    6297             : 
    6298          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6299           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6300           7 :     void *pTempBuffer = pDstBuffer;
    6301           7 :     if (bTempBufferNeeded)
    6302             :     {
    6303           2 :         size_t nElts = 1;
    6304           2 :         actualBufferStrideVector.resize(nDims);
    6305           7 :         for (size_t i = 0; i < nDims; i++)
    6306           5 :             nElts *= count[i];
    6307           2 :         actualBufferStrideVector.back() = 1;
    6308           5 :         for (size_t i = nDims - 1; i > 0;)
    6309             :         {
    6310           3 :             --i;
    6311           3 :             actualBufferStrideVector[i] =
    6312           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6313             :         }
    6314           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6315           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6316           2 :         if (!pTempBuffer)
    6317           0 :             return false;
    6318             :     }
    6319           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6320             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6321             :     {
    6322           0 :         if (bTempBufferNeeded)
    6323           0 :             VSIFree(pTempBuffer);
    6324           0 :         return false;
    6325             :     }
    6326             : 
    6327             :     struct Stack
    6328             :     {
    6329             :         size_t nIters = 0;
    6330             :         double *src_ptr = nullptr;
    6331             :         GByte *dst_ptr = nullptr;
    6332             :         GPtrDiff_t src_inc_offset = 0;
    6333             :         GPtrDiff_t dst_inc_offset = 0;
    6334             :     };
    6335             : 
    6336           7 :     std::vector<Stack> stack(nDims);
    6337           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6338          23 :     for (size_t i = 0; i < nDims; i++)
    6339             :     {
    6340          32 :         stack[i].src_inc_offset =
    6341          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6342          16 :         stack[i].dst_inc_offset =
    6343          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6344             :     }
    6345           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6346           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6347             : 
    6348           7 :     size_t dimIdx = 0;
    6349           7 :     const size_t nDimsMinus1 = nDims - 1;
    6350             :     GByte abyDstNoData[16];
    6351           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6352           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6353             :                                     bufferDataType);
    6354             : 
    6355          37 : lbl_next_depth:
    6356          37 :     if (dimIdx == nDimsMinus1)
    6357             :     {
    6358          25 :         auto nIters = count[dimIdx];
    6359          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6360          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6361             :         while (true)
    6362             :         {
    6363          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6364             :             {
    6365          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6366          88 :                 if (bDTIsComplex)
    6367             :                 {
    6368           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6369             :                 }
    6370          88 :                 if (bTempBufferNeeded)
    6371             :                 {
    6372          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6373             :                                                     dst_ptr, bufferDataType);
    6374             :                 }
    6375             :             }
    6376             :             else
    6377             :             {
    6378           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6379             :             }
    6380             : 
    6381          92 :             if ((--nIters) == 0)
    6382          25 :                 break;
    6383          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6384          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6385             :         }
    6386             :     }
    6387             :     else
    6388             :     {
    6389          12 :         stack[dimIdx].nIters = count[dimIdx];
    6390             :         while (true)
    6391             :         {
    6392          30 :             dimIdx++;
    6393          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6394          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6395          30 :             goto lbl_next_depth;
    6396          30 :         lbl_return_to_caller:
    6397          30 :             dimIdx--;
    6398          30 :             if ((--stack[dimIdx].nIters) == 0)
    6399          12 :                 break;
    6400          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6401          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6402             :         }
    6403             :     }
    6404          37 :     if (dimIdx > 0)
    6405          30 :         goto lbl_return_to_caller;
    6406             : 
    6407           7 :     if (bTempBufferNeeded)
    6408           2 :         VSIFree(pTempBuffer);
    6409           7 :     return true;
    6410             : }
    6411             : 
    6412             : /************************************************************************/
    6413             : /*                             IWrite()                                 */
    6414             : /************************************************************************/
    6415             : 
    6416          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6417             :                                  const size_t *count, const GInt64 *arrayStep,
    6418             :                                  const GPtrDiff_t *bufferStride,
    6419             :                                  const GDALExtendedDataType &bufferDataType,
    6420             :                                  const void *pSrcBuffer)
    6421             : {
    6422          16 :     const double dfScale = m_dfScale;
    6423          16 :     const double dfOffset = m_dfOffset;
    6424          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6425             :     const auto dtDouble =
    6426          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6427          16 :     const size_t nDTSize = dtDouble.GetSize();
    6428          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6429             :     const bool bSelfAndParentHaveNoData =
    6430          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6431          16 :     double dfNoData = 0;
    6432          16 :     if (m_bHasNoData)
    6433             :     {
    6434           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6435             :                         &dfNoData, GDT_Float64, 0, 1);
    6436             :     }
    6437             : 
    6438          16 :     double adfSrcNoData[2] = {0, 0};
    6439          16 :     if (bSelfAndParentHaveNoData)
    6440             :     {
    6441           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6442           7 :                                         m_poParent->GetDataType(),
    6443             :                                         &adfSrcNoData[0], dtDouble);
    6444             :     }
    6445             : 
    6446          16 :     const auto nDims = GetDimensions().size();
    6447          16 :     if (nDims == 0)
    6448             :     {
    6449             :         double adfVal[2];
    6450          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6451             :                                         dtDouble);
    6452          16 :         if (bSelfAndParentHaveNoData &&
    6453           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6454             :         {
    6455           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6456           2 :                                      bufferStride, m_poParent->GetDataType(),
    6457           4 :                                      m_poParent->GetRawNoDataValue());
    6458             :         }
    6459             :         else
    6460             :         {
    6461           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6462           8 :             if (bDTIsComplex)
    6463             :             {
    6464           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6465             :             }
    6466           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6467           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6468             :         }
    6469             :     }
    6470             : 
    6471          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6472           6 :     size_t nElts = 1;
    6473           6 :     tmpBufferStrideVector.resize(nDims);
    6474          20 :     for (size_t i = 0; i < nDims; i++)
    6475          14 :         nElts *= count[i];
    6476           6 :     tmpBufferStrideVector.back() = 1;
    6477          14 :     for (size_t i = nDims - 1; i > 0;)
    6478             :     {
    6479           8 :         --i;
    6480           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6481             :     }
    6482           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6483           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6484           6 :     if (!pTempBuffer)
    6485           0 :         return false;
    6486             : 
    6487             :     struct Stack
    6488             :     {
    6489             :         size_t nIters = 0;
    6490             :         double *dst_ptr = nullptr;
    6491             :         const GByte *src_ptr = nullptr;
    6492             :         GPtrDiff_t src_inc_offset = 0;
    6493             :         GPtrDiff_t dst_inc_offset = 0;
    6494             :     };
    6495             : 
    6496           6 :     std::vector<Stack> stack(nDims);
    6497           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6498          20 :     for (size_t i = 0; i < nDims; i++)
    6499             :     {
    6500          28 :         stack[i].dst_inc_offset =
    6501          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6502          14 :         stack[i].src_inc_offset =
    6503          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6504             :     }
    6505           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6506           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6507             : 
    6508           6 :     size_t dimIdx = 0;
    6509           6 :     const size_t nDimsMinus1 = nDims - 1;
    6510             : 
    6511          34 : lbl_next_depth:
    6512          34 :     if (dimIdx == nDimsMinus1)
    6513             :     {
    6514          23 :         auto nIters = count[dimIdx];
    6515          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6516          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6517             :         while (true)
    6518             :         {
    6519             :             double adfVal[2];
    6520             :             const double *padfSrcVal;
    6521          86 :             if (bIsBufferDataTypeNativeDataType)
    6522             :             {
    6523          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6524             :             }
    6525             :             else
    6526             :             {
    6527          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6528             :                                                 &adfVal[0], dtDouble);
    6529          36 :                 padfSrcVal = adfVal;
    6530             :             }
    6531             : 
    6532         148 :             if (bSelfAndParentHaveNoData &&
    6533          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6534             :             {
    6535           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6536           3 :                 if (bDTIsComplex)
    6537             :                 {
    6538           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6539             :                 }
    6540             :             }
    6541             :             else
    6542             :             {
    6543          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6544          83 :                 if (bDTIsComplex)
    6545             :                 {
    6546           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6547             :                 }
    6548             :             }
    6549             : 
    6550          86 :             if ((--nIters) == 0)
    6551          23 :                 break;
    6552          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6553          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6554          63 :         }
    6555             :     }
    6556             :     else
    6557             :     {
    6558          11 :         stack[dimIdx].nIters = count[dimIdx];
    6559             :         while (true)
    6560             :         {
    6561          28 :             dimIdx++;
    6562          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6563          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6564          28 :             goto lbl_next_depth;
    6565          28 :         lbl_return_to_caller:
    6566          28 :             dimIdx--;
    6567          28 :             if ((--stack[dimIdx].nIters) == 0)
    6568          11 :                 break;
    6569          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6570          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6571             :         }
    6572             :     }
    6573          34 :     if (dimIdx > 0)
    6574          28 :         goto lbl_return_to_caller;
    6575             : 
    6576             :     // If the parent array is not double/complex-double, then convert the
    6577             :     // values to it, before calling Write(), as some implementations can be
    6578             :     // very slow when doing the type conversion.
    6579           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6580           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6581           6 :     if (nParentDTSize <= nDTSize / 2)
    6582             :     {
    6583             :         // Copy in-place by making sure that source and target do not overlap
    6584           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6585           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6586             : 
    6587             :         // Copy first element
    6588             :         {
    6589           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6590           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6591           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6592             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6593             :                             1);
    6594           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6595             :         }
    6596             :         // Remaining elements
    6597          86 :         for (size_t i = 1; i < nElts; ++i)
    6598             :         {
    6599          80 :             GDALCopyWords64(
    6600          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
    6601          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6602             :                 eParentNumericDT, 0, 1);
    6603             :         }
    6604             :     }
    6605             : 
    6606             :     const bool ret =
    6607           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6608             :                           eParentDT, pTempBuffer);
    6609             : 
    6610           6 :     VSIFree(pTempBuffer);
    6611           6 :     return ret;
    6612             : }
    6613             : 
    6614             : /************************************************************************/
    6615             : /*                           GetUnscaled()                              */
    6616             : /************************************************************************/
    6617             : 
    6618             : /** Return an array that is the unscaled version of the current one.
    6619             :  *
    6620             :  * That is each value of the unscaled array will be
    6621             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6622             :  *
    6623             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6624             :  * from unscaled values to raw values.
    6625             :  *
    6626             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6627             :  *
    6628             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6629             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6630             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6631             :  * @return a new array, that holds a reference to the original one, and thus is
    6632             :  * a view of it (not a copy), or nullptr in case of error.
    6633             :  */
    6634             : std::shared_ptr<GDALMDArray>
    6635          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6636             :                          double dfOverriddenDstNodata) const
    6637             : {
    6638          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6639          17 :     if (!self)
    6640             :     {
    6641           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6642             :                  "Driver implementation issue: m_pSelf not set !");
    6643           0 :         return nullptr;
    6644             :     }
    6645          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6646             :     {
    6647           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6648             :                  "GetUnscaled() only supports numeric data type");
    6649           0 :         return nullptr;
    6650             :     }
    6651             :     const double dfScale =
    6652          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6653             :     const double dfOffset =
    6654          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6655          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6656           4 :         return self;
    6657             : 
    6658          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6659          13 :                            ? GDT_CFloat64
    6660          13 :                            : GDT_Float64;
    6661          13 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
    6662             :     {
    6663           1 :         if (GetDataType().GetNumericDataType() == GDT_Float16)
    6664           0 :             eDT = GDT_Float16;
    6665           1 :         if (GetDataType().GetNumericDataType() == GDT_Float32)
    6666           1 :             eDT = GDT_Float32;
    6667             :     }
    6668             : 
    6669          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6670          13 :                                        dfOverriddenDstNodata, eDT);
    6671             : }
    6672             : 
    6673             : /************************************************************************/
    6674             : /*                         GDALMDArrayMask                              */
    6675             : /************************************************************************/
    6676             : 
    6677             : class GDALMDArrayMask final : public GDALPamMDArray
    6678             : {
    6679             :   private:
    6680             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6681             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
    6682             :     double m_dfMissingValue = 0.0;
    6683             :     bool m_bHasMissingValue = false;
    6684             :     double m_dfFillValue = 0.0;
    6685             :     bool m_bHasFillValue = false;
    6686             :     double m_dfValidMin = 0.0;
    6687             :     bool m_bHasValidMin = false;
    6688             :     double m_dfValidMax = 0.0;
    6689             :     bool m_bHasValidMax = false;
    6690             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6691             :     std::vector<uint32_t> m_anValidFlagValues{};
    6692             : 
    6693             :     bool Init(CSLConstList papszOptions);
    6694             : 
    6695             :     template <typename Type>
    6696             :     void
    6697             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6698             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6699             :                  const void *pTempBuffer,
    6700             :                  const GDALExtendedDataType &oTmpBufferDT,
    6701             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6702             : 
    6703             :   protected:
    6704          48 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6705          96 :         : GDALAbstractMDArray(std::string(),
    6706          96 :                               "Mask of " + poParent->GetFullName()),
    6707          96 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6708          96 :                          GDALPamMultiDim::GetPAM(poParent),
    6709             :                          poParent->GetContext()),
    6710         240 :           m_poParent(std::move(poParent))
    6711             :     {
    6712          48 :     }
    6713             : 
    6714             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6715             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6716             :                const GDALExtendedDataType &bufferDataType,
    6717             :                void *pDstBuffer) const override;
    6718             : 
    6719           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6720             :                      CSLConstList papszOptions) const override
    6721             :     {
    6722           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6723             :     }
    6724             : 
    6725             :   public:
    6726             :     static std::shared_ptr<GDALMDArrayMask>
    6727             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6728             :            CSLConstList papszOptions);
    6729             : 
    6730           1 :     bool IsWritable() const override
    6731             :     {
    6732           1 :         return false;
    6733             :     }
    6734             : 
    6735          54 :     const std::string &GetFilename() const override
    6736             :     {
    6737          54 :         return m_poParent->GetFilename();
    6738             :     }
    6739             : 
    6740             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6741         382 :     GetDimensions() const override
    6742             :     {
    6743         382 :         return m_poParent->GetDimensions();
    6744             :     }
    6745             : 
    6746         138 :     const GDALExtendedDataType &GetDataType() const override
    6747             :     {
    6748         138 :         return m_dt;
    6749             :     }
    6750             : 
    6751           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6752             :     {
    6753           1 :         return m_poParent->GetSpatialRef();
    6754             :     }
    6755             : 
    6756           2 :     std::vector<GUInt64> GetBlockSize() const override
    6757             :     {
    6758           2 :         return m_poParent->GetBlockSize();
    6759             :     }
    6760             : };
    6761             : 
    6762             : /************************************************************************/
    6763             : /*                    GDALMDArrayMask::Create()                         */
    6764             : /************************************************************************/
    6765             : 
    6766             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6767          48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6768             :                         CSLConstList papszOptions)
    6769             : {
    6770          96 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6771          48 :     newAr->SetSelf(newAr);
    6772          48 :     if (!newAr->Init(papszOptions))
    6773           6 :         return nullptr;
    6774          42 :     return newAr;
    6775             : }
    6776             : 
    6777             : /************************************************************************/
    6778             : /*                    GDALMDArrayMask::Init()                           */
    6779             : /************************************************************************/
    6780             : 
    6781          48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6782             : {
    6783             :     const auto GetSingleValNumericAttr =
    6784         192 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6785             :     {
    6786         576 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6787         192 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6788             :         {
    6789          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6790          21 :             if (anDimSizes.empty() ||
    6791          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6792             :             {
    6793          11 :                 bHasVal = true;
    6794          11 :                 dfVal = poAttr->ReadAsDouble();
    6795             :             }
    6796             :         }
    6797         192 :     };
    6798             : 
    6799          48 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6800          48 :                             m_dfMissingValue);
    6801          48 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6802          48 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6803          48 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6804             : 
    6805             :     {
    6806         144 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6807          54 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6808          60 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6809           6 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6810             :         {
    6811           6 :             m_bHasValidMin = true;
    6812           6 :             m_bHasValidMax = true;
    6813           6 :             auto vals = poValidRange->ReadAsDoubleArray();
    6814           6 :             CPLAssert(vals.size() == 2);
    6815           6 :             m_dfValidMin = vals[0];
    6816           6 :             m_dfValidMax = vals[1];
    6817             :         }
    6818             :     }
    6819             : 
    6820             :     // Take into account
    6821             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6822             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6823             :     const char *pszUnmaskFlags =
    6824          48 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6825          48 :     if (pszUnmaskFlags)
    6826             :     {
    6827             :         const auto IsScalarStringAttr =
    6828          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6829             :         {
    6830          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6831          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6832          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6833          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6834             :         };
    6835             : 
    6836          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6837          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6838             :         {
    6839           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6840             :                      "UNMASK_FLAGS option specified but array has no "
    6841             :                      "flag_meanings attribute");
    6842           1 :             return false;
    6843             :         }
    6844          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6845          13 :         if (!pszFlagMeanings)
    6846             :         {
    6847           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6848             :                      "Cannot read flag_meanings attribute");
    6849           1 :             return false;
    6850             :         }
    6851             : 
    6852             :         const auto IsSingleDimNumericAttr =
    6853          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6854             :         {
    6855          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6856          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6857             :         };
    6858             : 
    6859          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6860             :         const bool bHasFlagValues =
    6861          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6862             : 
    6863          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6864             :         const bool bHasFlagMasks =
    6865          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6866             : 
    6867          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6868             :         {
    6869           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6870             :                      "Cannot find flag_values and/or flag_masks attribute");
    6871           1 :             return false;
    6872             :         }
    6873             : 
    6874             :         const CPLStringList aosUnmaskFlags(
    6875          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6876             :         const CPLStringList aosFlagMeanings(
    6877          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6878             : 
    6879          11 :         if (bHasFlagValues)
    6880             :         {
    6881           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6882             :             // We could support Int64 or UInt64, but more work...
    6883           7 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6884           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6885             :             {
    6886           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6887             :                          "Unsupported data type for flag_values attribute: %s",
    6888             :                          GDALGetDataTypeName(eType));
    6889           0 :                 return false;
    6890             :             }
    6891             :         }
    6892             : 
    6893          11 :         if (bHasFlagMasks)
    6894             :         {
    6895           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6896             :             // We could support Int64 or UInt64, but more work...
    6897           6 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6898           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6899             :             {
    6900           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6901             :                          "Unsupported data type for flag_masks attribute: %s",
    6902             :                          GDALGetDataTypeName(eType));
    6903           0 :                 return false;
    6904             :             }
    6905             :         }
    6906             : 
    6907             :         const std::vector<double> adfValues(
    6908             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6909          11 :                            : std::vector<double>());
    6910             :         const std::vector<double> adfMasks(
    6911             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6912          11 :                           : std::vector<double>());
    6913             : 
    6914          18 :         if (bHasFlagValues &&
    6915           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6916             :         {
    6917           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6918             :                      "Number of values in flag_values attribute is different "
    6919             :                      "from the one in flag_meanings");
    6920           1 :             return false;
    6921             :         }
    6922             : 
    6923          16 :         if (bHasFlagMasks &&
    6924           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6925             :         {
    6926           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6927             :                      "Number of values in flag_masks attribute is different "
    6928             :                      "from the one in flag_meanings");
    6929           1 :             return false;
    6930             :         }
    6931             : 
    6932          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6933             :         {
    6934          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6935          11 :             if (nIdxFlag < 0)
    6936             :             {
    6937           1 :                 CPLError(
    6938             :                     CE_Failure, CPLE_AppDefined,
    6939             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6940             :                     aosUnmaskFlags[i], pszFlagMeanings);
    6941           1 :                 return false;
    6942             :             }
    6943             : 
    6944          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    6945             :             {
    6946           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6947             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    6948           0 :                          adfValues[nIdxFlag]);
    6949           0 :                 return false;
    6950             :             }
    6951             : 
    6952          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    6953             :             {
    6954           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6955             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    6956           0 :                          adfMasks[nIdxFlag]);
    6957           0 :                 return false;
    6958             :             }
    6959             : 
    6960          10 :             if (bHasFlagValues)
    6961             :             {
    6962          12 :                 m_anValidFlagValues.push_back(
    6963           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    6964             :             }
    6965             : 
    6966          10 :             if (bHasFlagMasks)
    6967             :             {
    6968          12 :                 m_anValidFlagMasks.push_back(
    6969           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    6970             :             }
    6971             :         }
    6972             :     }
    6973             : 
    6974          42 :     return true;
    6975             : }
    6976             : 
    6977             : /************************************************************************/
    6978             : /*                             IRead()                                  */
    6979             : /************************************************************************/
    6980             : 
    6981          51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6982             :                             const GInt64 *arrayStep,
    6983             :                             const GPtrDiff_t *bufferStride,
    6984             :                             const GDALExtendedDataType &bufferDataType,
    6985             :                             void *pDstBuffer) const
    6986             : {
    6987          51 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    6988             :     {
    6989           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6990             :                  "%s: only reading to a numeric data type is supported",
    6991             :                  __func__);
    6992           0 :         return false;
    6993             :     }
    6994          51 :     size_t nElts = 1;
    6995          51 :     const size_t nDims = GetDimensionCount();
    6996         102 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    6997         139 :     for (size_t i = 0; i < nDims; i++)
    6998          88 :         nElts *= count[i];
    6999          51 :     if (nDims > 0)
    7000             :     {
    7001          46 :         tmpBufferStrideVector.back() = 1;
    7002          88 :         for (size_t i = nDims - 1; i > 0;)
    7003             :         {
    7004          42 :             --i;
    7005          42 :             tmpBufferStrideVector[i] =
    7006          42 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    7007             :         }
    7008             :     }
    7009             : 
    7010             :     /* Optimized case: if we are an integer data type and that there is no */
    7011             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    7012             :     /* directly */
    7013          49 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    7014          74 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    7015          34 :         m_anValidFlagMasks.empty() &&
    7016         111 :         m_poParent->GetRawNoDataValue() == nullptr &&
    7017          11 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    7018             :     {
    7019           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7020           7 :         if (bBufferDataTypeIsByte)  // Byte case
    7021             :         {
    7022           4 :             bool bContiguous = true;
    7023          10 :             for (size_t i = 0; i < nDims; i++)
    7024             :             {
    7025           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    7026             :                 {
    7027           1 :                     bContiguous = false;
    7028           1 :                     break;
    7029             :                 }
    7030             :             }
    7031           4 :             if (bContiguous)
    7032             :             {
    7033             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    7034           3 :                 memset(pDstBuffer, 1, nElts);
    7035           3 :                 return true;
    7036             :             }
    7037             :         }
    7038             : 
    7039             :         struct Stack
    7040             :         {
    7041             :             size_t nIters = 0;
    7042             :             GByte *dst_ptr = nullptr;
    7043             :             GPtrDiff_t dst_inc_offset = 0;
    7044             :         };
    7045             : 
    7046           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7047           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    7048          13 :         for (size_t i = 0; i < nDims; i++)
    7049             :         {
    7050           9 :             stack[i].dst_inc_offset =
    7051           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7052             :         }
    7053           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7054             : 
    7055           4 :         size_t dimIdx = 0;
    7056           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7057             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    7058           4 :         CPLAssert(nBufferDTSize <= 16);
    7059           4 :         const GByte flag = 1;
    7060           4 :         GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
    7061             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7062             : 
    7063          28 :     lbl_next_depth:
    7064          28 :         if (dimIdx == nDimsMinus1)
    7065             :         {
    7066          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7067          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7068             : 
    7069             :             while (true)
    7070             :             {
    7071             :                 // cppcheck-suppress knownConditionTrueFalse
    7072          73 :                 if (bBufferDataTypeIsByte)
    7073             :                 {
    7074          24 :                     *dst_ptr = flag;
    7075             :                 }
    7076             :                 else
    7077             :                 {
    7078          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    7079             :                 }
    7080             : 
    7081          73 :                 if ((--nIters) == 0)
    7082          19 :                     break;
    7083          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    7084             :             }
    7085             :         }
    7086             :         else
    7087             :         {
    7088           9 :             stack[dimIdx].nIters = count[dimIdx];
    7089             :             while (true)
    7090             :             {
    7091          24 :                 dimIdx++;
    7092          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7093          24 :                 goto lbl_next_depth;
    7094          24 :             lbl_return_to_caller:
    7095          24 :                 dimIdx--;
    7096          24 :                 if ((--stack[dimIdx].nIters) == 0)
    7097           9 :                     break;
    7098          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7099             :             }
    7100             :         }
    7101          28 :         if (dimIdx > 0)
    7102          24 :             goto lbl_return_to_caller;
    7103             : 
    7104           4 :         return true;
    7105             :     }
    7106             : 
    7107             :     const auto oTmpBufferDT =
    7108          44 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7109             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7110          88 :             : m_poParent->GetDataType();
    7111          44 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7112          44 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7113          44 :     if (!pTempBuffer)
    7114           0 :         return false;
    7115          88 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7116          44 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7117             :                           pTempBuffer))
    7118             :     {
    7119           0 :         VSIFree(pTempBuffer);
    7120           0 :         return false;
    7121             :     }
    7122             : 
    7123          44 :     switch (oTmpBufferDT.GetNumericDataType())
    7124             :     {
    7125           7 :         case GDT_Byte:
    7126           7 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7127             :                                 pTempBuffer, oTmpBufferDT,
    7128             :                                 tmpBufferStrideVector);
    7129           7 :             break;
    7130             : 
    7131           0 :         case GDT_Int8:
    7132           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7133             :                                 pTempBuffer, oTmpBufferDT,
    7134             :                                 tmpBufferStrideVector);
    7135           0 :             break;
    7136             : 
    7137           1 :         case GDT_UInt16:
    7138           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7139             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7140             :                                   tmpBufferStrideVector);
    7141           1 :             break;
    7142             : 
    7143          14 :         case GDT_Int16:
    7144          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7145             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7146             :                                  tmpBufferStrideVector);
    7147          14 :             break;
    7148             : 
    7149           1 :         case GDT_UInt32:
    7150           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7151             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7152             :                                   tmpBufferStrideVector);
    7153           1 :             break;
    7154             : 
    7155           5 :         case GDT_Int32:
    7156           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7157             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7158             :                                  tmpBufferStrideVector);
    7159           5 :             break;
    7160             : 
    7161           0 :         case GDT_UInt64:
    7162           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7163             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7164             :                                         tmpBufferStrideVector);
    7165           0 :             break;
    7166             : 
    7167           0 :         case GDT_Int64:
    7168           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7169             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7170             :                                        tmpBufferStrideVector);
    7171           0 :             break;
    7172             : 
    7173           0 :         case GDT_Float16:
    7174           0 :             ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
    7175             :                                    pDstBuffer, pTempBuffer, oTmpBufferDT,
    7176             :                                    tmpBufferStrideVector);
    7177           0 :             break;
    7178             : 
    7179           7 :         case GDT_Float32:
    7180           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7181             :                                 pTempBuffer, oTmpBufferDT,
    7182             :                                 tmpBufferStrideVector);
    7183           7 :             break;
    7184             : 
    7185           9 :         case GDT_Float64:
    7186           9 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7187             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7188             :                                  tmpBufferStrideVector);
    7189           9 :             break;
    7190           0 :         case GDT_Unknown:
    7191             :         case GDT_CInt16:
    7192             :         case GDT_CInt32:
    7193             :         case GDT_CFloat16:
    7194             :         case GDT_CFloat32:
    7195             :         case GDT_CFloat64:
    7196             :         case GDT_TypeCount:
    7197           0 :             CPLAssert(false);
    7198             :             break;
    7199             :     }
    7200             : 
    7201          44 :     VSIFree(pTempBuffer);
    7202             : 
    7203          44 :     return true;
    7204             : }
    7205             : 
    7206             : /************************************************************************/
    7207             : /*                          IsValidForDT()                              */
    7208             : /************************************************************************/
    7209             : 
    7210          40 : template <typename Type> static bool IsValidForDT(double dfVal)
    7211             : {
    7212          40 :     if (std::isnan(dfVal))
    7213           0 :         return false;
    7214          40 :     if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
    7215           0 :         return false;
    7216          40 :     if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
    7217           0 :         return false;
    7218          40 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7219             : }
    7220             : 
    7221           9 : template <> bool IsValidForDT<double>(double)
    7222             : {
    7223           9 :     return true;
    7224             : }
    7225             : 
    7226             : /************************************************************************/
    7227             : /*                              IsNan()                                 */
    7228             : /************************************************************************/
    7229             : 
    7230        1438 : template <typename Type> inline bool IsNan(Type)
    7231             : {
    7232        1438 :     return false;
    7233             : }
    7234             : 
    7235          65 : template <> bool IsNan<double>(double val)
    7236             : {
    7237          65 :     return std::isnan(val);
    7238             : }
    7239             : 
    7240          26 : template <> bool IsNan<float>(float val)
    7241             : {
    7242          26 :     return std::isnan(val);
    7243             : }
    7244             : 
    7245             : /************************************************************************/
    7246             : /*                         ReadInternal()                               */
    7247             : /************************************************************************/
    7248             : 
    7249             : template <typename Type>
    7250          44 : void GDALMDArrayMask::ReadInternal(
    7251             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7252             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7253             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7254             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7255             : {
    7256          44 :     const size_t nDims = GetDimensionCount();
    7257             : 
    7258         220 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7259             :     {
    7260         220 :         if (bHasVal)
    7261             :         {
    7262          49 :             if (IsValidForDT<Type>(dfVal))
    7263             :             {
    7264          49 :                 return static_cast<Type>(dfVal);
    7265             :             }
    7266             :             else
    7267             :             {
    7268           0 :                 bHasVal = false;
    7269             :             }
    7270             :         }
    7271         171 :         return 0;
    7272             :     };
    7273             : 
    7274          44 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7275          44 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7276             :     const Type nNoDataValue =
    7277          44 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7278          44 :     bool bHasMissingValue = m_bHasMissingValue;
    7279          44 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7280          44 :     bool bHasFillValue = m_bHasFillValue;
    7281          44 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7282          44 :     bool bHasValidMin = m_bHasValidMin;
    7283          44 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7284          44 :     bool bHasValidMax = m_bHasValidMax;
    7285          44 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7286          44 :     const bool bHasValidFlags =
    7287          44 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7288             : 
    7289         351 :     const auto IsValidFlag = [this](Type v)
    7290             :     {
    7291          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7292             :         {
    7293          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7294             :             {
    7295          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7296             :                     m_anValidFlagValues[i])
    7297             :                 {
    7298           4 :                     return true;
    7299             :                 }
    7300             :             }
    7301             :         }
    7302          42 :         else if (!m_anValidFlagValues.empty())
    7303             :         {
    7304          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7305             :             {
    7306          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7307             :                 {
    7308           4 :                     return true;
    7309             :                 }
    7310             :             }
    7311             :         }
    7312             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7313             :         {
    7314          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7315             :             {
    7316          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7317             :                 {
    7318           9 :                     return true;
    7319             :                 }
    7320             :             }
    7321             :         }
    7322          37 :         return false;
    7323             :     };
    7324             : 
    7325             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7326             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7327             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7328             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7329             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7330             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7331             :                        (!bHasValidFlags || IsValidFlag(v)));
    7332             : 
    7333          44 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7334             :     /* Optimized case: Byte output and output buffer is contiguous */
    7335          44 :     if (bBufferDataTypeIsByte)
    7336             :     {
    7337          40 :         bool bContiguous = true;
    7338         103 :         for (size_t i = 0; i < nDims; i++)
    7339             :         {
    7340          64 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7341             :             {
    7342           1 :                 bContiguous = false;
    7343           1 :                 break;
    7344             :             }
    7345             :         }
    7346          40 :         if (bContiguous)
    7347             :         {
    7348          39 :             size_t nElts = 1;
    7349         102 :             for (size_t i = 0; i < nDims; i++)
    7350          63 :                 nElts *= count[i];
    7351             : 
    7352        1113 :             for (size_t i = 0; i < nElts; i++)
    7353             :             {
    7354        1074 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7355        1074 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7356        1074 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7357             :             }
    7358          39 :             return;
    7359             :         }
    7360             :     }
    7361             : 
    7362           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7363             : 
    7364             :     struct Stack
    7365             :     {
    7366             :         size_t nIters = 0;
    7367             :         const GByte *src_ptr = nullptr;
    7368             :         GByte *dst_ptr = nullptr;
    7369             :         GPtrDiff_t src_inc_offset = 0;
    7370             :         GPtrDiff_t dst_inc_offset = 0;
    7371             :     };
    7372             : 
    7373          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7374           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7375          15 :     for (size_t i = 0; i < nDims; i++)
    7376             :     {
    7377          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7378          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7379          10 :         stack[i].dst_inc_offset =
    7380          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7381             :     }
    7382           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7383           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7384             : 
    7385           5 :     size_t dimIdx = 0;
    7386           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7387             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7388           5 :     CPLAssert(nBufferDTSize <= 16);
    7389          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7390             :     {
    7391          10 :         GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
    7392             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7393             :     }
    7394             : 
    7395          43 : lbl_next_depth:
    7396          43 :     if (dimIdx == nDimsMinus1)
    7397             :     {
    7398          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7399          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7400          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7401             : 
    7402         420 :         while (true)
    7403             :         {
    7404         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7405         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7406             : 
    7407         455 :             if (bBufferDataTypeIsByte)
    7408             :             {
    7409          24 :                 *dst_ptr = flag;
    7410             :             }
    7411             :             else
    7412             :             {
    7413         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7414             :             }
    7415             : 
    7416         455 :             if ((--nIters) == 0)
    7417          35 :                 break;
    7418         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7419         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7420             :         }
    7421             :     }
    7422             :     else
    7423             :     {
    7424           8 :         stack[dimIdx].nIters = count[dimIdx];
    7425             :         while (true)
    7426             :         {
    7427          38 :             dimIdx++;
    7428          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7429          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7430          38 :             goto lbl_next_depth;
    7431          38 :         lbl_return_to_caller:
    7432          38 :             dimIdx--;
    7433          38 :             if ((--stack[dimIdx].nIters) == 0)
    7434           8 :                 break;
    7435          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7436          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7437             :         }
    7438             :     }
    7439          43 :     if (dimIdx > 0)
    7440          38 :         goto lbl_return_to_caller;
    7441             : }
    7442             : 
    7443             : /************************************************************************/
    7444             : /*                            GetMask()                                 */
    7445             : /************************************************************************/
    7446             : 
    7447             : /** Return an array that is a mask for the current array
    7448             : 
    7449             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7450             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7451             : 
    7452             :  The generic implementation honours the NoDataValue, as well as various
    7453             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7454             :  and valid_range.
    7455             : 
    7456             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7457             :  can be used to specify strings of the "flag_meanings" attribute
    7458             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7459             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7460             :  and pixels matching none of those flags will be set at 0.
    7461             :  For example, let's consider the following netCDF variable defined with:
    7462             :  \verbatim
    7463             :  l2p_flags:valid_min = 0s ;
    7464             :  l2p_flags:valid_max = 256s ;
    7465             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7466             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7467             :  \endverbatim
    7468             : 
    7469             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7470             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7471             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7472             :    will be 1.
    7473             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7474             :    will be 0.
    7475             : 
    7476             :  This is the same as the C function GDALMDArrayGetMask().
    7477             : 
    7478             :  @param papszOptions NULL-terminated list of options, or NULL.
    7479             : 
    7480             :  @return a new array, that holds a reference to the original one, and thus is
    7481             :  a view of it (not a copy), or nullptr in case of error.
    7482             : */
    7483             : std::shared_ptr<GDALMDArray>
    7484          49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7485             : {
    7486          98 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7487          49 :     if (!self)
    7488             :     {
    7489           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7490             :                  "Driver implementation issue: m_pSelf not set !");
    7491           0 :         return nullptr;
    7492             :     }
    7493          49 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7494             :     {
    7495           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7496             :                  "GetMask() only supports numeric data type");
    7497           1 :         return nullptr;
    7498             :     }
    7499          48 :     return GDALMDArrayMask::Create(self, papszOptions);
    7500             : }
    7501             : 
    7502             : /************************************************************************/
    7503             : /*                         IsRegularlySpaced()                          */
    7504             : /************************************************************************/
    7505             : 
    7506             : /** Returns whether an array is a 1D regularly spaced array.
    7507             :  *
    7508             :  * @param[out] dfStart     First value in the array
    7509             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7510             :  * @return true if the array is regularly spaced.
    7511             :  */
    7512         189 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7513             : {
    7514         189 :     dfStart = 0;
    7515         189 :     dfIncrement = 0;
    7516         189 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7517           0 :         return false;
    7518         189 :     const auto nSize = GetDimensions()[0]->GetSize();
    7519         189 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7520           2 :         return false;
    7521             : 
    7522         187 :     size_t nCount = static_cast<size_t>(nSize);
    7523         374 :     std::vector<double> adfTmp;
    7524             :     try
    7525             :     {
    7526         187 :         adfTmp.resize(nCount);
    7527             :     }
    7528           0 :     catch (const std::exception &)
    7529             :     {
    7530           0 :         return false;
    7531             :     }
    7532             : 
    7533         187 :     GUInt64 anStart[1] = {0};
    7534         187 :     size_t anCount[1] = {nCount};
    7535             : 
    7536             :     const auto IsRegularlySpacedInternal =
    7537       83950 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7538             :     {
    7539         261 :         dfStart = adfTmp[0];
    7540         261 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7541         261 :         if (dfIncrement == 0)
    7542             :         {
    7543           3 :             return false;
    7544             :         }
    7545       20920 :         for (size_t i = 1; i < anCount[0]; i++)
    7546             :         {
    7547       20662 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7548       20662 :                 1e-3 * fabs(dfIncrement))
    7549             :             {
    7550           0 :                 return false;
    7551             :             }
    7552             :         }
    7553         258 :         return true;
    7554         187 :     };
    7555             : 
    7556             :     // First try with the first block(s). This can avoid excessive processing
    7557             :     // time, for example with Zarr datasets.
    7558             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7559             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7560         187 :     const auto nBlockSize = GetBlockSize()[0];
    7561         187 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7562             :     {
    7563             :         size_t nReducedCount =
    7564          77 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7565         440 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7566         363 :             nReducedCount *= 2;
    7567          77 :         anCount[0] = nReducedCount;
    7568          77 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7569         154 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7570             :         {
    7571           0 :             return false;
    7572             :         }
    7573          77 :         if (!IsRegularlySpacedInternal())
    7574             :         {
    7575           3 :             return false;
    7576             :         }
    7577             : 
    7578             :         // Get next values
    7579          74 :         anStart[0] = nReducedCount;
    7580          74 :         anCount[0] = nCount - nReducedCount;
    7581             :     }
    7582             : 
    7583         184 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7584         368 :               GDALExtendedDataType::Create(GDT_Float64),
    7585         184 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7586             :     {
    7587           0 :         return false;
    7588             :     }
    7589             : 
    7590         184 :     return IsRegularlySpacedInternal();
    7591             : }
    7592             : 
    7593             : /************************************************************************/
    7594             : /*                         GuessGeoTransform()                          */
    7595             : /************************************************************************/
    7596             : 
    7597             : /** Returns whether 2 specified dimensions form a geotransform
    7598             :  *
    7599             :  * @param nDimX                Index of the X axis.
    7600             :  * @param nDimY                Index of the Y axis.
    7601             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7602             :  *                             with the pixel-is-point (pixel-center) convention
    7603             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7604             :  *                             (top left corner convention)
    7605             :  *                             (bPixelIsPoint = false)
    7606             :  * @param[out] gt              Computed geotransform
    7607             :  * @return true if a geotransform could be computed.
    7608             :  */
    7609         229 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7610             :                                     bool bPixelIsPoint,
    7611             :                                     GDALGeoTransform &gt) const
    7612             : {
    7613         229 :     const auto &dims(GetDimensions());
    7614         458 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7615         458 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7616         229 :     double dfXStart = 0.0;
    7617         229 :     double dfXSpacing = 0.0;
    7618         229 :     double dfYStart = 0.0;
    7619         229 :     double dfYSpacing = 0.0;
    7620         517 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7621         288 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7622         334 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7623          95 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7624         463 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7625          90 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7626             :     {
    7627          90 :         gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7628          90 :         gt[1] = dfXSpacing;
    7629          90 :         gt[2] = 0;
    7630          90 :         gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7631          90 :         gt[4] = 0;
    7632          90 :         gt[5] = dfYSpacing;
    7633          90 :         return true;
    7634             :     }
    7635         139 :     return false;
    7636             : }
    7637             : 
    7638             : /** Returns whether 2 specified dimensions form a geotransform
    7639             :  *
    7640             :  * @param nDimX                Index of the X axis.
    7641             :  * @param nDimY                Index of the Y axis.
    7642             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7643             :  *                             with the pixel-is-point (pixel-center) convention
    7644             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7645             :  *                             (top left corner convention)
    7646             :  *                             (bPixelIsPoint = false)
    7647             :  * @param[out] adfGeoTransform Computed geotransform
    7648             :  * @return true if a geotransform could be computed.
    7649             :  */
    7650           0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7651             :                                     bool bPixelIsPoint,
    7652             :                                     double adfGeoTransform[6]) const
    7653             : {
    7654           0 :     GDALGeoTransform *gt =
    7655             :         reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
    7656           0 :     return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
    7657             : }
    7658             : 
    7659             : /************************************************************************/
    7660             : /*                       GDALMDArrayResampled                           */
    7661             : /************************************************************************/
    7662             : 
    7663             : class GDALMDArrayResampledDataset;
    7664             : 
    7665             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7666             : {
    7667             :   protected:
    7668             :     CPLErr IReadBlock(int, int, void *) override;
    7669             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7670             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7671             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7672             :                      GSpacing nLineSpaceBuf,
    7673             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7674             : 
    7675             :   public:
    7676             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7677             :         GDALMDArrayResampledDataset *poDSIn);
    7678             : 
    7679             :     double GetNoDataValue(int *pbHasNoData) override;
    7680             : };
    7681             : 
    7682             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7683             : {
    7684             :     friend class GDALMDArrayResampled;
    7685             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7686             : 
    7687             :     std::shared_ptr<GDALMDArray> m_poArray;
    7688             :     const size_t m_iXDim;
    7689             :     const size_t m_iYDim;
    7690             :     GDALGeoTransform m_gt{};
    7691             :     bool m_bHasGT = false;
    7692             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7693             : 
    7694             :     std::vector<GUInt64> m_anOffset{};
    7695             :     std::vector<size_t> m_anCount{};
    7696             :     std::vector<GPtrDiff_t> m_anStride{};
    7697             : 
    7698             :     std::string m_osFilenameLong{};
    7699             :     std::string m_osFilenameLat{};
    7700             : 
    7701             :   public:
    7702          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7703             :                                 size_t iXDim, size_t iYDim)
    7704          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7705          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7706          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7707          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7708             :     {
    7709          24 :         const auto &dims(m_poArray->GetDimensions());
    7710             : 
    7711          24 :         nRasterYSize = static_cast<int>(
    7712          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7713          24 :         nRasterXSize = static_cast<int>(
    7714          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7715             : 
    7716          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
    7717             : 
    7718          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7719          24 :     }
    7720             : 
    7721             :     ~GDALMDArrayResampledDataset() override;
    7722             : 
    7723          43 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    7724             :     {
    7725          43 :         gt = m_gt;
    7726          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7727             :     }
    7728             : 
    7729         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7730             :     {
    7731         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7732         105 :         if (m_poSRS)
    7733             :         {
    7734          79 :             m_poSRS.reset(m_poSRS->Clone());
    7735         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7736         237 :             for (auto &m : axisMapping)
    7737             :             {
    7738         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7739          79 :                     m = 1;
    7740          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7741          79 :                     m = 2;
    7742             :             }
    7743          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7744             :         }
    7745         105 :         return m_poSRS.get();
    7746             :     }
    7747             : 
    7748           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7749             :                              const std::string &osFilenameLat)
    7750             :     {
    7751           5 :         m_osFilenameLong = osFilenameLong;
    7752           5 :         m_osFilenameLat = osFilenameLat;
    7753          10 :         CPLStringList aosGeoLoc;
    7754           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7755           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7756           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7757           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7758           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7759           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7760           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7761           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7762           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7763           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7764           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7765           5 :     }
    7766             : };
    7767             : 
    7768          48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
    7769             : {
    7770          24 :     if (!m_osFilenameLong.empty())
    7771           5 :         VSIUnlink(m_osFilenameLong.c_str());
    7772          24 :     if (!m_osFilenameLat.empty())
    7773           5 :         VSIUnlink(m_osFilenameLat.c_str());
    7774          48 : }
    7775             : 
    7776             : /************************************************************************/
    7777             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7778             : /************************************************************************/
    7779             : 
    7780          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7781          24 :     GDALMDArrayResampledDataset *poDSIn)
    7782             : {
    7783          24 :     const auto &poArray(poDSIn->m_poArray);
    7784          24 :     const auto blockSize(poArray->GetBlockSize());
    7785          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7786          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7787          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7788          24 :                       : 1;
    7789          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7790          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7791          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7792          24 :                       : poDSIn->GetRasterXSize();
    7793          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7794          24 :     eAccess = poDSIn->eAccess;
    7795          24 : }
    7796             : 
    7797             : /************************************************************************/
    7798             : /*                           GetNoDataValue()                           */
    7799             : /************************************************************************/
    7800             : 
    7801          50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7802             : {
    7803          50 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7804          50 :     const auto &poArray(l_poDS->m_poArray);
    7805          50 :     bool bHasNodata = false;
    7806          50 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7807          50 :     if (pbHasNoData)
    7808          46 :         *pbHasNoData = bHasNodata;
    7809          50 :     return dfRes;
    7810             : }
    7811             : 
    7812             : /************************************************************************/
    7813             : /*                            IReadBlock()                              */
    7814             : /************************************************************************/
    7815             : 
    7816           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7817             :                                                          int nBlockYOff,
    7818             :                                                          void *pImage)
    7819             : {
    7820           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7821           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7822           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7823           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7824           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7825             :     GDALRasterIOExtraArg sExtraArg;
    7826           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7827           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7828             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7829           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7830             : }
    7831             : 
    7832             : /************************************************************************/
    7833             : /*                            IRasterIO()                               */
    7834             : /************************************************************************/
    7835             : 
    7836          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7837             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7838             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7839             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7840             :     GDALRasterIOExtraArg *psExtraArg)
    7841             : {
    7842          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7843          32 :     const auto &poArray(l_poDS->m_poArray);
    7844          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7845          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7846          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7847          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7848             :     {
    7849          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7850          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7851          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7852          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7853             : 
    7854          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7855          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7856          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7857          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7858             : 
    7859          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7860          32 :                              l_poDS->m_anCount.data(), nullptr,
    7861          32 :                              l_poDS->m_anStride.data(),
    7862          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7863          32 :                    ? CE_None
    7864          32 :                    : CE_Failure;
    7865             :     }
    7866           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7867             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7868           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7869             : }
    7870             : 
    7871             : class GDALMDArrayResampled final : public GDALPamMDArray
    7872             : {
    7873             :   private:
    7874             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7875             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7876             :     std::vector<GUInt64> m_anBlockSize;
    7877             :     GDALExtendedDataType m_dt;
    7878             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7879             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7880             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7881             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7882             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7883             : 
    7884             :   protected:
    7885          21 :     GDALMDArrayResampled(
    7886             :         const std::shared_ptr<GDALMDArray> &poParent,
    7887             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7888             :         const std::vector<GUInt64> &anBlockSize)
    7889          42 :         : GDALAbstractMDArray(std::string(),
    7890          42 :                               "Resampled view of " + poParent->GetFullName()),
    7891             :           GDALPamMDArray(
    7892          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7893          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7894          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7895         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7896             :     {
    7897          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7898          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7899          21 :     }
    7900             : 
    7901             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7902             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7903             :                const GDALExtendedDataType &bufferDataType,
    7904             :                void *pDstBuffer) const override;
    7905             : 
    7906             :   public:
    7907             :     static std::shared_ptr<GDALMDArray>
    7908             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7909             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7910             :            GDALRIOResampleAlg resampleAlg,
    7911             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7912             : 
    7913          42 :     ~GDALMDArrayResampled()
    7914          21 :     {
    7915             :         // First close the warped VRT
    7916          21 :         m_poReprojectedDS.reset();
    7917          21 :         m_poParentDS.reset();
    7918          42 :     }
    7919             : 
    7920          11 :     bool IsWritable() const override
    7921             :     {
    7922          11 :         return false;
    7923             :     }
    7924             : 
    7925          74 :     const std::string &GetFilename() const override
    7926             :     {
    7927          74 :         return m_poParent->GetFilename();
    7928             :     }
    7929             : 
    7930             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7931         257 :     GetDimensions() const override
    7932             :     {
    7933         257 :         return m_apoDims;
    7934             :     }
    7935             : 
    7936         109 :     const GDALExtendedDataType &GetDataType() const override
    7937             :     {
    7938         109 :         return m_dt;
    7939             :     }
    7940             : 
    7941          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    7942             :     {
    7943          21 :         return m_poSRS;
    7944             :     }
    7945             : 
    7946          12 :     std::vector<GUInt64> GetBlockSize() const override
    7947             :     {
    7948          12 :         return m_anBlockSize;
    7949             :     }
    7950             : 
    7951             :     std::shared_ptr<GDALAttribute>
    7952           1 :     GetAttribute(const std::string &osName) const override
    7953             :     {
    7954           1 :         return m_poParent->GetAttribute(osName);
    7955             :     }
    7956             : 
    7957             :     std::vector<std::shared_ptr<GDALAttribute>>
    7958          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    7959             :     {
    7960          12 :         return m_poParent->GetAttributes(papszOptions);
    7961             :     }
    7962             : 
    7963           1 :     const std::string &GetUnit() const override
    7964             :     {
    7965           1 :         return m_poParent->GetUnit();
    7966             :     }
    7967             : 
    7968           1 :     const void *GetRawNoDataValue() const override
    7969             :     {
    7970           1 :         return m_poParent->GetRawNoDataValue();
    7971             :     }
    7972             : 
    7973           1 :     double GetOffset(bool *pbHasOffset,
    7974             :                      GDALDataType *peStorageType) const override
    7975             :     {
    7976           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    7977             :     }
    7978             : 
    7979           1 :     double GetScale(bool *pbHasScale,
    7980             :                     GDALDataType *peStorageType) const override
    7981             :     {
    7982           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    7983             :     }
    7984             : };
    7985             : 
    7986             : /************************************************************************/
    7987             : /*                   GDALMDArrayResampled::Create()                     */
    7988             : /************************************************************************/
    7989             : 
    7990          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    7991             :     const std::shared_ptr<GDALMDArray> &poParent,
    7992             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    7993             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    7994             :     CSLConstList /* papszOptions */)
    7995             : {
    7996          29 :     const char *pszResampleAlg = "nearest";
    7997          29 :     bool unsupported = false;
    7998          29 :     switch (resampleAlg)
    7999             :     {
    8000          16 :         case GRIORA_NearestNeighbour:
    8001          16 :             pszResampleAlg = "nearest";
    8002          16 :             break;
    8003           2 :         case GRIORA_Bilinear:
    8004           2 :             pszResampleAlg = "bilinear";
    8005           2 :             break;
    8006           5 :         case GRIORA_Cubic:
    8007           5 :             pszResampleAlg = "cubic";
    8008           5 :             break;
    8009           1 :         case GRIORA_CubicSpline:
    8010           1 :             pszResampleAlg = "cubicspline";
    8011           1 :             break;
    8012           1 :         case GRIORA_Lanczos:
    8013           1 :             pszResampleAlg = "lanczos";
    8014           1 :             break;
    8015           1 :         case GRIORA_Average:
    8016           1 :             pszResampleAlg = "average";
    8017           1 :             break;
    8018           1 :         case GRIORA_Mode:
    8019           1 :             pszResampleAlg = "mode";
    8020           1 :             break;
    8021           1 :         case GRIORA_Gauss:
    8022           1 :             unsupported = true;
    8023           1 :             break;
    8024           0 :         case GRIORA_RESERVED_START:
    8025           0 :             unsupported = true;
    8026           0 :             break;
    8027           0 :         case GRIORA_RESERVED_END:
    8028           0 :             unsupported = true;
    8029           0 :             break;
    8030           1 :         case GRIORA_RMS:
    8031           1 :             pszResampleAlg = "rms";
    8032           1 :             break;
    8033             :     }
    8034          29 :     if (unsupported)
    8035             :     {
    8036           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8037             :                  "Unsupported resample method for GetResampled()");
    8038           1 :         return nullptr;
    8039             :     }
    8040             : 
    8041          28 :     if (poParent->GetDimensionCount() < 2)
    8042             :     {
    8043           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8044             :                  "GetResampled() only supports 2 dimensions or more");
    8045           1 :         return nullptr;
    8046             :     }
    8047             : 
    8048          27 :     const auto &aoParentDims = poParent->GetDimensions();
    8049          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    8050             :     {
    8051           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8052             :                  "GetResampled(): apoNewDims size should be the same as "
    8053             :                  "GetDimensionCount()");
    8054           2 :         return nullptr;
    8055             :     }
    8056             : 
    8057          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    8058          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    8059             : 
    8060          50 :     std::vector<GUInt64> anBlockSize;
    8061          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    8062          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    8063             : 
    8064          50 :     auto apoParentDims = poParent->GetDimensions();
    8065             :     // Special case for NASA EMIT datasets
    8066          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    8067           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    8068          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    8069           2 :                                apoParentDims[2]->GetName() == "bands");
    8070             : 
    8071             :     const size_t iYDimParent =
    8072          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    8073             :     const size_t iXDimParent =
    8074          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    8075             : 
    8076          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    8077             :     {
    8078          53 :         if (i == iYDimParent || i == iXDimParent)
    8079          48 :             continue;
    8080           5 :         if (apoNewDimsIn[i] == nullptr)
    8081             :         {
    8082           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    8083             :         }
    8084           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    8085           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    8086             :         {
    8087           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    8088             :                      "GetResampled(): apoNewDims[%u] should be the same "
    8089             :                      "as its parent",
    8090             :                      i);
    8091           1 :             return nullptr;
    8092             :         }
    8093             :         else
    8094             :         {
    8095           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    8096             :         }
    8097           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    8098             :     }
    8099             : 
    8100             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    8101          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    8102             : 
    8103          24 :     double dfXStart = 0.0;
    8104          24 :     double dfXSpacing = 0.0;
    8105          24 :     bool gotXSpacing = false;
    8106          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    8107          24 :     if (poNewDimX)
    8108             :     {
    8109           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    8110             :         {
    8111           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8112             :                      "Too big size for X dimension");
    8113           0 :             return nullptr;
    8114             :         }
    8115           4 :         auto var = poNewDimX->GetIndexingVariable();
    8116           4 :         if (var)
    8117             :         {
    8118           2 :             if (var->GetDimensionCount() != 1 ||
    8119           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    8120           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    8121             :             {
    8122           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8123             :                          "New X dimension should be indexed by a regularly "
    8124             :                          "spaced variable");
    8125           0 :                 return nullptr;
    8126             :             }
    8127           1 :             gotXSpacing = true;
    8128             :         }
    8129             :     }
    8130             : 
    8131          24 :     double dfYStart = 0.0;
    8132          24 :     double dfYSpacing = 0.0;
    8133          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    8134          24 :     bool gotYSpacing = false;
    8135          24 :     if (poNewDimY)
    8136             :     {
    8137           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8138             :         {
    8139           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8140             :                      "Too big size for Y dimension");
    8141           0 :             return nullptr;
    8142             :         }
    8143           4 :         auto var = poNewDimY->GetIndexingVariable();
    8144           4 :         if (var)
    8145             :         {
    8146           2 :             if (var->GetDimensionCount() != 1 ||
    8147           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8148           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8149             :             {
    8150           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8151             :                          "New Y dimension should be indexed by a regularly "
    8152             :                          "spaced variable");
    8153           0 :                 return nullptr;
    8154             :             }
    8155           1 :             gotYSpacing = true;
    8156             :         }
    8157             :     }
    8158             : 
    8159             :     // This limitation could probably be removed
    8160          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8161             :     {
    8162           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8163             :                  "Either none of new X or Y dimension should have an indexing "
    8164             :                  "variable, or both should both should have one.");
    8165           0 :         return nullptr;
    8166             :     }
    8167             : 
    8168          48 :     std::string osDstWKT;
    8169          24 :     if (poTargetSRS)
    8170             :     {
    8171           2 :         char *pszDstWKT = nullptr;
    8172           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8173             :         {
    8174           0 :             CPLFree(pszDstWKT);
    8175           0 :             return nullptr;
    8176             :         }
    8177           2 :         osDstWKT = pszDstWKT;
    8178           2 :         CPLFree(pszDstWKT);
    8179             :     }
    8180             : 
    8181             :     // Use coordinate variables for geolocation array
    8182          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8183          24 :     bool useGeolocationArray = false;
    8184          24 :     if (apoCoordinateVars.size() >= 2)
    8185             :     {
    8186           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8187           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8188          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8189             :         {
    8190          10 :             const auto &osName = poCoordVar->GetName();
    8191          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8192          20 :             std::string osStandardName;
    8193          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8194           2 :                 poAttr->GetDimensionCount() == 0)
    8195             :             {
    8196           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8197           2 :                 if (pszStandardName)
    8198           2 :                     osStandardName = pszStandardName;
    8199             :             }
    8200          21 :             if (osName == "lon" || osName == "longitude" ||
    8201          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8202             :             {
    8203           5 :                 poLongVar = poCoordVar;
    8204             :             }
    8205           6 :             else if (osName == "lat" || osName == "latitude" ||
    8206           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8207             :             {
    8208           5 :                 poLatVar = poCoordVar;
    8209             :             }
    8210             :         }
    8211           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8212             :         {
    8213           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8214           5 :             const auto &longDims = poLongVar->GetDimensions();
    8215           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8216           5 :             const auto &latDims = poLatVar->GetDimensions();
    8217           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8218           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8219           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8220           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8221             :             {
    8222             :                 // Geolocation arrays are 1D, and of consistent size with
    8223             :                 // the variable
    8224           0 :                 useGeolocationArray = true;
    8225             :             }
    8226           1 :             else if ((longDimCount == 2 ||
    8227           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8228          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8229          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8230           1 :                      (latDimCount == 2 ||
    8231           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8232          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8233           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8234             : 
    8235             :             {
    8236             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8237             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8238             :                 // size with the variable
    8239           5 :                 useGeolocationArray = true;
    8240             :             }
    8241             :             else
    8242             :             {
    8243           0 :                 CPLDebug(
    8244             :                     "GDAL",
    8245             :                     "Longitude and latitude coordinate variables found, "
    8246             :                     "but their characteristics are not compatible of using "
    8247             :                     "them as geolocation arrays");
    8248             :             }
    8249           5 :             if (useGeolocationArray)
    8250             :             {
    8251          10 :                 CPLDebug("GDAL",
    8252             :                          "Setting geolocation array from variables %s and %s",
    8253           5 :                          poLongVar->GetName().c_str(),
    8254           5 :                          poLatVar->GetName().c_str());
    8255             :                 const std::string osFilenameLong =
    8256           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8257             :                 const std::string osFilenameLat =
    8258           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8259             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8260             :                     longDimCount == 1
    8261           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8262          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8263          15 :                                                       longDimCount - 2));
    8264           5 :                 auto hTIFFLongDS = GDALTranslate(
    8265             :                     osFilenameLong.c_str(),
    8266             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8267             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8268           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8269          20 :                                      : poLatVar->AsClassicDataset(
    8270          15 :                                            latDimCount - 1, latDimCount - 2));
    8271           5 :                 auto hTIFFLatDS = GDALTranslate(
    8272             :                     osFilenameLat.c_str(),
    8273             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8274           5 :                 const bool bError =
    8275           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8276           5 :                 GDALClose(hTIFFLongDS);
    8277           5 :                 GDALClose(hTIFFLatDS);
    8278           5 :                 if (bError)
    8279             :                 {
    8280           0 :                     VSIUnlink(osFilenameLong.c_str());
    8281           0 :                     VSIUnlink(osFilenameLat.c_str());
    8282           0 :                     return nullptr;
    8283             :                 }
    8284             : 
    8285           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8286             :             }
    8287             :         }
    8288             :         else
    8289             :         {
    8290           0 :             CPLDebug("GDAL",
    8291             :                      "Coordinate variables available for %s, but "
    8292             :                      "longitude and/or latitude variables were not identified",
    8293           0 :                      poParent->GetName().c_str());
    8294             :         }
    8295             :     }
    8296             : 
    8297             :     // Build gdalwarp arguments
    8298          48 :     CPLStringList aosArgv;
    8299             : 
    8300          24 :     aosArgv.AddString("-of");
    8301          24 :     aosArgv.AddString("VRT");
    8302             : 
    8303          24 :     aosArgv.AddString("-r");
    8304          24 :     aosArgv.AddString(pszResampleAlg);
    8305             : 
    8306          24 :     if (!osDstWKT.empty())
    8307             :     {
    8308           2 :         aosArgv.AddString("-t_srs");
    8309           2 :         aosArgv.AddString(osDstWKT.c_str());
    8310             :     }
    8311             : 
    8312          24 :     if (useGeolocationArray)
    8313           5 :         aosArgv.AddString("-geoloc");
    8314             : 
    8315          24 :     if (gotXSpacing && gotYSpacing)
    8316             :     {
    8317           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8318             :         const double dfXMax =
    8319           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8320           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8321             :         const double dfYMin =
    8322           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8323           1 :         aosArgv.AddString("-te");
    8324           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8325           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8326           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8327           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8328             :     }
    8329             : 
    8330          24 :     if (poNewDimX && poNewDimY)
    8331             :     {
    8332           3 :         aosArgv.AddString("-ts");
    8333             :         aosArgv.AddString(
    8334           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8335             :         aosArgv.AddString(
    8336           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8337             :     }
    8338          21 :     else if (poNewDimX)
    8339             :     {
    8340           1 :         aosArgv.AddString("-ts");
    8341             :         aosArgv.AddString(
    8342           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8343           1 :         aosArgv.AddString("0");
    8344             :     }
    8345          20 :     else if (poNewDimY)
    8346             :     {
    8347           1 :         aosArgv.AddString("-ts");
    8348           1 :         aosArgv.AddString("0");
    8349             :         aosArgv.AddString(
    8350           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8351             :     }
    8352             : 
    8353             :     // Create a warped VRT dataset
    8354             :     GDALWarpAppOptions *psOptions =
    8355          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8356          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8357             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8358          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8359          24 :     GDALWarpAppOptionsFree(psOptions);
    8360          24 :     if (poReprojectedDS == nullptr)
    8361           3 :         return nullptr;
    8362             : 
    8363             :     int nBlockXSize;
    8364             :     int nBlockYSize;
    8365          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8366          21 :     anBlockSize.emplace_back(nBlockYSize);
    8367          21 :     anBlockSize.emplace_back(nBlockXSize);
    8368             : 
    8369          21 :     GDALGeoTransform gt;
    8370          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
    8371          21 :     CPLAssert(eErr == CE_None);
    8372          21 :     CPL_IGNORE_RET_VAL(eErr);
    8373             : 
    8374             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8375           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8376          42 :         poReprojectedDS->GetRasterYSize());
    8377             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8378          63 :         std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
    8379          21 :     poDimY->SetIndexingVariable(varY);
    8380             : 
    8381             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8382           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8383          42 :         poReprojectedDS->GetRasterXSize());
    8384             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8385          63 :         std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
    8386          21 :     poDimX->SetIndexingVariable(varX);
    8387             : 
    8388          21 :     apoNewDims.emplace_back(poDimY);
    8389          21 :     apoNewDims.emplace_back(poDimX);
    8390             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8391          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8392          21 :     newAr->SetSelf(newAr);
    8393          21 :     if (poTargetSRS)
    8394             :     {
    8395           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8396             :     }
    8397             :     else
    8398             :     {
    8399          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8400             :     }
    8401          21 :     newAr->m_poVarX = varX;
    8402          21 :     newAr->m_poVarY = varY;
    8403          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8404          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8405             : 
    8406             :     // If the input array is y,x,band ordered, the above newAr is
    8407             :     // actually band,y,x ordered as it is more convenient for
    8408             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8409             :     // array to the order of the input array
    8410          21 :     if (bYXBandOrder)
    8411           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8412             : 
    8413          19 :     return newAr;
    8414             : }
    8415             : 
    8416             : /************************************************************************/
    8417             : /*                   GDALMDArrayResampled::IRead()                      */
    8418             : /************************************************************************/
    8419             : 
    8420          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8421             :                                  const size_t *count, const GInt64 *arrayStep,
    8422             :                                  const GPtrDiff_t *bufferStride,
    8423             :                                  const GDALExtendedDataType &bufferDataType,
    8424             :                                  void *pDstBuffer) const
    8425             : {
    8426          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8427           0 :         return false;
    8428             : 
    8429             :     struct Stack
    8430             :     {
    8431             :         size_t nIters = 0;
    8432             :         GByte *dst_ptr = nullptr;
    8433             :         GPtrDiff_t dst_inc_offset = 0;
    8434             :     };
    8435             : 
    8436          29 :     const auto nDims = GetDimensionCount();
    8437          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8438          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8439          92 :     for (size_t i = 0; i < nDims; i++)
    8440             :     {
    8441          63 :         stack[i].dst_inc_offset =
    8442          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8443             :     }
    8444          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8445             : 
    8446          29 :     size_t dimIdx = 0;
    8447          29 :     const size_t iDimY = nDims - 2;
    8448          29 :     const size_t iDimX = nDims - 1;
    8449             :     // Use an array to avoid a false positive warning from CLang Static
    8450             :     // Analyzer about flushCaches being never read
    8451          29 :     bool flushCaches[] = {false};
    8452             :     const bool bYXBandOrder =
    8453          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8454             : 
    8455          38 : lbl_next_depth:
    8456          38 :     if (dimIdx == iDimY)
    8457             :     {
    8458          33 :         if (flushCaches[0])
    8459             :         {
    8460           5 :             flushCaches[0] = false;
    8461             :             // When changing of 2D slice, flush GDAL 2D buffers
    8462           5 :             m_poParentDS->FlushCache(false);
    8463           5 :             m_poReprojectedDS->FlushCache(false);
    8464             :         }
    8465             : 
    8466          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8467             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8468             :                                     arrayStep, bufferStride, bufferDataType,
    8469          33 :                                     stack[dimIdx].dst_ptr))
    8470             :         {
    8471           0 :             return false;
    8472             :         }
    8473             :     }
    8474             :     else
    8475             :     {
    8476           5 :         stack[dimIdx].nIters = count[dimIdx];
    8477           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8478           5 :             arrayStartIdx[dimIdx])
    8479             :         {
    8480           1 :             flushCaches[0] = true;
    8481             :         }
    8482           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8483           5 :             arrayStartIdx[dimIdx];
    8484             :         while (true)
    8485             :         {
    8486           9 :             dimIdx++;
    8487           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8488           9 :             goto lbl_next_depth;
    8489           9 :         lbl_return_to_caller:
    8490           9 :             dimIdx--;
    8491           9 :             if ((--stack[dimIdx].nIters) == 0)
    8492           5 :                 break;
    8493           4 :             flushCaches[0] = true;
    8494           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8495           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8496             :         }
    8497             :     }
    8498          38 :     if (dimIdx > 0)
    8499           9 :         goto lbl_return_to_caller;
    8500             : 
    8501          29 :     return true;
    8502             : }
    8503             : 
    8504             : /************************************************************************/
    8505             : /*                           GetResampled()                             */
    8506             : /************************************************************************/
    8507             : 
    8508             : /** Return an array that is a resampled / reprojected view of the current array
    8509             :  *
    8510             :  * This is the same as the C function GDALMDArrayGetResampled().
    8511             :  *
    8512             :  * Currently this method can only resample along the last 2 dimensions, unless
    8513             :  * orthorectifying a NASA EMIT dataset.
    8514             :  *
    8515             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8516             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8517             :  *
    8518             :  * Options available are:
    8519             :  * <ul>
    8520             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8521             :  * Can be set to NO to use generic reprojection method.
    8522             :  * </li>
    8523             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8524             :  * orthorectification to take into account the value of the
    8525             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8526             :  * current array along the band dimension are valid.</li>
    8527             :  * </ul>
    8528             :  *
    8529             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8530             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8531             :  *                   determine it.
    8532             :  * @param resampleAlg Resampling algorithm
    8533             :  * @param poTargetSRS Target SRS, or nullptr
    8534             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8535             :  *
    8536             :  * @return a new array, that holds a reference to the original one, and thus is
    8537             :  * a view of it (not a copy), or nullptr in case of error.
    8538             :  *
    8539             :  * @since 3.4
    8540             :  */
    8541          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8542             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8543             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8544             :     CSLConstList papszOptions) const
    8545             : {
    8546          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8547          38 :     if (!self)
    8548             :     {
    8549           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8550             :                  "Driver implementation issue: m_pSelf not set !");
    8551           0 :         return nullptr;
    8552             :     }
    8553          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8554             :     {
    8555           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8556             :                  "GetResampled() only supports numeric data type");
    8557           0 :         return nullptr;
    8558             :     }
    8559             : 
    8560             :     // Special case for NASA EMIT datasets
    8561          76 :     auto apoDims = GetDimensions();
    8562          36 :     if (poTargetSRS == nullptr &&
    8563          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8564          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8565          10 :           apoDims[2]->GetName() == "bands" &&
    8566          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8567           1 :            apoNewDims ==
    8568          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8569          30 :                                                            apoDims[2]})) ||
    8570          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8571           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8572          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8573          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8574             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8575             :     {
    8576           9 :         auto poRootGroup = GetRootGroup();
    8577           9 :         if (poRootGroup)
    8578             :         {
    8579          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8580          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8581           9 :             if (poAttrGeotransform &&
    8582           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8583           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8584          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8585           9 :                 poLocationGroup)
    8586             :             {
    8587          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8588          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8589          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8590          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8591          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8592          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8593          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8594           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8595             :                 {
    8596             :                     return CreateGLTOrthorectified(
    8597             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8598             :                         /* nGLTIndexOffset = */ -1,
    8599          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8600             :                 }
    8601             :             }
    8602             :         }
    8603             :     }
    8604             : 
    8605          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8606             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8607             :     {
    8608           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8609             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8610             :                  "parameters are not compatible with it");
    8611           0 :         return nullptr;
    8612             :     }
    8613             : 
    8614             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8615          29 :                                         poTargetSRS, papszOptions);
    8616             : }
    8617             : 
    8618             : /************************************************************************/
    8619             : /*                         GDALDatasetFromArray()                       */
    8620             : /************************************************************************/
    8621             : 
    8622             : class GDALDatasetFromArray;
    8623             : 
    8624             : namespace
    8625             : {
    8626             : struct MetadataItem
    8627             : {
    8628             :     std::shared_ptr<GDALAbstractMDArray> poArray{};
    8629             :     std::string osName{};
    8630             :     std::string osDefinition{};
    8631             :     bool bDefinitionUsesPctForG = false;
    8632             : };
    8633             : 
    8634             : struct BandImageryMetadata
    8635             : {
    8636             :     std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
    8637             :     double dfCentralWavelengthToMicrometer = 1.0;
    8638             :     std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
    8639             :     double dfFWHMToMicrometer = 1.0;
    8640             : };
    8641             : 
    8642             : }  // namespace
    8643             : 
    8644             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8645             : {
    8646             :     std::vector<GUInt64> m_anOffset{};
    8647             :     std::vector<size_t> m_anCount{};
    8648             :     std::vector<GPtrDiff_t> m_anStride{};
    8649             : 
    8650             :   protected:
    8651             :     CPLErr IReadBlock(int, int, void *) override;
    8652             :     CPLErr IWriteBlock(int, int, void *) override;
    8653             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8654             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8655             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8656             :                      GSpacing nLineSpaceBuf,
    8657             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8658             : 
    8659             :   public:
    8660             :     explicit GDALRasterBandFromArray(
    8661             :         GDALDatasetFromArray *poDSIn,
    8662             :         const std::vector<GUInt64> &anOtherDimCoord,
    8663             :         const std::vector<std::vector<MetadataItem>>
    8664             :             &aoBandParameterMetadataItems,
    8665             :         const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8666             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8667             : 
    8668             :     double GetNoDataValue(int *pbHasNoData) override;
    8669             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8670             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8671             :     double GetOffset(int *pbHasOffset) override;
    8672             :     double GetScale(int *pbHasScale) override;
    8673             :     const char *GetUnitType() override;
    8674             :     GDALColorInterp GetColorInterpretation() override;
    8675             : };
    8676             : 
    8677             : class GDALDatasetFromArray final : public GDALPamDataset
    8678             : {
    8679             :     friend class GDALRasterBandFromArray;
    8680             : 
    8681             :     std::shared_ptr<GDALMDArray> m_poArray;
    8682             :     size_t m_iXDim;
    8683             :     size_t m_iYDim;
    8684             :     GDALGeoTransform m_gt{};
    8685             :     bool m_bHasGT = false;
    8686             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8687             :     GDALMultiDomainMetadata m_oMDD{};
    8688             :     std::string m_osOvrFilename{};
    8689             : 
    8690             :   public:
    8691         205 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8692             :                          size_t iXDim, size_t iYDim)
    8693         205 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8694             :     {
    8695             :         // Initialize an overview filename from the filename of the array
    8696             :         // and its name.
    8697         205 :         const std::string &osFilename = m_poArray->GetFilename();
    8698         205 :         if (!osFilename.empty())
    8699             :         {
    8700         181 :             m_osOvrFilename = osFilename;
    8701         181 :             m_osOvrFilename += '.';
    8702        6630 :             for (char ch : m_poArray->GetName())
    8703             :             {
    8704        6449 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8705        5715 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8706             :                 {
    8707        5188 :                     m_osOvrFilename += ch;
    8708             :                 }
    8709             :                 else
    8710             :                 {
    8711        1261 :                     m_osOvrFilename += '_';
    8712             :                 }
    8713             :             }
    8714         181 :             m_osOvrFilename += ".ovr";
    8715         181 :             oOvManager.Initialize(this);
    8716             :         }
    8717         205 :     }
    8718             : 
    8719             :     static GDALDatasetFromArray *
    8720             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8721             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8722             :            CSLConstList papszOptions);
    8723             : 
    8724             :     ~GDALDatasetFromArray() override;
    8725             : 
    8726         336 :     CPLErr Close() override
    8727             :     {
    8728         336 :         CPLErr eErr = CE_None;
    8729         336 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8730             :         {
    8731         336 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8732             :                 CE_None)
    8733           0 :                 eErr = CE_Failure;
    8734         336 :             m_poArray.reset();
    8735             :         }
    8736         336 :         return eErr;
    8737             :     }
    8738             : 
    8739          54 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    8740             :     {
    8741          54 :         gt = m_gt;
    8742          54 :         return m_bHasGT ? CE_None : CE_Failure;
    8743             :     }
    8744             : 
    8745          62 :     const OGRSpatialReference *GetSpatialRef() const override
    8746             :     {
    8747          62 :         if (m_poArray->GetDimensionCount() < 2)
    8748           3 :             return nullptr;
    8749          59 :         m_poSRS = m_poArray->GetSpatialRef();
    8750          59 :         if (m_poSRS)
    8751             :         {
    8752          20 :             m_poSRS.reset(m_poSRS->Clone());
    8753          40 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8754          60 :             for (auto &m : axisMapping)
    8755             :             {
    8756          40 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8757          20 :                     m = 1;
    8758          20 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8759          20 :                     m = 2;
    8760             :             }
    8761          20 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8762             :         }
    8763          59 :         return m_poSRS.get();
    8764             :     }
    8765             : 
    8766           5 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8767             :     {
    8768           5 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8769             :     }
    8770             : 
    8771         163 :     char **GetMetadata(const char *pszDomain) override
    8772             :     {
    8773         163 :         return m_oMDD.GetMetadata(pszDomain);
    8774             :     }
    8775             : 
    8776         233 :     const char *GetMetadataItem(const char *pszName,
    8777             :                                 const char *pszDomain) override
    8778             :     {
    8779         421 :         if (!m_osOvrFilename.empty() && pszName &&
    8780         435 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8781          14 :             EQUAL(pszDomain, "OVERVIEWS"))
    8782             :         {
    8783          14 :             return m_osOvrFilename.c_str();
    8784             :         }
    8785         219 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8786             :     }
    8787             : };
    8788             : 
    8789         410 : GDALDatasetFromArray::~GDALDatasetFromArray()
    8790             : {
    8791         205 :     GDALDatasetFromArray::Close();
    8792         410 : }
    8793             : 
    8794             : /************************************************************************/
    8795             : /*                      GDALRasterBandFromArray()                       */
    8796             : /************************************************************************/
    8797             : 
    8798         279 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8799             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8800             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8801             :     const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8802         279 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8803             : {
    8804         279 :     const auto &poArray(poDSIn->m_poArray);
    8805         279 :     const auto &dims(poArray->GetDimensions());
    8806         279 :     const auto nDimCount(dims.size());
    8807         558 :     const auto blockSize(poArray->GetBlockSize());
    8808         267 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8809         546 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8810         138 :                                                   blockSize[poDSIn->m_iYDim]))
    8811             :                       : 1;
    8812         279 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8813         150 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8814         150 :                                                   blockSize[poDSIn->m_iXDim]))
    8815         279 :                       : poDSIn->GetRasterXSize();
    8816         279 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8817         279 :     eAccess = poDSIn->eAccess;
    8818         279 :     m_anOffset.resize(nDimCount);
    8819         279 :     m_anCount.resize(nDimCount, 1);
    8820         279 :     m_anStride.resize(nDimCount);
    8821         951 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8822             :     {
    8823         672 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8824             :         {
    8825         252 :             std::string dimName(dims[i]->GetName());
    8826         126 :             GUInt64 nIndex = anOtherDimCoord[j];
    8827             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8828             :             // subsetted dimensions as generated by GetView()
    8829         126 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8830             :             {
    8831             :                 CPLStringList aosTokens(
    8832          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8833           6 :                 if (aosTokens.size() == 5)
    8834             :                 {
    8835           6 :                     dimName = aosTokens[1];
    8836          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8837           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8838           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8839           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8840           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8841             :                 }
    8842             :             }
    8843         126 :             if (nDimCount != 3 || dimName != "Band")
    8844             :             {
    8845          70 :                 SetMetadataItem(
    8846             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8847             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8848             :             }
    8849             : 
    8850         126 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8851             : 
    8852             :             // If the indexing variable is also listed in band parameter arrays,
    8853             :             // then don't use our default formatting
    8854         126 :             if (indexingVar)
    8855             :             {
    8856          42 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8857             :                 {
    8858          14 :                     if (oItem.poArray->GetFullName() ==
    8859          14 :                         indexingVar->GetFullName())
    8860             :                     {
    8861          12 :                         indexingVar.reset();
    8862          12 :                         break;
    8863             :                     }
    8864             :                 }
    8865             :             }
    8866             : 
    8867         154 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8868          28 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8869          28 :                     dims[i]->GetSize())
    8870             :             {
    8871          28 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8872             :                 {
    8873           0 :                     if (!bHasWarned)
    8874             :                     {
    8875           0 :                         CPLError(
    8876             :                             CE_Warning, CPLE_AppDefined,
    8877             :                             "Maximum delay to load band metadata from "
    8878             :                             "dimension indexing variables has expired. "
    8879             :                             "Increase the value of the "
    8880             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8881             :                             "option of GDALMDArray::AsClassicDataset() "
    8882             :                             "(also accessible as the "
    8883             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8884             :                             "configuration option), "
    8885             :                             "or set it to 'unlimited' for unlimited delay. ");
    8886           0 :                         bHasWarned = true;
    8887             :                     }
    8888             :                 }
    8889             :                 else
    8890             :                 {
    8891          28 :                     size_t nCount = 1;
    8892          28 :                     const auto &dt(indexingVar->GetDataType());
    8893          56 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8894          56 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8895          28 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8896             :                     {
    8897          28 :                         char *pszTmp = nullptr;
    8898          28 :                         GDALExtendedDataType::CopyValue(
    8899          28 :                             &abyTmp[0], dt, &pszTmp,
    8900          56 :                             GDALExtendedDataType::CreateString());
    8901          28 :                         if (pszTmp)
    8902             :                         {
    8903          28 :                             SetMetadataItem(
    8904             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8905             :                                 pszTmp);
    8906          28 :                             CPLFree(pszTmp);
    8907             :                         }
    8908             : 
    8909          28 :                         const auto &unit(indexingVar->GetUnit());
    8910          28 :                         if (!unit.empty())
    8911             :                         {
    8912          12 :                             SetMetadataItem(
    8913             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8914             :                                 unit.c_str());
    8915             :                         }
    8916             :                     }
    8917             :                 }
    8918             :             }
    8919             : 
    8920         144 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8921             :             {
    8922          36 :                 CPLString osVal;
    8923             : 
    8924          18 :                 size_t nCount = 1;
    8925          18 :                 const auto &dt(oItem.poArray->GetDataType());
    8926          18 :                 if (oItem.bDefinitionUsesPctForG)
    8927             :                 {
    8928             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8929          16 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8930          16 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8931           8 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8932             :                     {
    8933           8 :                         double dfVal = 0;
    8934           8 :                         GDALExtendedDataType::CopyValue(
    8935           8 :                             &abyTmp[0], dt, &dfVal,
    8936          16 :                             GDALExtendedDataType::Create(GDT_Float64));
    8937           8 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8938             :                     }
    8939             :                 }
    8940             :                 else
    8941             :                 {
    8942             :                     // There should be zero or one %s in osDefinition
    8943          10 :                     char *pszValue = nullptr;
    8944          10 :                     if (dt.GetClass() == GEDTC_STRING)
    8945             :                     {
    8946           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    8947           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    8948           2 :                             dt, &pszValue));
    8949             :                     }
    8950             :                     else
    8951             :                     {
    8952          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    8953          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8954             :                                                 nullptr, nullptr, dt,
    8955           8 :                                                 &abyTmp[0]))
    8956             :                         {
    8957           8 :                             GDALExtendedDataType::CopyValue(
    8958           8 :                                 &abyTmp[0], dt, &pszValue,
    8959          16 :                                 GDALExtendedDataType::CreateString());
    8960             :                         }
    8961             :                     }
    8962             : 
    8963          10 :                     if (pszValue)
    8964             :                     {
    8965          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    8966          10 :                         CPLFree(pszValue);
    8967             :                     }
    8968             :                 }
    8969          18 :                 if (!osVal.empty())
    8970          18 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    8971             :             }
    8972             : 
    8973         126 :             if (aoBandImageryMetadata[j].poCentralWavelengthArray)
    8974             :             {
    8975             :                 auto &poCentralWavelengthArray =
    8976           4 :                     aoBandImageryMetadata[j].poCentralWavelengthArray;
    8977           4 :                 size_t nCount = 1;
    8978           4 :                 const auto &dt(poCentralWavelengthArray->GetDataType());
    8979           8 :                 std::vector<GByte> abyTmp(dt.GetSize());
    8980           8 :                 if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
    8981             :                                                    &nCount, nullptr, nullptr,
    8982           4 :                                                    dt, &abyTmp[0]))
    8983             :                 {
    8984           4 :                     double dfVal = 0;
    8985           4 :                     GDALExtendedDataType::CopyValue(
    8986           4 :                         &abyTmp[0], dt, &dfVal,
    8987           8 :                         GDALExtendedDataType::Create(GDT_Float64));
    8988           4 :                     SetMetadataItem(
    8989             :                         "CENTRAL_WAVELENGTH_UM",
    8990             :                         CPLSPrintf(
    8991           4 :                             "%g", dfVal * aoBandImageryMetadata[j]
    8992           4 :                                               .dfCentralWavelengthToMicrometer),
    8993             :                         "IMAGERY");
    8994             :                 }
    8995             :             }
    8996             : 
    8997         126 :             if (aoBandImageryMetadata[j].poFWHMArray)
    8998             :             {
    8999           2 :                 auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
    9000           2 :                 size_t nCount = 1;
    9001           2 :                 const auto &dt(poFWHMArray->GetDataType());
    9002           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    9003           4 :                 if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
    9004           2 :                                       nullptr, dt, &abyTmp[0]))
    9005             :                 {
    9006           2 :                     double dfVal = 0;
    9007           2 :                     GDALExtendedDataType::CopyValue(
    9008           2 :                         &abyTmp[0], dt, &dfVal,
    9009           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    9010           2 :                     SetMetadataItem(
    9011             :                         "FWHM_UM",
    9012           2 :                         CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
    9013           2 :                                                      .dfFWHMToMicrometer),
    9014             :                         "IMAGERY");
    9015             :                 }
    9016             :             }
    9017             : 
    9018         126 :             m_anOffset[i] = anOtherDimCoord[j];
    9019         126 :             j++;
    9020             :         }
    9021             :     }
    9022         279 : }
    9023             : 
    9024             : /************************************************************************/
    9025             : /*                           GetNoDataValue()                           */
    9026             : /************************************************************************/
    9027             : 
    9028         111 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    9029             : {
    9030         111 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9031         111 :     const auto &poArray(l_poDS->m_poArray);
    9032         111 :     bool bHasNodata = false;
    9033         111 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    9034         111 :     if (pbHasNoData)
    9035          99 :         *pbHasNoData = bHasNodata;
    9036         111 :     return res;
    9037             : }
    9038             : 
    9039             : /************************************************************************/
    9040             : /*                       GetNoDataValueAsInt64()                        */
    9041             : /************************************************************************/
    9042             : 
    9043           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    9044             : {
    9045           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9046           1 :     const auto &poArray(l_poDS->m_poArray);
    9047           1 :     bool bHasNodata = false;
    9048           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    9049           1 :     if (pbHasNoData)
    9050           1 :         *pbHasNoData = bHasNodata;
    9051           1 :     return nodata;
    9052             : }
    9053             : 
    9054             : /************************************************************************/
    9055             : /*                      GetNoDataValueAsUInt64()                        */
    9056             : /************************************************************************/
    9057             : 
    9058           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    9059             : {
    9060           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9061           1 :     const auto &poArray(l_poDS->m_poArray);
    9062           1 :     bool bHasNodata = false;
    9063           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    9064           1 :     if (pbHasNoData)
    9065           1 :         *pbHasNoData = bHasNodata;
    9066           1 :     return nodata;
    9067             : }
    9068             : 
    9069             : /************************************************************************/
    9070             : /*                             GetOffset()                              */
    9071             : /************************************************************************/
    9072             : 
    9073          38 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    9074             : {
    9075          38 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9076          38 :     const auto &poArray(l_poDS->m_poArray);
    9077          38 :     bool bHasValue = false;
    9078          38 :     double dfRes = poArray->GetOffset(&bHasValue);
    9079          38 :     if (pbHasOffset)
    9080          19 :         *pbHasOffset = bHasValue;
    9081          38 :     return dfRes;
    9082             : }
    9083             : 
    9084             : /************************************************************************/
    9085             : /*                           GetUnitType()                              */
    9086             : /************************************************************************/
    9087             : 
    9088          44 : const char *GDALRasterBandFromArray::GetUnitType()
    9089             : {
    9090          44 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9091          44 :     const auto &poArray(l_poDS->m_poArray);
    9092          44 :     return poArray->GetUnit().c_str();
    9093             : }
    9094             : 
    9095             : /************************************************************************/
    9096             : /*                             GetScale()                              */
    9097             : /************************************************************************/
    9098             : 
    9099          36 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    9100             : {
    9101          36 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9102          36 :     const auto &poArray(l_poDS->m_poArray);
    9103          36 :     bool bHasValue = false;
    9104          36 :     double dfRes = poArray->GetScale(&bHasValue);
    9105          36 :     if (pbHasScale)
    9106          17 :         *pbHasScale = bHasValue;
    9107          36 :     return dfRes;
    9108             : }
    9109             : 
    9110             : /************************************************************************/
    9111             : /*                            IReadBlock()                              */
    9112             : /************************************************************************/
    9113             : 
    9114          68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    9115             :                                            void *pImage)
    9116             : {
    9117          68 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9118          68 :     const int nXOff = nBlockXOff * nBlockXSize;
    9119          68 :     const int nYOff = nBlockYOff * nBlockYSize;
    9120          68 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9121          68 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9122             :     GDALRasterIOExtraArg sExtraArg;
    9123          68 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9124         136 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9125             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9126         136 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9127             : }
    9128             : 
    9129             : /************************************************************************/
    9130             : /*                            IWriteBlock()                             */
    9131             : /************************************************************************/
    9132             : 
    9133           1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    9134             :                                             void *pImage)
    9135             : {
    9136           1 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9137           1 :     const int nXOff = nBlockXOff * nBlockXSize;
    9138           1 :     const int nYOff = nBlockYOff * nBlockYSize;
    9139           1 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9140           1 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9141             :     GDALRasterIOExtraArg sExtraArg;
    9142           1 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9143           2 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9144             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9145           2 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9146             : }
    9147             : 
    9148             : /************************************************************************/
    9149             : /*                            IRasterIO()                               */
    9150             : /************************************************************************/
    9151             : 
    9152         332 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9153             :                                           int nYOff, int nXSize, int nYSize,
    9154             :                                           void *pData, int nBufXSize,
    9155             :                                           int nBufYSize, GDALDataType eBufType,
    9156             :                                           GSpacing nPixelSpaceBuf,
    9157             :                                           GSpacing nLineSpaceBuf,
    9158             :                                           GDALRasterIOExtraArg *psExtraArg)
    9159             : {
    9160         332 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9161         332 :     const auto &poArray(l_poDS->m_poArray);
    9162         332 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9163         332 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9164         332 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9165         332 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    9166             :     {
    9167         332 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9168         332 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9169         664 :         m_anStride[l_poDS->m_iXDim] =
    9170         332 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9171         332 :         if (poArray->GetDimensionCount() >= 2)
    9172             :         {
    9173         323 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9174         323 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9175         323 :             m_anStride[l_poDS->m_iYDim] =
    9176         323 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9177             :         }
    9178         332 :         if (eRWFlag == GF_Read)
    9179             :         {
    9180         652 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9181         326 :                                  m_anStride.data(),
    9182         652 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9183         326 :                        ? CE_None
    9184         326 :                        : CE_Failure;
    9185             :         }
    9186             :         else
    9187             :         {
    9188          12 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9189           6 :                                   m_anStride.data(),
    9190          12 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9191           6 :                        ? CE_None
    9192           6 :                        : CE_Failure;
    9193             :         }
    9194             :     }
    9195           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9196             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9197           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9198             : }
    9199             : 
    9200             : /************************************************************************/
    9201             : /*                      GetColorInterpretation()                        */
    9202             : /************************************************************************/
    9203             : 
    9204          60 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9205             : {
    9206          60 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9207          60 :     const auto &poArray(l_poDS->m_poArray);
    9208         180 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9209          60 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9210             :     {
    9211           6 :         bool bOK = false;
    9212           6 :         GUInt64 nStartIndex = 0;
    9213           6 :         if (poArray->GetDimensionCount() == 2 &&
    9214           0 :             poAttr->GetDimensionCount() == 0)
    9215             :         {
    9216           0 :             bOK = true;
    9217             :         }
    9218           6 :         else if (poArray->GetDimensionCount() == 3)
    9219             :         {
    9220           6 :             uint64_t nExtraDimSamples = 1;
    9221           6 :             const auto &apoDims = poArray->GetDimensions();
    9222          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9223             :             {
    9224          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9225           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9226             :             }
    9227           6 :             if (poAttr->GetDimensionsSize() ==
    9228          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9229             :             {
    9230           6 :                 bOK = true;
    9231             :             }
    9232           6 :             nStartIndex = nBand - 1;
    9233             :         }
    9234           6 :         if (bOK)
    9235             :         {
    9236           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9237           6 :             const size_t nCount = 1;
    9238           6 :             const GInt64 arrayStep = 1;
    9239           6 :             const GPtrDiff_t bufferStride = 1;
    9240           6 :             char *pszValue = nullptr;
    9241           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9242           6 :                          oStringDT, &pszValue);
    9243           6 :             if (pszValue)
    9244             :             {
    9245             :                 const auto eColorInterp =
    9246           6 :                     GDALGetColorInterpretationByName(pszValue);
    9247           6 :                 CPLFree(pszValue);
    9248           6 :                 return eColorInterp;
    9249             :             }
    9250             :         }
    9251             :     }
    9252          54 :     return GCI_Undefined;
    9253             : }
    9254             : 
    9255             : /************************************************************************/
    9256             : /*                    GDALDatasetFromArray::Create()                    */
    9257             : /************************************************************************/
    9258             : 
    9259         255 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    9260             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9261             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9262             : 
    9263             : {
    9264         255 :     const auto nDimCount(array->GetDimensionCount());
    9265         255 :     if (nDimCount == 0)
    9266             :     {
    9267           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9268             :                  "Unsupported number of dimensions");
    9269           1 :         return nullptr;
    9270             :     }
    9271         507 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9272         253 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9273             :     {
    9274           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9275             :                  "Only arrays with numeric data types "
    9276             :                  "can be exposed as classic GDALDataset");
    9277           1 :         return nullptr;
    9278             :     }
    9279         253 :     if (iXDim >= nDimCount ||
    9280         238 :         (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
    9281             :     {
    9282           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9283           6 :         return nullptr;
    9284             :     }
    9285         247 :     GUInt64 nTotalBands = 1;
    9286         247 :     const auto &dims(array->GetDimensions());
    9287         817 :     for (size_t i = 0; i < nDimCount; ++i)
    9288             :     {
    9289         571 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9290             :         {
    9291          91 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9292             :             {
    9293           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9294             :                          "Too many bands. Operate on a sliced view");
    9295           1 :                 return nullptr;
    9296             :             }
    9297          90 :             nTotalBands *= dims[i]->GetSize();
    9298             :         }
    9299             :     }
    9300             : 
    9301         492 :     std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9302         492 :     std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
    9303         816 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9304             :     {
    9305         570 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9306             :         {
    9307          90 :             oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9308          90 :             oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
    9309          90 :             ++j;
    9310             :         }
    9311             :     }
    9312             : 
    9313         246 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9314             : 
    9315             :     const char *pszBandMetadata =
    9316         246 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9317             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9318         492 :         nNewDimCount);
    9319         246 :     if (pszBandMetadata)
    9320             :     {
    9321          32 :         if (!poRootGroup)
    9322             :         {
    9323           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9324             :                      "Root group should be provided when BAND_METADATA is set");
    9325          24 :             return nullptr;
    9326             :         }
    9327          31 :         CPLJSONDocument oDoc;
    9328          31 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9329             :         {
    9330           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9331             :                      "Invalid JSON content for BAND_METADATA");
    9332           1 :             return nullptr;
    9333             :         }
    9334          30 :         auto oRoot = oDoc.GetRoot();
    9335          30 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9336             :         {
    9337           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9338             :                      "Value of BAND_METADATA should be an array");
    9339           1 :             return nullptr;
    9340             :         }
    9341             : 
    9342          29 :         auto oArray = oRoot.ToArray();
    9343          38 :         for (int j = 0; j < oArray.Size(); ++j)
    9344             :         {
    9345          30 :             const auto oJsonItem = oArray[j];
    9346          30 :             MetadataItem oItem;
    9347          30 :             size_t iExtraDimIdx = 0;
    9348             : 
    9349          60 :             const auto osBandArrayFullname = oJsonItem.GetString("array");
    9350          60 :             const auto osBandAttributeName = oJsonItem.GetString("attribute");
    9351           0 :             std::shared_ptr<GDALMDArray> poArray;
    9352           0 :             std::shared_ptr<GDALAttribute> poAttribute;
    9353          30 :             if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9354             :             {
    9355           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9356             :                          "BAND_METADATA[%d][\"array\"] or "
    9357             :                          "BAND_METADATA[%d][\"attribute\"] is missing",
    9358             :                          j, j);
    9359           1 :                 return nullptr;
    9360             :             }
    9361          48 :             else if (!osBandArrayFullname.empty() &&
    9362          19 :                      !osBandAttributeName.empty())
    9363             :             {
    9364           1 :                 CPLError(
    9365             :                     CE_Failure, CPLE_AppDefined,
    9366             :                     "BAND_METADATA[%d][\"array\"] and "
    9367             :                     "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
    9368             :                     j, j);
    9369           1 :                 return nullptr;
    9370             :             }
    9371          28 :             else if (!osBandArrayFullname.empty())
    9372             :             {
    9373             :                 poArray =
    9374          18 :                     poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9375          18 :                 if (!poArray)
    9376             :                 {
    9377           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9378             :                              "Array %s cannot be found",
    9379             :                              osBandArrayFullname.c_str());
    9380           3 :                     return nullptr;
    9381             :                 }
    9382          17 :                 if (poArray->GetDimensionCount() != 1)
    9383             :                 {
    9384           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9385             :                              "Array %s is not a 1D array",
    9386             :                              osBandArrayFullname.c_str());
    9387           1 :                     return nullptr;
    9388             :                 }
    9389             :                 const auto &osAuxArrayDimName =
    9390          16 :                     poArray->GetDimensions()[0]->GetName();
    9391             :                 auto oIter =
    9392          16 :                     oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9393          16 :                 if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9394             :                 {
    9395           1 :                     CPLError(
    9396             :                         CE_Failure, CPLE_AppDefined,
    9397             :                         "Dimension %s of array %s is not a non-X/Y dimension "
    9398             :                         "of array %s",
    9399             :                         osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9400           1 :                         array->GetName().c_str());
    9401           1 :                     return nullptr;
    9402             :                 }
    9403          15 :                 iExtraDimIdx = oIter->second;
    9404          15 :                 CPLAssert(iExtraDimIdx < nNewDimCount);
    9405             :             }
    9406             :             else
    9407             :             {
    9408          10 :                 CPLAssert(!osBandAttributeName.empty());
    9409          10 :                 poAttribute = !osBandAttributeName.empty() &&
    9410          10 :                                       osBandAttributeName[0] == '/'
    9411          24 :                                   ? poRootGroup->OpenAttributeFromFullname(
    9412             :                                         osBandAttributeName)
    9413          10 :                                   : array->GetAttribute(osBandAttributeName);
    9414          10 :                 if (!poAttribute)
    9415             :                 {
    9416           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9417             :                              "Attribute %s cannot be found",
    9418             :                              osBandAttributeName.c_str());
    9419           8 :                     return nullptr;
    9420             :                 }
    9421           8 :                 const auto aoAttrDims = poAttribute->GetDimensionsSize();
    9422           8 :                 if (aoAttrDims.size() != 1)
    9423             :                 {
    9424           4 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9425             :                              "Attribute %s is not a 1D array",
    9426             :                              osBandAttributeName.c_str());
    9427           4 :                     return nullptr;
    9428             :                 }
    9429           4 :                 bool found = false;
    9430           8 :                 for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
    9431             :                 {
    9432           5 :                     if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
    9433           5 :                             ->GetSize() == aoAttrDims[0])
    9434             :                     {
    9435           4 :                         if (found)
    9436             :                         {
    9437           2 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9438             :                                      "Several dimensions of %s have the same "
    9439             :                                      "size as attribute %s. Cannot infer which "
    9440             :                                      "one to bind to!",
    9441           1 :                                      array->GetName().c_str(),
    9442             :                                      osBandAttributeName.c_str());
    9443           1 :                             return nullptr;
    9444             :                         }
    9445           3 :                         found = true;
    9446           3 :                         iExtraDimIdx = iter.second;
    9447             :                     }
    9448             :                 }
    9449           3 :                 if (!found)
    9450             :                 {
    9451           2 :                     CPLError(
    9452             :                         CE_Failure, CPLE_AppDefined,
    9453             :                         "No dimension of %s has the same size as attribute %s",
    9454           1 :                         array->GetName().c_str(), osBandAttributeName.c_str());
    9455           1 :                     return nullptr;
    9456             :                 }
    9457             :             }
    9458             : 
    9459          17 :             oItem.osName = oJsonItem.GetString("item_name");
    9460          17 :             if (oItem.osName.empty())
    9461             :             {
    9462           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9463             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9464           1 :                 return nullptr;
    9465             :             }
    9466             : 
    9467          32 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9468             : 
    9469             :             // Check correctness of definition
    9470          16 :             bool bFirstNumericFormatter = true;
    9471          16 :             std::string osModDefinition;
    9472          16 :             bool bDefinitionUsesPctForG = false;
    9473          79 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9474             :             {
    9475          70 :                 if (osDefinition[k] == '%')
    9476             :                 {
    9477          15 :                     osModDefinition += osDefinition[k];
    9478          15 :                     if (k + 1 == osDefinition.size())
    9479             :                     {
    9480           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9481             :                                  "Value of "
    9482             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9483             :                                  "%s is invalid at offset %d",
    9484             :                                  j, osDefinition.c_str(), int(k));
    9485           1 :                         return nullptr;
    9486             :                     }
    9487          14 :                     ++k;
    9488          14 :                     if (osDefinition[k] == '%')
    9489             :                     {
    9490           1 :                         osModDefinition += osDefinition[k];
    9491           1 :                         continue;
    9492             :                     }
    9493          13 :                     if (!bFirstNumericFormatter)
    9494             :                     {
    9495           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9496             :                                  "Value of "
    9497             :                                  "BAND_METADATA[%d][\"item_value\"] = %s is "
    9498             :                                  "invalid at offset %d: %%[x][.y]f|g or %%s "
    9499             :                                  "formatters should be specified at most once",
    9500             :                                  j, osDefinition.c_str(), int(k));
    9501           1 :                         return nullptr;
    9502             :                     }
    9503          12 :                     bFirstNumericFormatter = false;
    9504          19 :                     for (; k < osDefinition.size(); ++k)
    9505             :                     {
    9506          19 :                         osModDefinition += osDefinition[k];
    9507          38 :                         if (!((osDefinition[k] >= '0' &&
    9508          16 :                                osDefinition[k] <= '9') ||
    9509          15 :                               osDefinition[k] == '.'))
    9510          12 :                             break;
    9511             :                     }
    9512          24 :                     if (k == osDefinition.size() ||
    9513          12 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9514           5 :                          osDefinition[k] != 's'))
    9515             :                     {
    9516           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9517             :                                  "Value of "
    9518             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9519             :                                  "%s is invalid at offset %d: only "
    9520             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9521             :                                  j, osDefinition.c_str(), int(k));
    9522           1 :                         return nullptr;
    9523             :                     }
    9524          11 :                     bDefinitionUsesPctForG =
    9525          11 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9526          11 :                     if (bDefinitionUsesPctForG)
    9527             :                     {
    9528          12 :                         if (poArray &&
    9529          12 :                             poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
    9530             :                         {
    9531           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9532             :                                      "Data type of %s array is not numeric",
    9533           1 :                                      poArray->GetName().c_str());
    9534           1 :                             return nullptr;
    9535             :                         }
    9536           8 :                         else if (poAttribute &&
    9537           2 :                                  poAttribute->GetDataType().GetClass() !=
    9538           6 :                                      GEDTC_NUMERIC)
    9539             :                         {
    9540           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9541             :                                      "Data type of %s attribute is not numeric",
    9542           0 :                                      poAttribute->GetFullName().c_str());
    9543           0 :                             return nullptr;
    9544             :                         }
    9545             :                     }
    9546             :                 }
    9547          62 :                 else if (osDefinition[k] == '$' &&
    9548          62 :                          k + 1 < osDefinition.size() &&
    9549           7 :                          osDefinition[k + 1] == '{')
    9550             :                 {
    9551           7 :                     const auto nPos = osDefinition.find('}', k);
    9552           7 :                     if (nPos == std::string::npos)
    9553             :                     {
    9554           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9555             :                                  "Value of "
    9556             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9557             :                                  "%s is invalid at offset %d",
    9558             :                                  j, osDefinition.c_str(), int(k));
    9559           3 :                         return nullptr;
    9560             :                     }
    9561             :                     const auto osAttrName =
    9562           6 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9563           0 :                     std::shared_ptr<GDALAttribute> poAttr;
    9564           6 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
    9565             :                     {
    9566           4 :                         poAttr = poArray->GetAttribute(osAttrName);
    9567           4 :                         if (!poAttr)
    9568             :                         {
    9569           1 :                             CPLError(
    9570             :                                 CE_Failure, CPLE_AppDefined,
    9571             :                                 "Value of "
    9572             :                                 "BAND_METADATA[%d][\"item_value\"] = "
    9573             :                                 "%s is invalid: %s is not an attribute of %s",
    9574             :                                 j, osDefinition.c_str(), osAttrName.c_str(),
    9575           1 :                                 poArray->GetName().c_str());
    9576           1 :                             return nullptr;
    9577             :                         }
    9578             :                     }
    9579             :                     else
    9580             :                     {
    9581             :                         poAttr =
    9582           2 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
    9583           2 :                         if (!poAttr)
    9584             :                         {
    9585           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9586             :                                      "Value of "
    9587             :                                      "BAND_METADATA[%d][\"item_value\"] = "
    9588             :                                      "%s is invalid: %s is not an attribute",
    9589             :                                      j, osDefinition.c_str(),
    9590             :                                      osAttrName.c_str());
    9591           1 :                             return nullptr;
    9592             :                         }
    9593             :                     }
    9594           4 :                     k = nPos;
    9595           4 :                     const char *pszValue = poAttr->ReadAsString();
    9596           4 :                     if (!pszValue)
    9597             :                     {
    9598           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9599             :                                  "Cannot get value of attribute %s as a "
    9600             :                                  "string",
    9601             :                                  osAttrName.c_str());
    9602           0 :                         return nullptr;
    9603             :                     }
    9604           4 :                     osModDefinition += pszValue;
    9605             :                 }
    9606             :                 else
    9607             :                 {
    9608          48 :                     osModDefinition += osDefinition[k];
    9609             :                 }
    9610             :             }
    9611             : 
    9612           9 :             if (poArray)
    9613           8 :                 oItem.poArray = std::move(poArray);
    9614             :             else
    9615           1 :                 oItem.poArray = std::move(poAttribute);
    9616           9 :             oItem.osDefinition = std::move(osModDefinition);
    9617           9 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9618             : 
    9619           9 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9620           9 :                 std::move(oItem));
    9621             :         }
    9622             :     }
    9623             : 
    9624         444 :     std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
    9625             :     const char *pszBandImageryMetadata =
    9626         222 :         CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
    9627         222 :     if (pszBandImageryMetadata)
    9628             :     {
    9629          20 :         if (!poRootGroup)
    9630             :         {
    9631           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9632             :                      "Root group should be provided when BAND_IMAGERY_METADATA "
    9633             :                      "is set");
    9634          17 :             return nullptr;
    9635             :         }
    9636          19 :         CPLJSONDocument oDoc;
    9637          19 :         if (!oDoc.LoadMemory(pszBandImageryMetadata))
    9638             :         {
    9639           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9640             :                      "Invalid JSON content for BAND_IMAGERY_METADATA");
    9641           1 :             return nullptr;
    9642             :         }
    9643          18 :         auto oRoot = oDoc.GetRoot();
    9644          18 :         if (oRoot.GetType() != CPLJSONObject::Type::Object)
    9645             :         {
    9646           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9647             :                      "Value of BAND_IMAGERY_METADATA should be an object");
    9648           1 :             return nullptr;
    9649             :         }
    9650          21 :         for (const auto &oJsonItem : oRoot.GetChildren())
    9651             :         {
    9652          38 :             if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
    9653          20 :                 oJsonItem.GetName() == "FWHM_UM")
    9654             :             {
    9655          34 :                 const auto osBandArrayFullname = oJsonItem.GetString("array");
    9656             :                 const auto osBandAttributeName =
    9657          34 :                     oJsonItem.GetString("attribute");
    9658           0 :                 std::shared_ptr<GDALMDArray> poArray;
    9659           0 :                 std::shared_ptr<GDALAttribute> poAttribute;
    9660          17 :                 size_t iExtraDimIdx = 0;
    9661          17 :                 if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9662             :                 {
    9663           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9664             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
    9665             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
    9666             :                              "missing",
    9667           2 :                              oJsonItem.GetName().c_str(),
    9668           2 :                              oJsonItem.GetName().c_str());
    9669           1 :                     return nullptr;
    9670             :                 }
    9671          25 :                 else if (!osBandArrayFullname.empty() &&
    9672           9 :                          !osBandAttributeName.empty())
    9673             :                 {
    9674           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9675             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
    9676             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
    9677             :                              "mutually exclusive",
    9678           2 :                              oJsonItem.GetName().c_str(),
    9679           2 :                              oJsonItem.GetName().c_str());
    9680           1 :                     return nullptr;
    9681             :                 }
    9682          15 :                 else if (!osBandArrayFullname.empty())
    9683             :                 {
    9684          16 :                     poArray = poRootGroup->OpenMDArrayFromFullname(
    9685           8 :                         osBandArrayFullname);
    9686           8 :                     if (!poArray)
    9687             :                     {
    9688           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9689             :                                  "Array %s cannot be found",
    9690             :                                  osBandArrayFullname.c_str());
    9691           3 :                         return nullptr;
    9692             :                     }
    9693           7 :                     if (poArray->GetDimensionCount() != 1)
    9694             :                     {
    9695           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9696             :                                  "Array %s is not a 1D array",
    9697             :                                  osBandArrayFullname.c_str());
    9698           1 :                         return nullptr;
    9699             :                     }
    9700             :                     const auto &osAuxArrayDimName =
    9701           6 :                         poArray->GetDimensions()[0]->GetName();
    9702             :                     auto oIter =
    9703           6 :                         oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9704           6 :                     if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9705             :                     {
    9706           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9707             :                                  "Dimension \"%s\" of array \"%s\" is not a "
    9708             :                                  "non-X/Y dimension of array \"%s\"",
    9709             :                                  osAuxArrayDimName.c_str(),
    9710             :                                  osBandArrayFullname.c_str(),
    9711           1 :                                  array->GetName().c_str());
    9712           1 :                         return nullptr;
    9713             :                     }
    9714           5 :                     iExtraDimIdx = oIter->second;
    9715           5 :                     CPLAssert(iExtraDimIdx < nNewDimCount);
    9716             :                 }
    9717             :                 else
    9718             :                 {
    9719             :                     poAttribute =
    9720           7 :                         !osBandAttributeName.empty() &&
    9721           7 :                                 osBandAttributeName[0] == '/'
    9722          16 :                             ? poRootGroup->OpenAttributeFromFullname(
    9723             :                                   osBandAttributeName)
    9724           7 :                             : array->GetAttribute(osBandAttributeName);
    9725           7 :                     if (!poAttribute)
    9726             :                     {
    9727           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9728             :                                  "Attribute %s cannot be found",
    9729             :                                  osBandAttributeName.c_str());
    9730           6 :                         return nullptr;
    9731             :                     }
    9732           5 :                     const auto aoAttrDims = poAttribute->GetDimensionsSize();
    9733           5 :                     if (aoAttrDims.size() != 1)
    9734             :                     {
    9735           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9736             :                                  "Attribute %s is not a 1D array",
    9737             :                                  osBandAttributeName.c_str());
    9738           2 :                         return nullptr;
    9739             :                     }
    9740           3 :                     bool found = false;
    9741           6 :                     for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
    9742             :                     {
    9743           4 :                         if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
    9744           4 :                                 ->GetSize() == aoAttrDims[0])
    9745             :                         {
    9746           3 :                             if (found)
    9747             :                             {
    9748           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    9749             :                                          "Several dimensions of %s have the "
    9750             :                                          "same size as attribute %s. Cannot "
    9751             :                                          "infer which one to bind to!",
    9752           1 :                                          array->GetName().c_str(),
    9753             :                                          osBandAttributeName.c_str());
    9754           1 :                                 return nullptr;
    9755             :                             }
    9756           2 :                             found = true;
    9757           2 :                             iExtraDimIdx = iter.second;
    9758             :                         }
    9759             :                     }
    9760           2 :                     if (!found)
    9761             :                     {
    9762           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9763             :                                  "No dimension of %s has the same size as "
    9764             :                                  "attribute %s",
    9765           1 :                                  array->GetName().c_str(),
    9766             :                                  osBandAttributeName.c_str());
    9767           1 :                         return nullptr;
    9768             :                     }
    9769             :                 }
    9770             : 
    9771          12 :                 std::string osUnit = oJsonItem.GetString("unit", "um");
    9772           6 :                 if (STARTS_WITH(osUnit.c_str(), "${"))
    9773             :                 {
    9774           4 :                     if (osUnit.back() != '}')
    9775             :                     {
    9776           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9777             :                                  "Value of "
    9778             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9779             :                                  "%s is invalid",
    9780           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str());
    9781           2 :                         return nullptr;
    9782             :                     }
    9783           3 :                     const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
    9784           0 :                     std::shared_ptr<GDALAttribute> poAttr;
    9785           3 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
    9786             :                     {
    9787           2 :                         poAttr = poArray->GetAttribute(osAttrName);
    9788           2 :                         if (!poAttr)
    9789             :                         {
    9790           2 :                             CPLError(
    9791             :                                 CE_Failure, CPLE_AppDefined,
    9792             :                                 "Value of "
    9793             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9794             :                                 "%s is invalid: %s is not an attribute of %s",
    9795           2 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
    9796             :                                 osAttrName.c_str(),
    9797             :                                 osBandArrayFullname.c_str());
    9798           1 :                             return nullptr;
    9799             :                         }
    9800             :                     }
    9801             :                     else
    9802             :                     {
    9803             :                         poAttr =
    9804           1 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
    9805           1 :                         if (!poAttr)
    9806             :                         {
    9807           0 :                             CPLError(
    9808             :                                 CE_Failure, CPLE_AppDefined,
    9809             :                                 "Value of "
    9810             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9811             :                                 "%s is invalid: %s is not an attribute",
    9812           0 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
    9813             :                                 osAttrName.c_str());
    9814           0 :                             return nullptr;
    9815             :                         }
    9816             :                     }
    9817             : 
    9818           2 :                     const char *pszValue = poAttr->ReadAsString();
    9819           2 :                     if (!pszValue)
    9820             :                     {
    9821           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9822             :                                  "Cannot get value of attribute %s of %s as a "
    9823             :                                  "string",
    9824             :                                  osAttrName.c_str(),
    9825             :                                  osBandArrayFullname.c_str());
    9826           0 :                         return nullptr;
    9827             :                     }
    9828           2 :                     osUnit = pszValue;
    9829             :                 }
    9830           4 :                 double dfConvToUM = 1.0;
    9831          10 :                 if (osUnit == "nm" || osUnit == "nanometre" ||
    9832          13 :                     osUnit == "nanometres" || osUnit == "nanometer" ||
    9833           3 :                     osUnit == "nanometers")
    9834             :                 {
    9835           1 :                     dfConvToUM = 1e-3;
    9836             :                 }
    9837           5 :                 else if (!(osUnit == "um" || osUnit == "micrometre" ||
    9838           2 :                            osUnit == "micrometres" || osUnit == "micrometer" ||
    9839           1 :                            osUnit == "micrometers"))
    9840             :                 {
    9841           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9842             :                              "Unhandled value for "
    9843             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
    9844           2 :                              oJsonItem.GetName().c_str(), osUnit.c_str());
    9845           1 :                     return nullptr;
    9846             :                 }
    9847             : 
    9848           3 :                 BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
    9849             : 
    9850           3 :                 std::shared_ptr<GDALAbstractMDArray> abstractArray;
    9851           3 :                 if (poArray)
    9852           2 :                     abstractArray = std::move(poArray);
    9853             :                 else
    9854           1 :                     abstractArray = std::move(poAttribute);
    9855           3 :                 if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
    9856             :                 {
    9857           2 :                     item.poCentralWavelengthArray = std::move(abstractArray);
    9858           2 :                     item.dfCentralWavelengthToMicrometer = dfConvToUM;
    9859             :                 }
    9860             :                 else
    9861             :                 {
    9862           1 :                     item.poFWHMArray = std::move(abstractArray);
    9863           1 :                     item.dfFWHMToMicrometer = dfConvToUM;
    9864             :                 }
    9865             :             }
    9866             :             else
    9867             :             {
    9868           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    9869             :                          "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
    9870           2 :                          oJsonItem.GetName().c_str());
    9871             :             }
    9872             :         }
    9873             :     }
    9874             : 
    9875         410 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9876             : 
    9877         205 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9878             : 
    9879         205 :     poDS->nRasterYSize =
    9880         205 :         nDimCount < 2 ? 1
    9881         193 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9882         193 :                                                   dims[iYDim]->GetSize()));
    9883         410 :     poDS->nRasterXSize = static_cast<int>(
    9884         205 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9885             : 
    9886         410 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9887         410 :     std::vector<GUInt64> anStackIters(nDimCount);
    9888         410 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9889         650 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9890             :     {
    9891         445 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9892             :         {
    9893          47 :             anMapNewToOld[j] = i;
    9894          47 :             j++;
    9895             :         }
    9896             :     }
    9897             : 
    9898         205 :     poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
    9899             : 
    9900         410 :     const auto attrs(array->GetAttributes());
    9901         314 :     for (const auto &attr : attrs)
    9902             :     {
    9903         109 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9904             :         {
    9905         200 :             auto stringArray = attr->ReadAsStringArray();
    9906         200 :             std::string val;
    9907         100 :             if (stringArray.size() > 1)
    9908             :             {
    9909          44 :                 val += '{';
    9910             :             }
    9911         448 :             for (int i = 0; i < stringArray.size(); ++i)
    9912             :             {
    9913         348 :                 if (i > 0)
    9914         248 :                     val += ',';
    9915         348 :                 val += stringArray[i];
    9916             :             }
    9917         100 :             if (stringArray.size() > 1)
    9918             :             {
    9919          44 :                 val += '}';
    9920             :             }
    9921         100 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9922             :         }
    9923             :     }
    9924             : 
    9925         205 :     const char *pszDelay = CSLFetchNameValueDef(
    9926             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9927             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9928             :     const double dfDelay =
    9929         205 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9930         205 :     const auto nStartTime = time(nullptr);
    9931         205 :     bool bHasWarned = false;
    9932             :     // Instantiate bands by iterating over non-XY variables
    9933         205 :     size_t iDim = 0;
    9934         205 :     int nCurBand = 1;
    9935         328 : lbl_next_depth:
    9936         328 :     if (iDim < nNewDimCount)
    9937             :     {
    9938          49 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
    9939          49 :         anOtherDimCoord[iDim] = 0;
    9940             :         while (true)
    9941             :         {
    9942         123 :             ++iDim;
    9943         123 :             goto lbl_next_depth;
    9944         123 :         lbl_return_to_caller:
    9945         123 :             --iDim;
    9946         123 :             --anStackIters[iDim];
    9947         123 :             if (anStackIters[iDim] == 0)
    9948          49 :                 break;
    9949          74 :             ++anOtherDimCoord[iDim];
    9950             :         }
    9951             :     }
    9952             :     else
    9953             :     {
    9954         558 :         poDS->SetBand(nCurBand,
    9955             :                       new GDALRasterBandFromArray(
    9956         279 :                           poDS.get(), anOtherDimCoord,
    9957             :                           aoBandParameterMetadataItems, aoBandImageryMetadata,
    9958         279 :                           dfDelay, nStartTime, bHasWarned));
    9959         279 :         ++nCurBand;
    9960             :     }
    9961         328 :     if (iDim > 0)
    9962         123 :         goto lbl_return_to_caller;
    9963             : 
    9964         205 :     if (!array->GetFilename().empty())
    9965             :     {
    9966         181 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
    9967             :         std::string osDerivedDatasetName(
    9968             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
    9969         362 :                        int(iYDim), array->GetFullName().c_str()));
    9970         181 :         if (!array->GetContext().empty())
    9971             :         {
    9972           2 :             osDerivedDatasetName += " with context ";
    9973           2 :             osDerivedDatasetName += array->GetContext();
    9974             :         }
    9975         181 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
    9976         181 :         poDS->TryLoadXML();
    9977             : 
    9978           2 :         for (const auto &[pszKey, pszValue] :
    9979             :              cpl::IterateNameValue(static_cast<CSLConstList>(
    9980         183 :                  poDS->GDALPamDataset::GetMetadata())))
    9981             :         {
    9982           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
    9983             :         }
    9984             :     }
    9985             : 
    9986         205 :     return poDS.release();
    9987             : }
    9988             : 
    9989             : /************************************************************************/
    9990             : /*                          AsClassicDataset()                         */
    9991             : /************************************************************************/
    9992             : 
    9993             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
    9994             :  *
    9995             :  * In the case of > 2D arrays, additional dimensions will be represented as
    9996             :  * raster bands.
    9997             :  *
    9998             :  * The "reverse" method is GDALRasterBand::AsMDArray().
    9999             :  *
   10000             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
   10001             :  *
   10002             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   10003             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   10004             :  *              Ignored if the dimension count is 1.
   10005             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
   10006             :  *                    and BAND_IMAGERY_METADATA option.
   10007             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
   10008             :  *                     nullptr. Current supported options are:
   10009             :  *                     <ul>
   10010             :  *                     <li>BAND_METADATA: JSON serialized array defining which
   10011             :  *                         arrays of the poRootGroup, indexed by non-X and Y
   10012             :  *                         dimensions, should be mapped as band metadata items.
   10013             :  *                         Each array item should be an object with the
   10014             :  *                         following members:
   10015             :  *                         - "array": full name of a band parameter array.
   10016             :  *                           Such array must be a one
   10017             :  *                           dimensional array, and its dimension must be one of
   10018             :  *                           the dimensions of the array on which the method is
   10019             :  *                           called (excluding the X and Y dimensons).
   10020             :  *                         - "attribute": name relative to *this array or full
   10021             :  *                           name of a single dimension numeric array whose size
   10022             :  *                           must be one of the dimensions of *this array
   10023             :  *                           (excluding the X and Y dimensons).
   10024             :  *                           "array" and "attribute" are mutually exclusive.
   10025             :  *                         - "item_name": band metadata item name
   10026             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
   10027             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
   10028             :  *                           used to format the corresponding value of the
   10029             :  *                           parameter array. The percentage character should be
   10030             :  *                           repeated: "%%"
   10031             :  *                           "${attribute_name}" can also be used to include the
   10032             :  *                           value of an attribute for "array" when set and if
   10033             :  *                           not starting with '/'. Otherwise if starting with
   10034             :  *                           '/', it is the full path to the attribute.
   10035             :  *
   10036             :  *                           If "item_value" is not provided, a default formatting
   10037             :  *                           of the value will be applied.
   10038             :  *
   10039             :  *                         Example:
   10040             :  *                         [
   10041             :  *                            {
   10042             :  *                              "array": "/sensor_band_parameters/wavelengths",
   10043             :  *                              "item_name": "WAVELENGTH",
   10044             :  *                              "item_value": "%.1f ${units}"
   10045             :  *                            },
   10046             :  *                            {
   10047             :  *                              "array": "/sensor_band_parameters/fwhm",
   10048             :  *                              "item_name": "FWHM"
   10049             :  *                            },
   10050             :  *                            {
   10051             :  *                              "array": "/sensor_band_parameters/fwhm",
   10052             :  *                              "item_name": "FWHM_UNIT",
   10053             :  *                              "item_value": "${units}"
   10054             :  *                            }
   10055             :  *                         ]
   10056             :  *
   10057             :  *                         Example for Planet Labs Tanager radiance products:
   10058             :  *                         [
   10059             :  *                            {
   10060             :  *                              "attribute": "center_wavelengths",
   10061             :  *                              "item_name": "WAVELENGTH",
   10062             :  *                              "item_value": "%.1f ${center_wavelengths_units}"
   10063             :  *                            }
   10064             :  *                         ]
   10065             :  *
   10066             :  *                     </li>
   10067             :  *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
   10068             :  *                         JSON serialized object defining which arrays of the
   10069             :  *                         poRootGroup, indexed by non-X and Y dimensions,
   10070             :  *                         should be mapped as band metadata items in the
   10071             :  *                         band IMAGERY domain.
   10072             :  *                         The object currently accepts 2 members:
   10073             :  *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
   10074             :  *                           micrometers.
   10075             :  *                         - "FWHM_UM": Full-width half-maximum
   10076             :  *                           in micrometers.
   10077             :  *                         The value of each member should be an object with the
   10078             :  *                         following members:
   10079             :  *                         - "array": full name of a band parameter array.
   10080             :  *                           Such array must be a one dimensional array, and its
   10081             :  *                           dimension must be one of the dimensions of the
   10082             :  *                           array on which the method is called
   10083             :  *                           (excluding the X and Y dimensons).
   10084             :  *                         - "attribute": name relative to *this array or full
   10085             :  *                           name of a single dimension numeric array whose size
   10086             :  *                           must be one of the dimensions of *this array
   10087             :  *                           (excluding the X and Y dimensons).
   10088             :  *                           "array" and "attribute" are mutually exclusive,
   10089             :  *                           and one of them is required.
   10090             :  *                         - "unit": (optional) unit of the values pointed in
   10091             :  *                           the above array.
   10092             :  *                           Can be a literal string or a string of the form
   10093             :  *                           "${attribute_name}" to point to an attribute for
   10094             :  *                           "array" when set and if no starting
   10095             :  *                           with '/'. Otherwise if starting with '/', it is
   10096             :  *                           the full path to the attribute.
   10097             :  *                           Accepted values are "um", "micrometer"
   10098             :  *                           (with UK vs US spelling, singular or plural), "nm",
   10099             :  *                           "nanometer" (with UK vs US spelling, singular or
   10100             :  *                           plural)
   10101             :  *                           If not provided, micrometer is assumed.
   10102             :  *
   10103             :  *                         Example for EMIT datasets:
   10104             :  *                         {
   10105             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10106             :  *                                "array": "/sensor_band_parameters/wavelengths",
   10107             :  *                                "unit": "${units}"
   10108             :  *                            },
   10109             :  *                            "FWHM_UM": {
   10110             :  *                                "array": "/sensor_band_parameters/fwhm",
   10111             :  *                                "unit": "${units}"
   10112             :  *                            }
   10113             :  *                         }
   10114             :  *
   10115             :  *                         Example for Planet Labs Tanager radiance products:
   10116             :  *                         {
   10117             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10118             :  *                              "attribute": "center_wavelengths",
   10119             :  *                              "unit": "${center_wavelengths_units}"
   10120             :  *                            },
   10121             :  *                            "FWHM_UM": {
   10122             :  *                              "attribute": "fwhm",
   10123             :  *                              "unit": "${fwhm_units}"
   10124             :  *                            }
   10125             :  *                         }
   10126             :  *
   10127             :  *                     </li>
   10128             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
   10129             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
   10130             :  *                         metadata items from the indexing variable of the
   10131             :  *                         dimensions.
   10132             :  *                         Default value is 5. 'unlimited' can be used to mean
   10133             :  *                         unlimited delay. Can also be defined globally with
   10134             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
   10135             :  *                         option.</li>
   10136             :  *                     </ul>
   10137             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   10138             :  */
   10139             : GDALDataset *
   10140         255 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
   10141             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
   10142             :                               CSLConstList papszOptions) const
   10143             : {
   10144         510 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
   10145         255 :     if (!self)
   10146             :     {
   10147           0 :         CPLError(CE_Failure, CPLE_AppDefined,
   10148             :                  "Driver implementation issue: m_pSelf not set !");
   10149           0 :         return nullptr;
   10150             :     }
   10151         255 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
   10152         255 :                                         papszOptions);
   10153             : }
   10154             : 
   10155             : /************************************************************************/
   10156             : /*                           GetStatistics()                            */
   10157             : /************************************************************************/
   10158             : 
   10159             : /**
   10160             :  * \brief Fetch statistics.
   10161             :  *
   10162             :  * Returns the minimum, maximum, mean and standard deviation of all
   10163             :  * pixel values in this array.
   10164             :  *
   10165             :  * If bForce is FALSE results will only be returned if it can be done
   10166             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
   10167             :  * results cannot be returned efficiently, the method will return CE_Warning
   10168             :  * but no warning will have been issued.   This is a non-standard use of
   10169             :  * the CE_Warning return value to indicate "nothing done".
   10170             :  *
   10171             :  * When cached statistics are not available, and bForce is TRUE,
   10172             :  * ComputeStatistics() is called.
   10173             :  *
   10174             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
   10175             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
   10176             :  * after the first request.
   10177             :  *
   10178             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10179             :  *
   10180             :  * This method is the same as the C function GDALMDArrayGetStatistics().
   10181             :  *
   10182             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10183             :  * if statistics on the whole array are wished, or to false if a subset of it
   10184             :  * may be used.
   10185             :  *
   10186             :  * @param bForce If false statistics will only be returned if it can
   10187             :  * be done without rescanning the image.
   10188             :  *
   10189             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10190             :  *
   10191             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10192             :  *
   10193             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10194             :  *
   10195             :  * @param pdfStdDev Location into which to load image standard deviation
   10196             :  * (may be NULL).
   10197             :  *
   10198             :  * @param pnValidCount Number of samples whose value is different from the
   10199             :  * nodata value. (may be NULL)
   10200             :  *
   10201             :  * @param pfnProgress a function to call to report progress, or NULL.
   10202             :  *
   10203             :  * @param pProgressData application data to pass to the progress function.
   10204             :  *
   10205             :  * @return CE_None on success, CE_Warning if no values returned,
   10206             :  * CE_Failure if an error occurs.
   10207             :  *
   10208             :  * @since GDAL 3.2
   10209             :  */
   10210             : 
   10211          10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
   10212             :                                   double *pdfMax, double *pdfMean,
   10213             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
   10214             :                                   GDALProgressFunc pfnProgress,
   10215             :                                   void *pProgressData)
   10216             : {
   10217          10 :     if (!bForce)
   10218           1 :         return CE_Warning;
   10219             : 
   10220          18 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
   10221           9 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
   10222           9 :                ? CE_None
   10223           9 :                : CE_Failure;
   10224             : }
   10225             : 
   10226             : /************************************************************************/
   10227             : /*                         ComputeStatistics()                          */
   10228             : /************************************************************************/
   10229             : 
   10230             : /**
   10231             :  * \brief Compute statistics.
   10232             :  *
   10233             :  * Returns the minimum, maximum, mean and standard deviation of all
   10234             :  * pixel values in this array.
   10235             :  *
   10236             :  * Pixels taken into account in statistics are those whose mask value
   10237             :  * (as determined by GetMask()) is non-zero.
   10238             :  *
   10239             :  * Once computed, the statistics will generally be "set" back on the
   10240             :  * owing dataset.
   10241             :  *
   10242             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10243             :  *
   10244             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
   10245             :  * and GDALMDArrayComputeStatisticsEx().
   10246             :  *
   10247             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10248             :  * if statistics on the whole array are wished, or to false if a subset of it
   10249             :  * may be used.
   10250             :  *
   10251             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10252             :  *
   10253             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10254             :  *
   10255             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10256             :  *
   10257             :  * @param pdfStdDev Location into which to load image standard deviation
   10258             :  * (may be NULL).
   10259             :  *
   10260             :  * @param pnValidCount Number of samples whose value is different from the
   10261             :  * nodata value. (may be NULL)
   10262             :  *
   10263             :  * @param pfnProgress a function to call to report progress, or NULL.
   10264             :  *
   10265             :  * @param pProgressData application data to pass to the progress function.
   10266             :  *
   10267             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
   10268             :  *                     Options are driver specific. For now the netCDF and Zarr
   10269             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
   10270             :  *                     to add or update the actual_range attribute with the
   10271             :  *                     computed min/max, only if done on the full array, in non
   10272             :  *                     approximate mode, and the dataset is opened in update
   10273             :  *                     mode.
   10274             :  *
   10275             :  * @return true on success
   10276             :  *
   10277             :  * @since GDAL 3.2
   10278             :  */
   10279             : 
   10280          13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
   10281             :                                     double *pdfMax, double *pdfMean,
   10282             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
   10283             :                                     GDALProgressFunc pfnProgress,
   10284             :                                     void *pProgressData,
   10285             :                                     CSLConstList papszOptions)
   10286             : {
   10287             :     struct StatsPerChunkType
   10288             :     {
   10289             :         const GDALMDArray *array = nullptr;
   10290             :         std::shared_ptr<GDALMDArray> poMask{};
   10291             :         double dfMin = cpl::NumericLimits<double>::max();
   10292             :         double dfMax = -cpl::NumericLimits<double>::max();
   10293             :         double dfMean = 0.0;
   10294             :         double dfM2 = 0.0;
   10295             :         GUInt64 nValidCount = 0;
   10296             :         std::vector<GByte> abyData{};
   10297             :         std::vector<double> adfData{};
   10298             :         std::vector<GByte> abyMaskData{};
   10299             :         GDALProgressFunc pfnProgress = nullptr;
   10300             :         void *pProgressData = nullptr;
   10301             :     };
   10302             : 
   10303          13 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
   10304             :                                  const GUInt64 *chunkArrayStartIdx,
   10305             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
   10306             :                                  GUInt64 nChunkCount, void *pUserData)
   10307             :     {
   10308          13 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
   10309          13 :         const GDALMDArray *array = data->array;
   10310          13 :         const GDALMDArray *poMask = data->poMask.get();
   10311          13 :         const size_t nDims = array->GetDimensionCount();
   10312          13 :         size_t nVals = 1;
   10313          34 :         for (size_t i = 0; i < nDims; i++)
   10314          21 :             nVals *= chunkCount[i];
   10315             : 
   10316             :         // Get mask
   10317          13 :         data->abyMaskData.resize(nVals);
   10318          13 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10319          13 :                            poMask->GetDataType(), &data->abyMaskData[0])))
   10320             :         {
   10321           0 :             return false;
   10322             :         }
   10323             : 
   10324             :         // Get data
   10325          13 :         const auto &oType = array->GetDataType();
   10326          13 :         if (oType.GetNumericDataType() == GDT_Float64)
   10327             :         {
   10328           6 :             data->adfData.resize(nVals);
   10329           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10330           6 :                              oType, &data->adfData[0]))
   10331             :             {
   10332           0 :                 return false;
   10333             :             }
   10334             :         }
   10335             :         else
   10336             :         {
   10337           7 :             data->abyData.resize(nVals * oType.GetSize());
   10338           7 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10339           7 :                              oType, &data->abyData[0]))
   10340             :             {
   10341           0 :                 return false;
   10342             :             }
   10343           7 :             data->adfData.resize(nVals);
   10344           7 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
   10345           7 :                             static_cast<int>(oType.GetSize()),
   10346           7 :                             &data->adfData[0], GDT_Float64,
   10347             :                             static_cast<int>(sizeof(double)),
   10348             :                             static_cast<GPtrDiff_t>(nVals));
   10349             :         }
   10350         912 :         for (size_t i = 0; i < nVals; i++)
   10351             :         {
   10352         899 :             if (data->abyMaskData[i])
   10353             :             {
   10354         894 :                 const double dfValue = data->adfData[i];
   10355         894 :                 data->dfMin = std::min(data->dfMin, dfValue);
   10356         894 :                 data->dfMax = std::max(data->dfMax, dfValue);
   10357         894 :                 data->nValidCount++;
   10358         894 :                 const double dfDelta = dfValue - data->dfMean;
   10359         894 :                 data->dfMean += dfDelta / data->nValidCount;
   10360         894 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
   10361             :             }
   10362             :         }
   10363          13 :         if (data->pfnProgress &&
   10364           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
   10365             :                                "", data->pProgressData))
   10366             :         {
   10367           0 :             return false;
   10368             :         }
   10369          13 :         return true;
   10370             :     };
   10371             : 
   10372          13 :     const auto &oType = GetDataType();
   10373          26 :     if (oType.GetClass() != GEDTC_NUMERIC ||
   10374          13 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
   10375             :     {
   10376           0 :         CPLError(
   10377             :             CE_Failure, CPLE_NotSupported,
   10378             :             "Statistics can only be computed on non-complex numeric data type");
   10379           0 :         return false;
   10380             :     }
   10381             : 
   10382          13 :     const size_t nDims = GetDimensionCount();
   10383          26 :     std::vector<GUInt64> arrayStartIdx(nDims);
   10384          26 :     std::vector<GUInt64> count(nDims);
   10385          13 :     const auto &poDims = GetDimensions();
   10386          34 :     for (size_t i = 0; i < nDims; i++)
   10387             :     {
   10388          21 :         count[i] = poDims[i]->GetSize();
   10389             :     }
   10390          13 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
   10391             :     const size_t nMaxChunkSize =
   10392             :         pszSwathSize
   10393          13 :             ? static_cast<size_t>(
   10394           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10395           0 :                            CPLAtoGIntBig(pszSwathSize)))
   10396             :             : static_cast<size_t>(
   10397          13 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10398          13 :                            GDALGetCacheMax64() / 4));
   10399          26 :     StatsPerChunkType sData;
   10400          13 :     sData.array = this;
   10401          13 :     sData.poMask = GetMask(nullptr);
   10402          13 :     if (sData.poMask == nullptr)
   10403             :     {
   10404           0 :         return false;
   10405             :     }
   10406          13 :     sData.pfnProgress = pfnProgress;
   10407          13 :     sData.pProgressData = pProgressData;
   10408          13 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
   10409          26 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
   10410          13 :                          PerChunkFunc, &sData))
   10411             :     {
   10412           0 :         return false;
   10413             :     }
   10414             : 
   10415          13 :     if (pdfMin)
   10416          13 :         *pdfMin = sData.dfMin;
   10417             : 
   10418          13 :     if (pdfMax)
   10419          13 :         *pdfMax = sData.dfMax;
   10420             : 
   10421          13 :     if (pdfMean)
   10422          11 :         *pdfMean = sData.dfMean;
   10423             : 
   10424             :     const double dfStdDev =
   10425          13 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
   10426          13 :     if (pdfStdDev)
   10427          11 :         *pdfStdDev = dfStdDev;
   10428             : 
   10429          13 :     if (pnValidCount)
   10430          11 :         *pnValidCount = sData.nValidCount;
   10431             : 
   10432          13 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
   10433          13 :                   sData.nValidCount, papszOptions);
   10434             : 
   10435          13 :     return true;
   10436             : }
   10437             : 
   10438             : /************************************************************************/
   10439             : /*                            SetStatistics()                           */
   10440             : /************************************************************************/
   10441             : //! @cond Doxygen_Suppress
   10442           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
   10443             :                                 double /* dfMax */, double /* dfMean */,
   10444             :                                 double /* dfStdDev */,
   10445             :                                 GUInt64 /* nValidCount */,
   10446             :                                 CSLConstList /* papszOptions */)
   10447             : {
   10448           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
   10449           5 :     return false;
   10450             : }
   10451             : 
   10452             : //! @endcond
   10453             : 
   10454             : /************************************************************************/
   10455             : /*                           ClearStatistics()                          */
   10456             : /************************************************************************/
   10457             : 
   10458             : /**
   10459             :  * \brief Clear statistics.
   10460             :  *
   10461             :  * @since GDAL 3.4
   10462             :  */
   10463           0 : void GDALMDArray::ClearStatistics()
   10464             : {
   10465           0 : }
   10466             : 
   10467             : /************************************************************************/
   10468             : /*                      GetCoordinateVariables()                        */
   10469             : /************************************************************************/
   10470             : 
   10471             : /**
   10472             :  * \brief Return coordinate variables.
   10473             :  *
   10474             :  * Coordinate variables are an alternate way of indexing an array that can
   10475             :  * be sometimes used. For example, an array collected through remote sensing
   10476             :  * might be indexed by (scanline, pixel). But there can be
   10477             :  * a longitude and latitude arrays alongside that are also both indexed by
   10478             :  * (scanline, pixel), and are referenced from operational arrays for
   10479             :  * reprojection purposes.
   10480             :  *
   10481             :  * For netCDF, this will return the arrays referenced by the "coordinates"
   10482             :  * attribute.
   10483             :  *
   10484             :  * This method is the same as the C function
   10485             :  * GDALMDArrayGetCoordinateVariables().
   10486             :  *
   10487             :  * @return a vector of arrays
   10488             :  *
   10489             :  * @since GDAL 3.4
   10490             :  */
   10491             : 
   10492             : std::vector<std::shared_ptr<GDALMDArray>>
   10493          13 : GDALMDArray::GetCoordinateVariables() const
   10494             : {
   10495          13 :     return {};
   10496             : }
   10497             : 
   10498             : /************************************************************************/
   10499             : /*                       ~GDALExtendedDataType()                        */
   10500             : /************************************************************************/
   10501             : 
   10502             : GDALExtendedDataType::~GDALExtendedDataType() = default;
   10503             : 
   10504             : /************************************************************************/
   10505             : /*                        GDALExtendedDataType()                        */
   10506             : /************************************************************************/
   10507             : 
   10508        9534 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
   10509        9534 :                                            GDALExtendedDataTypeSubType eSubType)
   10510             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
   10511        9534 :       m_nMaxStringLength(nMaxStringLength)
   10512             : {
   10513        9534 : }
   10514             : 
   10515             : /************************************************************************/
   10516             : /*                        GDALExtendedDataType()                        */
   10517             : /************************************************************************/
   10518             : 
   10519       37242 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
   10520             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
   10521       37242 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
   10522             : {
   10523       37242 : }
   10524             : 
   10525             : /************************************************************************/
   10526             : /*                        GDALExtendedDataType()                        */
   10527             : /************************************************************************/
   10528             : 
   10529          63 : GDALExtendedDataType::GDALExtendedDataType(
   10530             :     const std::string &osName, GDALDataType eBaseType,
   10531          63 :     std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10532             :     : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
   10533          63 :       m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
   10534             : {
   10535          63 : }
   10536             : 
   10537             : /************************************************************************/
   10538             : /*                        GDALExtendedDataType()                        */
   10539             : /************************************************************************/
   10540             : 
   10541         855 : GDALExtendedDataType::GDALExtendedDataType(
   10542             :     const std::string &osName, size_t nTotalSize,
   10543         855 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10544             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
   10545         855 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
   10546             : {
   10547         855 : }
   10548             : 
   10549             : /************************************************************************/
   10550             : /*                        GDALExtendedDataType()                        */
   10551             : /************************************************************************/
   10552             : 
   10553             : /** Copy constructor. */
   10554       17299 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
   10555       34598 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
   10556       17299 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
   10557       17299 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
   10558       17299 :       m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
   10559             : {
   10560       17299 :     if (m_eClass == GEDTC_COMPOUND)
   10561             :     {
   10562         521 :         for (const auto &elt : other.m_aoComponents)
   10563             :         {
   10564         341 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10565             :         }
   10566             :     }
   10567       17299 : }
   10568             : 
   10569             : /************************************************************************/
   10570             : /*                            operator= ()                              */
   10571             : /************************************************************************/
   10572             : 
   10573             : /** Copy assignment. */
   10574             : GDALExtendedDataType &
   10575        1045 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
   10576             : {
   10577        1045 :     if (this != &other)
   10578             :     {
   10579        1045 :         m_osName = other.m_osName;
   10580        1045 :         m_eClass = other.m_eClass;
   10581        1045 :         m_eSubType = other.m_eSubType;
   10582        1045 :         m_eNumericDT = other.m_eNumericDT;
   10583        1045 :         m_nSize = other.m_nSize;
   10584        1045 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10585        1045 :         m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
   10586        1045 :         m_aoComponents.clear();
   10587        1045 :         if (m_eClass == GEDTC_COMPOUND)
   10588             :         {
   10589           0 :             for (const auto &elt : other.m_aoComponents)
   10590             :             {
   10591           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10592             :             }
   10593             :         }
   10594             :     }
   10595        1045 :     return *this;
   10596             : }
   10597             : 
   10598             : /************************************************************************/
   10599             : /*                            operator= ()                              */
   10600             : /************************************************************************/
   10601             : 
   10602             : /** Move assignment. */
   10603             : GDALExtendedDataType &
   10604       15239 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
   10605             : {
   10606       15239 :     m_osName = std::move(other.m_osName);
   10607       15239 :     m_eClass = other.m_eClass;
   10608       15239 :     m_eSubType = other.m_eSubType;
   10609       15239 :     m_eNumericDT = other.m_eNumericDT;
   10610       15239 :     m_nSize = other.m_nSize;
   10611       15239 :     m_nMaxStringLength = other.m_nMaxStringLength;
   10612       15239 :     m_aoComponents = std::move(other.m_aoComponents);
   10613       15239 :     m_poRAT = std::move(other.m_poRAT);
   10614       15239 :     other.m_eClass = GEDTC_NUMERIC;
   10615       15239 :     other.m_eNumericDT = GDT_Unknown;
   10616       15239 :     other.m_nSize = 0;
   10617       15239 :     other.m_nMaxStringLength = 0;
   10618       15239 :     return *this;
   10619             : }
   10620             : 
   10621             : /************************************************************************/
   10622             : /*                           Create()                                   */
   10623             : /************************************************************************/
   10624             : 
   10625             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10626             :  *
   10627             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10628             :  *
   10629             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10630             :  * GDT_TypeCount
   10631             :  */
   10632       37235 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10633             : {
   10634       37235 :     return GDALExtendedDataType(eType);
   10635             : }
   10636             : 
   10637             : /************************************************************************/
   10638             : /*                           Create()                                   */
   10639             : /************************************************************************/
   10640             : 
   10641             : /** Return a new GDALExtendedDataType from a raster attribute table.
   10642             :  *
   10643             :  * @param osName Type name
   10644             :  * @param eBaseType Base integer data type.
   10645             :  * @param poRAT Raster attribute table. Must not be NULL.
   10646             :  * @since 3.12
   10647             :  */
   10648             : GDALExtendedDataType
   10649          63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
   10650             :                              std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10651             : {
   10652          63 :     return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
   10653             : }
   10654             : 
   10655             : /************************************************************************/
   10656             : /*                           Create()                                   */
   10657             : /************************************************************************/
   10658             : 
   10659             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10660             :  *
   10661             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10662             :  *
   10663             :  * @param osName Type name.
   10664             :  * @param nTotalSize Total size of the type in bytes.
   10665             :  *                   Should be large enough to store all components.
   10666             :  * @param components Components of the compound type.
   10667             :  */
   10668         862 : GDALExtendedDataType GDALExtendedDataType::Create(
   10669             :     const std::string &osName, size_t nTotalSize,
   10670             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10671             : {
   10672         862 :     size_t nLastOffset = 0;
   10673             :     // Some arbitrary threshold to avoid potential integer overflows
   10674         862 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10675             :     {
   10676           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10677           2 :         return GDALExtendedDataType(GDT_Unknown);
   10678             :     }
   10679        4088 :     for (const auto &comp : components)
   10680             :     {
   10681             :         // Check alignment too ?
   10682        3229 :         if (comp->GetOffset() < nLastOffset)
   10683             :         {
   10684           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10685           1 :             return GDALExtendedDataType(GDT_Unknown);
   10686             :         }
   10687        3228 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10688             :     }
   10689         859 :     if (nTotalSize < nLastOffset)
   10690             :     {
   10691           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10692           1 :         return GDALExtendedDataType(GDT_Unknown);
   10693             :     }
   10694         858 :     if (nTotalSize == 0 || components.empty())
   10695             :     {
   10696           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10697           3 :         return GDALExtendedDataType(GDT_Unknown);
   10698             :     }
   10699         855 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10700             : }
   10701             : 
   10702             : /************************************************************************/
   10703             : /*                           Create()                                   */
   10704             : /************************************************************************/
   10705             : 
   10706             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10707             :  *
   10708             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   10709             :  *
   10710             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   10711             :  * unknown/unlimited
   10712             :  * @param eSubType Subtype.
   10713             :  */
   10714             : GDALExtendedDataType
   10715        9534 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   10716             :                                    GDALExtendedDataTypeSubType eSubType)
   10717             : {
   10718        9534 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   10719             : }
   10720             : 
   10721             : /************************************************************************/
   10722             : /*                           operator==()                               */
   10723             : /************************************************************************/
   10724             : 
   10725             : /** Equality operator.
   10726             :  *
   10727             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   10728             :  */
   10729        2718 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   10730             : {
   10731        2691 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   10732        5409 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   10733             :     {
   10734         258 :         return false;
   10735             :     }
   10736        2460 :     if (m_eClass == GEDTC_NUMERIC)
   10737             :     {
   10738         873 :         return m_eNumericDT == other.m_eNumericDT;
   10739             :     }
   10740        1587 :     if (m_eClass == GEDTC_STRING)
   10741             :     {
   10742        1369 :         return true;
   10743             :     }
   10744         218 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10745         218 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   10746             :     {
   10747           2 :         return false;
   10748             :     }
   10749        1139 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   10750             :     {
   10751         923 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   10752             :         {
   10753           0 :             return false;
   10754             :         }
   10755             :     }
   10756         216 :     return true;
   10757             : }
   10758             : 
   10759             : /************************************************************************/
   10760             : /*                        CanConvertTo()                                */
   10761             : /************************************************************************/
   10762             : 
   10763             : /** Return whether this data type can be converted to the other one.
   10764             :  *
   10765             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10766             :  *
   10767             :  * @param other Target data type for the conversion being considered.
   10768             :  */
   10769        8527 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10770             : {
   10771        8527 :     if (m_eClass == GEDTC_NUMERIC)
   10772             :     {
   10773        5952 :         if (m_eNumericDT == GDT_Unknown)
   10774           0 :             return false;
   10775        5952 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10776        5843 :             other.m_eNumericDT == GDT_Unknown)
   10777           0 :             return false;
   10778        6061 :         return other.m_eClass == GEDTC_NUMERIC ||
   10779        6061 :                other.m_eClass == GEDTC_STRING;
   10780             :     }
   10781        2575 :     if (m_eClass == GEDTC_STRING)
   10782             :     {
   10783        2389 :         return other.m_eClass == m_eClass;
   10784             :     }
   10785         186 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10786         186 :     if (other.m_eClass != GEDTC_COMPOUND)
   10787           0 :         return false;
   10788             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10789         372 :         srcComponents;
   10790         929 :     for (const auto &srcComp : m_aoComponents)
   10791             :     {
   10792         743 :         srcComponents[srcComp->GetName()] = &srcComp;
   10793             :     }
   10794         513 :     for (const auto &dstComp : other.m_aoComponents)
   10795             :     {
   10796         328 :         auto oIter = srcComponents.find(dstComp->GetName());
   10797         328 :         if (oIter == srcComponents.end())
   10798           1 :             return false;
   10799         327 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10800           0 :             return false;
   10801             :     }
   10802         185 :     return true;
   10803             : }
   10804             : 
   10805             : /************************************************************************/
   10806             : /*                     NeedsFreeDynamicMemory()                         */
   10807             : /************************************************************************/
   10808             : 
   10809             : /** Return whether the data type holds dynamically allocated memory, that
   10810             :  * needs to be freed with FreeDynamicMemory().
   10811             :  *
   10812             :  */
   10813        3742 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10814             : {
   10815        3742 :     switch (m_eClass)
   10816             :     {
   10817         936 :         case GEDTC_STRING:
   10818         936 :             return true;
   10819             : 
   10820        2696 :         case GEDTC_NUMERIC:
   10821        2696 :             return false;
   10822             : 
   10823         110 :         case GEDTC_COMPOUND:
   10824             :         {
   10825         223 :             for (const auto &comp : m_aoComponents)
   10826             :             {
   10827         209 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10828          96 :                     return true;
   10829             :             }
   10830             :         }
   10831             :     }
   10832          14 :     return false;
   10833             : }
   10834             : 
   10835             : /************************************************************************/
   10836             : /*                        FreeDynamicMemory()                           */
   10837             : /************************************************************************/
   10838             : 
   10839             : /** Release the dynamic memory (strings typically) from a raw value.
   10840             :  *
   10841             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10842             :  *
   10843             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10844             :  */
   10845        3865 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10846             : {
   10847        3865 :     switch (m_eClass)
   10848             :     {
   10849        2788 :         case GEDTC_STRING:
   10850             :         {
   10851             :             char *pszStr;
   10852        2788 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10853        2788 :             if (pszStr)
   10854             :             {
   10855        2243 :                 VSIFree(pszStr);
   10856             :             }
   10857        2788 :             break;
   10858             :         }
   10859             : 
   10860         901 :         case GEDTC_NUMERIC:
   10861             :         {
   10862         901 :             break;
   10863             :         }
   10864             : 
   10865         176 :         case GEDTC_COMPOUND:
   10866             :         {
   10867         176 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10868         938 :             for (const auto &comp : m_aoComponents)
   10869             :             {
   10870        1524 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10871         762 :                                                   comp->GetOffset());
   10872             :             }
   10873         176 :             break;
   10874             :         }
   10875             :     }
   10876        3865 : }
   10877             : 
   10878             : /************************************************************************/
   10879             : /*                      ~GDALEDTComponent()                             */
   10880             : /************************************************************************/
   10881             : 
   10882             : GDALEDTComponent::~GDALEDTComponent() = default;
   10883             : 
   10884             : /************************************************************************/
   10885             : /*                      GDALEDTComponent()                              */
   10886             : /************************************************************************/
   10887             : 
   10888             : /** constructor of a GDALEDTComponent
   10889             :  *
   10890             :  * This is the same as the C function GDALEDTComponendCreate()
   10891             :  *
   10892             :  * @param name Component name
   10893             :  * @param offset Offset in byte of the component in the compound data type.
   10894             :  *               In case of nesting of compound data type, this should be
   10895             :  *               the offset to the immediate belonging data type, not to the
   10896             :  *               higher level one.
   10897             :  * @param type   Component data type.
   10898             :  */
   10899        3220 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10900        3220 :                                    const GDALExtendedDataType &type)
   10901        3220 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10902             : {
   10903        3220 : }
   10904             : 
   10905             : /************************************************************************/
   10906             : /*                      GDALEDTComponent()                              */
   10907             : /************************************************************************/
   10908             : 
   10909             : /** Copy constructor. */
   10910             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10911             : 
   10912             : /************************************************************************/
   10913             : /*                           operator==()                               */
   10914             : /************************************************************************/
   10915             : 
   10916             : /** Equality operator.
   10917             :  */
   10918         923 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10919             : {
   10920        1846 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10921        1846 :            m_oType == other.m_oType;
   10922             : }
   10923             : 
   10924             : /************************************************************************/
   10925             : /*                        ~GDALDimension()                              */
   10926             : /************************************************************************/
   10927             : 
   10928             : GDALDimension::~GDALDimension() = default;
   10929             : 
   10930             : /************************************************************************/
   10931             : /*                         GDALDimension()                              */
   10932             : /************************************************************************/
   10933             : 
   10934             : //! @cond Doxygen_Suppress
   10935             : /** Constructor.
   10936             :  *
   10937             :  * @param osParentName Parent name
   10938             :  * @param osName name
   10939             :  * @param osType type. See GetType().
   10940             :  * @param osDirection direction. See GetDirection().
   10941             :  * @param nSize size.
   10942             :  */
   10943        8502 : GDALDimension::GDALDimension(const std::string &osParentName,
   10944             :                              const std::string &osName,
   10945             :                              const std::string &osType,
   10946        8502 :                              const std::string &osDirection, GUInt64 nSize)
   10947             :     : m_osName(osName),
   10948             :       m_osFullName(
   10949        8502 :           !osParentName.empty()
   10950       12521 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   10951             :               : osName),
   10952       29525 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   10953             : {
   10954        8502 : }
   10955             : 
   10956             : //! @endcond
   10957             : 
   10958             : /************************************************************************/
   10959             : /*                         GetIndexingVariable()                        */
   10960             : /************************************************************************/
   10961             : 
   10962             : /** Return the variable that is used to index the dimension (if there is one).
   10963             :  *
   10964             :  * This is the array, typically one-dimensional, describing the values taken
   10965             :  * by the dimension.
   10966             :  */
   10967          73 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   10968             : {
   10969          73 :     return nullptr;
   10970             : }
   10971             : 
   10972             : /************************************************************************/
   10973             : /*                         SetIndexingVariable()                        */
   10974             : /************************************************************************/
   10975             : 
   10976             : /** Set the variable that is used to index the dimension.
   10977             :  *
   10978             :  * This is the array, typically one-dimensional, describing the values taken
   10979             :  * by the dimension.
   10980             :  *
   10981             :  * Optionally implemented by drivers.
   10982             :  *
   10983             :  * Drivers known to implement it: MEM.
   10984             :  *
   10985             :  * @param poArray Variable to use to index the dimension.
   10986             :  * @return true in case of success.
   10987             :  */
   10988           3 : bool GDALDimension::SetIndexingVariable(
   10989             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   10990             : {
   10991           3 :     CPLError(CE_Failure, CPLE_NotSupported,
   10992             :              "SetIndexingVariable() not implemented");
   10993           3 :     return false;
   10994             : }
   10995             : 
   10996             : /************************************************************************/
   10997             : /*                            Rename()                                  */
   10998             : /************************************************************************/
   10999             : 
   11000             : /** Rename the dimension.
   11001             :  *
   11002             :  * This is not implemented by all drivers.
   11003             :  *
   11004             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   11005             :  *
   11006             :  * This is the same as the C function GDALDimensionRename().
   11007             :  *
   11008             :  * @param osNewName New name.
   11009             :  *
   11010             :  * @return true in case of success
   11011             :  * @since GDAL 3.8
   11012             :  */
   11013           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   11014             : {
   11015           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   11016           0 :     return false;
   11017             : }
   11018             : 
   11019             : /************************************************************************/
   11020             : /*                         BaseRename()                                 */
   11021             : /************************************************************************/
   11022             : 
   11023             : //! @cond Doxygen_Suppress
   11024           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   11025             : {
   11026           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   11027           8 :     m_osFullName += osNewName;
   11028           8 :     m_osName = osNewName;
   11029           8 : }
   11030             : 
   11031             : //! @endcond
   11032             : 
   11033             : //! @cond Doxygen_Suppress
   11034             : /************************************************************************/
   11035             : /*                          ParentRenamed()                             */
   11036             : /************************************************************************/
   11037             : 
   11038           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   11039             : {
   11040           8 :     m_osFullName = osNewParentFullName;
   11041           8 :     m_osFullName += "/";
   11042           8 :     m_osFullName += m_osName;
   11043           8 : }
   11044             : 
   11045             : //! @endcond
   11046             : 
   11047             : //! @cond Doxygen_Suppress
   11048             : /************************************************************************/
   11049             : /*                          ParentDeleted()                             */
   11050             : /************************************************************************/
   11051             : 
   11052           4 : void GDALDimension::ParentDeleted()
   11053             : {
   11054           4 : }
   11055             : 
   11056             : //! @endcond
   11057             : 
   11058             : /************************************************************************/
   11059             : /************************************************************************/
   11060             : /************************************************************************/
   11061             : /*                              C API                                   */
   11062             : /************************************************************************/
   11063             : /************************************************************************/
   11064             : /************************************************************************/
   11065             : 
   11066             : /************************************************************************/
   11067             : /*                      GDALExtendedDataTypeCreate()                    */
   11068             : /************************************************************************/
   11069             : 
   11070             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   11071             :  *
   11072             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   11073             :  *
   11074             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11075             :  *
   11076             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   11077             :  * GDT_TypeCount
   11078             :  *
   11079             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11080             :  */
   11081        2008 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   11082             : {
   11083        2008 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   11084             :     {
   11085           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   11086             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   11087           0 :         return nullptr;
   11088             :     }
   11089             :     return new GDALExtendedDataTypeHS(
   11090        2008 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   11091             : }
   11092             : 
   11093             : /************************************************************************/
   11094             : /*                    GDALExtendedDataTypeCreateString()                */
   11095             : /************************************************************************/
   11096             : 
   11097             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11098             :  *
   11099             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11100             :  *
   11101             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11102             :  *
   11103             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11104             :  */
   11105           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   11106             : {
   11107           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11108           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   11109             : }
   11110             : 
   11111             : /************************************************************************/
   11112             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   11113             : /************************************************************************/
   11114             : 
   11115             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11116             :  *
   11117             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11118             :  *
   11119             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11120             :  *
   11121             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11122             :  * @since GDAL 3.4
   11123             :  */
   11124             : GDALExtendedDataTypeH
   11125         190 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   11126             :                                    GDALExtendedDataTypeSubType eSubType)
   11127             : {
   11128         190 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11129         190 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   11130             : }
   11131             : 
   11132             : /************************************************************************/
   11133             : /*                   GDALExtendedDataTypeCreateCompound()               */
   11134             : /************************************************************************/
   11135             : 
   11136             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   11137             :  *
   11138             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   11139             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   11140             :  *
   11141             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11142             :  *
   11143             :  * @param pszName Type name.
   11144             :  * @param nTotalSize Total size of the type in bytes.
   11145             :  *                   Should be large enough to store all components.
   11146             :  * @param nComponents Number of components in comps array.
   11147             :  * @param comps Components.
   11148             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11149             :  */
   11150             : GDALExtendedDataTypeH
   11151          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   11152             :                                    size_t nComponents,
   11153             :                                    const GDALEDTComponentH *comps)
   11154             : {
   11155          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   11156          54 :     for (size_t i = 0; i < nComponents; i++)
   11157             :     {
   11158          64 :         compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
   11159          64 :             new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
   11160             :     }
   11161             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   11162          66 :                                            std::move(compsCpp));
   11163          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   11164           6 :         return nullptr;
   11165          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
   11166             : }
   11167             : 
   11168             : /************************************************************************/
   11169             : /*                     GDALExtendedDataTypeRelease()                    */
   11170             : /************************************************************************/
   11171             : 
   11172             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   11173             :  *
   11174             :  * Note: when applied on a object coming from a driver, this does not
   11175             :  * destroy the object in the file, database, etc...
   11176             :  */
   11177        6621 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   11178             : {
   11179        6621 :     delete hEDT;
   11180        6621 : }
   11181             : 
   11182             : /************************************************************************/
   11183             : /*                     GDALExtendedDataTypeGetName()                    */
   11184             : /************************************************************************/
   11185             : 
   11186             : /** Return type name.
   11187             :  *
   11188             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   11189             :  */
   11190           8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   11191             : {
   11192           8 :     VALIDATE_POINTER1(hEDT, __func__, "");
   11193           8 :     return hEDT->m_poImpl->GetName().c_str();
   11194             : }
   11195             : 
   11196             : /************************************************************************/
   11197             : /*                     GDALExtendedDataTypeGetClass()                    */
   11198             : /************************************************************************/
   11199             : 
   11200             : /** Return type class.
   11201             :  *
   11202             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   11203             :  */
   11204             : GDALExtendedDataTypeClass
   11205        9177 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   11206             : {
   11207        9177 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   11208        9177 :     return hEDT->m_poImpl->GetClass();
   11209             : }
   11210             : 
   11211             : /************************************************************************/
   11212             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   11213             : /************************************************************************/
   11214             : 
   11215             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   11216             :  *
   11217             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   11218             :  */
   11219        2069 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   11220             : {
   11221        2069 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   11222        2069 :     return hEDT->m_poImpl->GetNumericDataType();
   11223             : }
   11224             : 
   11225             : /************************************************************************/
   11226             : /*                   GDALExtendedDataTypeGetSize()                      */
   11227             : /************************************************************************/
   11228             : 
   11229             : /** Return data type size in bytes.
   11230             :  *
   11231             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   11232             :  */
   11233        2538 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   11234             : {
   11235        2538 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11236        2538 :     return hEDT->m_poImpl->GetSize();
   11237             : }
   11238             : 
   11239             : /************************************************************************/
   11240             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   11241             : /************************************************************************/
   11242             : 
   11243             : /** Return the maximum length of a string in bytes.
   11244             :  *
   11245             :  * 0 indicates unknown/unlimited string.
   11246             :  *
   11247             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   11248             :  */
   11249           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   11250             : {
   11251           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11252           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   11253             : }
   11254             : 
   11255             : /************************************************************************/
   11256             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   11257             : /************************************************************************/
   11258             : 
   11259             : /** Return whether this data type can be converted to the other one.
   11260             :  *
   11261             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   11262             :  *
   11263             :  * @param hSourceEDT Source data type for the conversion being considered.
   11264             :  * @param hTargetEDT Target data type for the conversion being considered.
   11265             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   11266             :  */
   11267           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   11268             :                                      GDALExtendedDataTypeH hTargetEDT)
   11269             : {
   11270           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   11271           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   11272           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   11273             : }
   11274             : 
   11275             : /************************************************************************/
   11276             : /*                        GDALExtendedDataTypeEquals()                  */
   11277             : /************************************************************************/
   11278             : 
   11279             : /** Return whether this data type is equal to another one.
   11280             :  *
   11281             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   11282             :  *
   11283             :  * @param hFirstEDT First data type.
   11284             :  * @param hSecondEDT Second data type.
   11285             :  * @return TRUE if they are equal. FALSE otherwise.
   11286             :  */
   11287         100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   11288             :                                GDALExtendedDataTypeH hSecondEDT)
   11289             : {
   11290         100 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   11291         100 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   11292         100 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   11293             : }
   11294             : 
   11295             : /************************************************************************/
   11296             : /*                    GDALExtendedDataTypeGetSubType()                  */
   11297             : /************************************************************************/
   11298             : 
   11299             : /** Return the subtype of a type.
   11300             :  *
   11301             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   11302             :  *
   11303             :  * @param hEDT Data type.
   11304             :  * @return subtype.
   11305             :  * @since 3.4
   11306             :  */
   11307             : GDALExtendedDataTypeSubType
   11308         104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   11309             : {
   11310         104 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   11311         104 :     return hEDT->m_poImpl->GetSubType();
   11312             : }
   11313             : 
   11314             : /************************************************************************/
   11315             : /*                      GDALExtendedDataTypeGetRAT()                    */
   11316             : /************************************************************************/
   11317             : 
   11318             : /** Return associated raster attribute table, when there is one.
   11319             :  *
   11320             :  * * For the netCDF driver, the RAT will capture enumerated types, with
   11321             :  * a "value" column with an integer value and a "name" column with the
   11322             :  * associated name.
   11323             :  * This is the same as the C++ method GDALExtendedDataType::GetRAT()
   11324             :  *
   11325             :  * @param hEDT Data type.
   11326             :  * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
   11327             :  * @since 3.12
   11328             :  */
   11329           1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
   11330             : {
   11331           1 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11332           1 :     return GDALRasterAttributeTable::ToHandle(
   11333           2 :         const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
   11334             : }
   11335             : 
   11336             : /************************************************************************/
   11337             : /*                     GDALExtendedDataTypeGetComponents()              */
   11338             : /************************************************************************/
   11339             : 
   11340             : /** Return the components of the data type (only valid when GetClass() ==
   11341             :  * GEDTC_COMPOUND)
   11342             :  *
   11343             :  * The returned array and its content must be freed with
   11344             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   11345             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   11346             :  * individual array members).
   11347             :  *
   11348             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   11349             :  *
   11350             :  * @param hEDT Data type
   11351             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11352             :  * @return an array of *pnCount components.
   11353             :  */
   11354          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   11355             :                                                      size_t *pnCount)
   11356             : {
   11357          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11358          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11359          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   11360             :     auto ret = static_cast<GDALEDTComponentH *>(
   11361          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   11362         131 :     for (size_t i = 0; i < components.size(); i++)
   11363             :     {
   11364          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   11365             :     }
   11366          44 :     *pnCount = components.size();
   11367          44 :     return ret;
   11368             : }
   11369             : 
   11370             : /************************************************************************/
   11371             : /*                     GDALExtendedDataTypeFreeComponents()             */
   11372             : /************************************************************************/
   11373             : 
   11374             : /** Free the return of GDALExtendedDataTypeGetComponents().
   11375             :  *
   11376             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   11377             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   11378             :  */
   11379          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   11380             :                                         size_t nCount)
   11381             : {
   11382         131 :     for (size_t i = 0; i < nCount; i++)
   11383             :     {
   11384          87 :         delete components[i];
   11385             :     }
   11386          44 :     CPLFree(components);
   11387          44 : }
   11388             : 
   11389             : /************************************************************************/
   11390             : /*                         GDALEDTComponentCreate()                     */
   11391             : /************************************************************************/
   11392             : 
   11393             : /** Create a new GDALEDTComponent.
   11394             :  *
   11395             :  * The returned value must be freed with GDALEDTComponentRelease().
   11396             :  *
   11397             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   11398             :  */
   11399          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   11400             :                                          GDALExtendedDataTypeH hType)
   11401             : {
   11402          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11403          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   11404             :     return new GDALEDTComponentHS(
   11405          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   11406             : }
   11407             : 
   11408             : /************************************************************************/
   11409             : /*                         GDALEDTComponentRelease()                    */
   11410             : /************************************************************************/
   11411             : 
   11412             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   11413             :  *
   11414             :  * Note: when applied on a object coming from a driver, this does not
   11415             :  * destroy the object in the file, database, etc...
   11416             :  */
   11417          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   11418             : {
   11419          61 :     delete hComp;
   11420          61 : }
   11421             : 
   11422             : /************************************************************************/
   11423             : /*                         GDALEDTComponentGetName()                    */
   11424             : /************************************************************************/
   11425             : 
   11426             : /** Return the name.
   11427             :  *
   11428             :  * The returned pointer is valid until hComp is released.
   11429             :  *
   11430             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   11431             :  */
   11432          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   11433             : {
   11434          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11435          33 :     return hComp->m_poImpl->GetName().c_str();
   11436             : }
   11437             : 
   11438             : /************************************************************************/
   11439             : /*                       GDALEDTComponentGetOffset()                    */
   11440             : /************************************************************************/
   11441             : 
   11442             : /** Return the offset (in bytes) of the component in the compound data type.
   11443             :  *
   11444             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   11445             :  */
   11446          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   11447             : {
   11448          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   11449          31 :     return hComp->m_poImpl->GetOffset();
   11450             : }
   11451             : 
   11452             : /************************************************************************/
   11453             : /*                       GDALEDTComponentGetType()                      */
   11454             : /************************************************************************/
   11455             : 
   11456             : /** Return the data type of the component.
   11457             :  *
   11458             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   11459             :  */
   11460          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   11461             : {
   11462          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11463             :     return new GDALExtendedDataTypeHS(
   11464          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   11465             : }
   11466             : 
   11467             : /************************************************************************/
   11468             : /*                           GDALGroupRelease()                         */
   11469             : /************************************************************************/
   11470             : 
   11471             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   11472             :  *
   11473             :  * Note: when applied on a object coming from a driver, this does not
   11474             :  * destroy the object in the file, database, etc...
   11475             :  */
   11476        1440 : void GDALGroupRelease(GDALGroupH hGroup)
   11477             : {
   11478        1440 :     delete hGroup;
   11479        1440 : }
   11480             : 
   11481             : /************************************************************************/
   11482             : /*                           GDALGroupGetName()                         */
   11483             : /************************************************************************/
   11484             : 
   11485             : /** Return the name of the group.
   11486             :  *
   11487             :  * The returned pointer is valid until hGroup is released.
   11488             :  *
   11489             :  * This is the same as the C++ method GDALGroup::GetName().
   11490             :  */
   11491          95 : const char *GDALGroupGetName(GDALGroupH hGroup)
   11492             : {
   11493          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11494          95 :     return hGroup->m_poImpl->GetName().c_str();
   11495             : }
   11496             : 
   11497             : /************************************************************************/
   11498             : /*                         GDALGroupGetFullName()                       */
   11499             : /************************************************************************/
   11500             : 
   11501             : /** Return the full name of the group.
   11502             :  *
   11503             :  * The returned pointer is valid until hGroup is released.
   11504             :  *
   11505             :  * This is the same as the C++ method GDALGroup::GetFullName().
   11506             :  */
   11507          47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   11508             : {
   11509          47 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11510          47 :     return hGroup->m_poImpl->GetFullName().c_str();
   11511             : }
   11512             : 
   11513             : /************************************************************************/
   11514             : /*                          GDALGroupGetMDArrayNames()                  */
   11515             : /************************************************************************/
   11516             : 
   11517             : /** Return the list of multidimensional array names contained in this group.
   11518             :  *
   11519             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11520             :  *
   11521             :  * @return the array names, to be freed with CSLDestroy()
   11522             :  */
   11523         329 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11524             : {
   11525         329 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11526         658 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   11527         658 :     CPLStringList res;
   11528         829 :     for (const auto &name : names)
   11529             :     {
   11530         500 :         res.AddString(name.c_str());
   11531             :     }
   11532         329 :     return res.StealList();
   11533             : }
   11534             : 
   11535             : /************************************************************************/
   11536             : /*                  GDALGroupGetMDArrayFullNamesRecursive()             */
   11537             : /************************************************************************/
   11538             : 
   11539             : /** Return the list of multidimensional array full names contained in this
   11540             :  * group and its subgroups.
   11541             :  *
   11542             :  * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
   11543             :  *
   11544             :  * @return the array names, to be freed with CSLDestroy()
   11545             :  *
   11546             :  * @since 3.11
   11547             :  */
   11548           1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
   11549             :                                              CSLConstList papszGroupOptions,
   11550             :                                              CSLConstList papszArrayOptions)
   11551             : {
   11552           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11553           1 :     auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
   11554           2 :         papszGroupOptions, papszArrayOptions);
   11555           2 :     CPLStringList res;
   11556           5 :     for (const auto &name : names)
   11557             :     {
   11558           4 :         res.AddString(name.c_str());
   11559             :     }
   11560           1 :     return res.StealList();
   11561             : }
   11562             : 
   11563             : /************************************************************************/
   11564             : /*                          GDALGroupOpenMDArray()                      */
   11565             : /************************************************************************/
   11566             : 
   11567             : /** Open and return a multidimensional array.
   11568             :  *
   11569             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   11570             :  *
   11571             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11572             :  */
   11573         806 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   11574             :                                   CSLConstList papszOptions)
   11575             : {
   11576         806 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11577         806 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   11578        2418 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   11579        2418 :                                                papszOptions);
   11580         806 :     if (!array)
   11581          30 :         return nullptr;
   11582         776 :     return new GDALMDArrayHS(array);
   11583             : }
   11584             : 
   11585             : /************************************************************************/
   11586             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   11587             : /************************************************************************/
   11588             : 
   11589             : /** Open and return a multidimensional array from its fully qualified name.
   11590             :  *
   11591             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   11592             :  *
   11593             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11594             :  *
   11595             :  * @since GDAL 3.2
   11596             :  */
   11597          16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   11598             :                                               const char *pszFullname,
   11599             :                                               CSLConstList papszOptions)
   11600             : {
   11601          16 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11602          16 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11603          16 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   11604          48 :         std::string(pszFullname), papszOptions);
   11605          16 :     if (!array)
   11606           2 :         return nullptr;
   11607          14 :     return new GDALMDArrayHS(array);
   11608             : }
   11609             : 
   11610             : /************************************************************************/
   11611             : /*                      GDALGroupResolveMDArray()                       */
   11612             : /************************************************************************/
   11613             : 
   11614             : /** Locate an array in a group and its subgroups by name.
   11615             :  *
   11616             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   11617             :  * @since GDAL 3.2
   11618             :  */
   11619          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   11620             :                                      const char *pszStartingPoint,
   11621             :                                      CSLConstList papszOptions)
   11622             : {
   11623          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11624          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11625          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   11626          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   11627          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   11628          19 :     if (!array)
   11629           2 :         return nullptr;
   11630          17 :     return new GDALMDArrayHS(array);
   11631             : }
   11632             : 
   11633             : /************************************************************************/
   11634             : /*                        GDALGroupGetGroupNames()                      */
   11635             : /************************************************************************/
   11636             : 
   11637             : /** Return the list of sub-groups contained in this group.
   11638             :  *
   11639             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11640             :  *
   11641             :  * @return the group names, to be freed with CSLDestroy()
   11642             :  */
   11643          97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11644             : {
   11645          97 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11646         194 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   11647         194 :     CPLStringList res;
   11648         219 :     for (const auto &name : names)
   11649             :     {
   11650         122 :         res.AddString(name.c_str());
   11651             :     }
   11652          97 :     return res.StealList();
   11653             : }
   11654             : 
   11655             : /************************************************************************/
   11656             : /*                           GDALGroupOpenGroup()                       */
   11657             : /************************************************************************/
   11658             : 
   11659             : /** Open and return a sub-group.
   11660             :  *
   11661             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11662             :  *
   11663             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11664             :  */
   11665         163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11666             :                               CSLConstList papszOptions)
   11667             : {
   11668         163 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11669         163 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11670             :     auto subGroup =
   11671         489 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11672         163 :     if (!subGroup)
   11673          30 :         return nullptr;
   11674         133 :     return new GDALGroupHS(subGroup);
   11675             : }
   11676             : 
   11677             : /************************************************************************/
   11678             : /*                   GDALGroupGetVectorLayerNames()                     */
   11679             : /************************************************************************/
   11680             : 
   11681             : /** Return the list of layer names contained in this group.
   11682             :  *
   11683             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11684             :  *
   11685             :  * @return the group names, to be freed with CSLDestroy()
   11686             :  * @since 3.4
   11687             :  */
   11688           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11689             :                                     CSLConstList papszOptions)
   11690             : {
   11691           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11692          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11693          16 :     CPLStringList res;
   11694          18 :     for (const auto &name : names)
   11695             :     {
   11696          10 :         res.AddString(name.c_str());
   11697             :     }
   11698           8 :     return res.StealList();
   11699             : }
   11700             : 
   11701             : /************************************************************************/
   11702             : /*                      GDALGroupOpenVectorLayer()                      */
   11703             : /************************************************************************/
   11704             : 
   11705             : /** Open and return a vector layer.
   11706             :  *
   11707             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   11708             :  *
   11709             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   11710             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   11711             :  * opened.
   11712             :  *
   11713             :  * @return the vector layer, or nullptr.
   11714             :  * @since 3.4
   11715             :  */
   11716          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   11717             :                                    const char *pszVectorLayerName,
   11718             :                                    CSLConstList papszOptions)
   11719             : {
   11720          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11721          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   11722          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   11723          24 :         std::string(pszVectorLayerName), papszOptions));
   11724             : }
   11725             : 
   11726             : /************************************************************************/
   11727             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   11728             : /************************************************************************/
   11729             : 
   11730             : /** Open and return a sub-group from its fully qualified name.
   11731             :  *
   11732             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   11733             :  *
   11734             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11735             :  *
   11736             :  * @since GDAL 3.2
   11737             :  */
   11738           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   11739             :                                           const char *pszFullname,
   11740             :                                           CSLConstList papszOptions)
   11741             : {
   11742           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11743           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11744           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   11745           9 :         std::string(pszFullname), papszOptions);
   11746           3 :     if (!subGroup)
   11747           2 :         return nullptr;
   11748           1 :     return new GDALGroupHS(subGroup);
   11749             : }
   11750             : 
   11751             : /************************************************************************/
   11752             : /*                         GDALGroupGetDimensions()                     */
   11753             : /************************************************************************/
   11754             : 
   11755             : /** Return the list of dimensions contained in this group and used by its
   11756             :  * arrays.
   11757             :  *
   11758             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   11759             :  * array itself needs to be freed, CPLFree() should be called (and
   11760             :  * GDALDimensionRelease() on individual array members).
   11761             :  *
   11762             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   11763             :  *
   11764             :  * @param hGroup Group.
   11765             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11766             :  * @param papszOptions Driver specific options determining how dimensions
   11767             :  * should be retrieved. Pass nullptr for default behavior.
   11768             :  *
   11769             :  * @return an array of *pnCount dimensions.
   11770             :  */
   11771          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   11772             :                                        CSLConstList papszOptions)
   11773             : {
   11774          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11775          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11776          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   11777             :     auto ret = static_cast<GDALDimensionH *>(
   11778          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11779         230 :     for (size_t i = 0; i < dims.size(); i++)
   11780             :     {
   11781         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   11782             :     }
   11783          73 :     *pnCount = dims.size();
   11784          73 :     return ret;
   11785             : }
   11786             : 
   11787             : /************************************************************************/
   11788             : /*                          GDALGroupGetAttribute()                     */
   11789             : /************************************************************************/
   11790             : 
   11791             : /** Return an attribute by its name.
   11792             :  *
   11793             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11794             :  *
   11795             :  * The returned attribute must be freed with GDALAttributeRelease().
   11796             :  */
   11797          80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   11798             : {
   11799          80 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11800          80 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11801         240 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   11802          80 :     if (attr)
   11803          76 :         return new GDALAttributeHS(attr);
   11804           4 :     return nullptr;
   11805             : }
   11806             : 
   11807             : /************************************************************************/
   11808             : /*                         GDALGroupGetAttributes()                     */
   11809             : /************************************************************************/
   11810             : 
   11811             : /** Return the list of attributes contained in this group.
   11812             :  *
   11813             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11814             :  * array itself needs to be freed, CPLFree() should be called (and
   11815             :  * GDALAttributeRelease() on individual array members).
   11816             :  *
   11817             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   11818             :  *
   11819             :  * @param hGroup Group.
   11820             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11821             :  * @param papszOptions Driver specific options determining how attributes
   11822             :  * should be retrieved. Pass nullptr for default behavior.
   11823             :  *
   11824             :  * @return an array of *pnCount attributes.
   11825             :  */
   11826          71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11827             :                                        CSLConstList papszOptions)
   11828             : {
   11829          71 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11830          71 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11831          71 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11832             :     auto ret = static_cast<GDALAttributeH *>(
   11833          71 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11834         229 :     for (size_t i = 0; i < attrs.size(); i++)
   11835             :     {
   11836         158 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11837             :     }
   11838          71 :     *pnCount = attrs.size();
   11839          71 :     return ret;
   11840             : }
   11841             : 
   11842             : /************************************************************************/
   11843             : /*                     GDALGroupGetStructuralInfo()                     */
   11844             : /************************************************************************/
   11845             : 
   11846             : /** Return structural information on the group.
   11847             :  *
   11848             :  * This may be the compression, etc..
   11849             :  *
   11850             :  * The return value should not be freed and is valid until GDALGroup is
   11851             :  * released or this function called again.
   11852             :  *
   11853             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11854             :  */
   11855           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11856             : {
   11857           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11858           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11859             : }
   11860             : 
   11861             : /************************************************************************/
   11862             : /*                   GDALGroupGetDataTypeCount()                        */
   11863             : /************************************************************************/
   11864             : 
   11865             : /** Return the number of data types associated with the group
   11866             :  * (typically enumerations).
   11867             :  *
   11868             :  * This is the same as the C++ method GDALGroup::GetDataTypes().size().
   11869             :  *
   11870             :  * @since 3.12
   11871             :  */
   11872           4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
   11873             : {
   11874           4 :     VALIDATE_POINTER1(hGroup, __func__, 0);
   11875           4 :     return hGroup->m_poImpl->GetDataTypes().size();
   11876             : }
   11877             : 
   11878             : /************************************************************************/
   11879             : /*                      GDALGroupGetDataType()                          */
   11880             : /************************************************************************/
   11881             : 
   11882             : /** Return one of the data types associated with the group.
   11883             :  *
   11884             :  * This is the same as the C++ method GDALGroup::GetDataTypes()[].
   11885             :  *
   11886             :  * @return a type to release with GDALExtendedDataTypeRelease() once done,
   11887             :  * or nullptr in case of error.
   11888             :  * @since 3.12
   11889             :  */
   11890           1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
   11891             : {
   11892           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11893           1 :     if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
   11894           0 :         return nullptr;
   11895           1 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11896           1 :         *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
   11897             : }
   11898             : 
   11899             : /************************************************************************/
   11900             : /*                         GDALReleaseAttributes()                      */
   11901             : /************************************************************************/
   11902             : 
   11903             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11904             :  *
   11905             :  * @param attributes return pointer of above methods
   11906             :  * @param nCount *pnCount value returned by above methods
   11907             :  */
   11908         129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11909             : {
   11910         416 :     for (size_t i = 0; i < nCount; i++)
   11911             :     {
   11912         287 :         delete attributes[i];
   11913             :     }
   11914         129 :     CPLFree(attributes);
   11915         129 : }
   11916             : 
   11917             : /************************************************************************/
   11918             : /*                         GDALGroupCreateGroup()                       */
   11919             : /************************************************************************/
   11920             : 
   11921             : /** Create a sub-group within a group.
   11922             :  *
   11923             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11924             :  *
   11925             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11926             :  */
   11927         177 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11928             :                                 CSLConstList papszOptions)
   11929             : {
   11930         177 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11931         177 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11932         531 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11933         531 :                                              papszOptions);
   11934         177 :     if (!ret)
   11935          49 :         return nullptr;
   11936         128 :     return new GDALGroupHS(ret);
   11937             : }
   11938             : 
   11939             : /************************************************************************/
   11940             : /*                         GDALGroupDeleteGroup()                       */
   11941             : /************************************************************************/
   11942             : 
   11943             : /** Delete a sub-group from a group.
   11944             :  *
   11945             :  * After this call, if a previously obtained instance of the deleted object
   11946             :  * is still alive, no method other than for freeing it should be invoked.
   11947             :  *
   11948             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   11949             :  *
   11950             :  * @return true in case of success.
   11951             :  * @since GDAL 3.8
   11952             :  */
   11953          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11954             :                           CSLConstList papszOptions)
   11955             : {
   11956          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11957          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   11958          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   11959          20 :                                          papszOptions);
   11960             : }
   11961             : 
   11962             : /************************************************************************/
   11963             : /*                      GDALGroupCreateDimension()                      */
   11964             : /************************************************************************/
   11965             : 
   11966             : /** Create a dimension within a group.
   11967             :  *
   11968             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   11969             :  *
   11970             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   11971             :  */
   11972         666 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   11973             :                                         const char *pszType,
   11974             :                                         const char *pszDirection, GUInt64 nSize,
   11975             :                                         CSLConstList papszOptions)
   11976             : {
   11977         666 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11978         666 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11979         666 :     auto ret = hGroup->m_poImpl->CreateDimension(
   11980        1332 :         std::string(pszName), std::string(pszType ? pszType : ""),
   11981        2664 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   11982         666 :     if (!ret)
   11983           9 :         return nullptr;
   11984         657 :     return new GDALDimensionHS(ret);
   11985             : }
   11986             : 
   11987             : /************************************************************************/
   11988             : /*                      GDALGroupCreateMDArray()                        */
   11989             : /************************************************************************/
   11990             : 
   11991             : /** Create a multidimensional array within a group.
   11992             :  *
   11993             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   11994             :  *
   11995             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11996             :  */
   11997         609 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   11998             :                                     size_t nDimensions,
   11999             :                                     GDALDimensionH *pahDimensions,
   12000             :                                     GDALExtendedDataTypeH hEDT,
   12001             :                                     CSLConstList papszOptions)
   12002             : {
   12003         609 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12004         609 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12005         609 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12006        1218 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   12007         609 :     dims.reserve(nDimensions);
   12008        1437 :     for (size_t i = 0; i < nDimensions; i++)
   12009         828 :         dims.push_back(pahDimensions[i]->m_poImpl);
   12010        1827 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   12011        1827 :                                                *(hEDT->m_poImpl), papszOptions);
   12012         609 :     if (!ret)
   12013          65 :         return nullptr;
   12014         544 :     return new GDALMDArrayHS(ret);
   12015             : }
   12016             : 
   12017             : /************************************************************************/
   12018             : /*                         GDALGroupDeleteMDArray()                     */
   12019             : /************************************************************************/
   12020             : 
   12021             : /** Delete an array from a group.
   12022             :  *
   12023             :  * After this call, if a previously obtained instance of the deleted object
   12024             :  * is still alive, no method other than for freeing it should be invoked.
   12025             :  *
   12026             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   12027             :  *
   12028             :  * @return true in case of success.
   12029             :  * @since GDAL 3.8
   12030             :  */
   12031          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   12032             :                             CSLConstList papszOptions)
   12033             : {
   12034          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12035          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   12036          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   12037             : }
   12038             : 
   12039             : /************************************************************************/
   12040             : /*                      GDALGroupCreateAttribute()                      */
   12041             : /************************************************************************/
   12042             : 
   12043             : /** Create a attribute within a group.
   12044             :  *
   12045             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   12046             :  *
   12047             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12048             :  */
   12049         122 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   12050             :                                         size_t nDimensions,
   12051             :                                         const GUInt64 *panDimensions,
   12052             :                                         GDALExtendedDataTypeH hEDT,
   12053             :                                         CSLConstList papszOptions)
   12054             : {
   12055         122 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12056         122 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12057         244 :     std::vector<GUInt64> dims;
   12058         122 :     dims.reserve(nDimensions);
   12059         172 :     for (size_t i = 0; i < nDimensions; i++)
   12060          50 :         dims.push_back(panDimensions[i]);
   12061         122 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   12062         366 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12063         122 :     if (!ret)
   12064          14 :         return nullptr;
   12065         108 :     return new GDALAttributeHS(ret);
   12066             : }
   12067             : 
   12068             : /************************************************************************/
   12069             : /*                         GDALGroupDeleteAttribute()                   */
   12070             : /************************************************************************/
   12071             : 
   12072             : /** Delete an attribute from a group.
   12073             :  *
   12074             :  * After this call, if a previously obtained instance of the deleted object
   12075             :  * is still alive, no method other than for freeing it should be invoked.
   12076             :  *
   12077             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   12078             :  *
   12079             :  * @return true in case of success.
   12080             :  * @since GDAL 3.8
   12081             :  */
   12082          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   12083             :                               CSLConstList papszOptions)
   12084             : {
   12085          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12086          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   12087          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   12088          25 :                                              papszOptions);
   12089             : }
   12090             : 
   12091             : /************************************************************************/
   12092             : /*                          GDALGroupRename()                           */
   12093             : /************************************************************************/
   12094             : 
   12095             : /** Rename the group.
   12096             :  *
   12097             :  * This is not implemented by all drivers.
   12098             :  *
   12099             :  * Drivers known to implement it: MEM, netCDF.
   12100             :  *
   12101             :  * This is the same as the C++ method GDALGroup::Rename()
   12102             :  *
   12103             :  * @return true in case of success
   12104             :  * @since GDAL 3.8
   12105             :  */
   12106          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   12107             : {
   12108          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12109          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12110          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   12111             : }
   12112             : 
   12113             : /************************************************************************/
   12114             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   12115             : /************************************************************************/
   12116             : 
   12117             : /** Return a virtual group whose one dimension has been subset according to a
   12118             :  * selection.
   12119             :  *
   12120             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   12121             :  *
   12122             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   12123             :  */
   12124             : GDALGroupH
   12125          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   12126             :                                       const char *pszSelection,
   12127             :                                       CPL_UNUSED CSLConstList papszOptions)
   12128             : {
   12129          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12130          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   12131          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   12132          42 :         std::string(pszSelection));
   12133          14 :     if (!hNewGroup)
   12134           8 :         return nullptr;
   12135           6 :     return new GDALGroupHS(hNewGroup);
   12136             : }
   12137             : 
   12138             : /************************************************************************/
   12139             : /*                        GDALMDArrayRelease()                          */
   12140             : /************************************************************************/
   12141             : 
   12142             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   12143             :  *
   12144             :  * Note: when applied on a object coming from a driver, this does not
   12145             :  * destroy the object in the file, database, etc...
   12146             :  */
   12147        2017 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   12148             : {
   12149        2017 :     delete hMDArray;
   12150        2017 : }
   12151             : 
   12152             : /************************************************************************/
   12153             : /*                        GDALMDArrayGetName()                          */
   12154             : /************************************************************************/
   12155             : 
   12156             : /** Return array name.
   12157             :  *
   12158             :  * This is the same as the C++ method GDALMDArray::GetName()
   12159             :  */
   12160          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   12161             : {
   12162          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12163          83 :     return hArray->m_poImpl->GetName().c_str();
   12164             : }
   12165             : 
   12166             : /************************************************************************/
   12167             : /*                    GDALMDArrayGetFullName()                          */
   12168             : /************************************************************************/
   12169             : 
   12170             : /** Return array full name.
   12171             :  *
   12172             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   12173             :  */
   12174          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   12175             : {
   12176          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12177          50 :     return hArray->m_poImpl->GetFullName().c_str();
   12178             : }
   12179             : 
   12180             : /************************************************************************/
   12181             : /*                        GDALMDArrayGetName()                          */
   12182             : /************************************************************************/
   12183             : 
   12184             : /** Return the total number of values in the array.
   12185             :  *
   12186             :  * This is the same as the C++ method
   12187             :  * GDALAbstractMDArray::GetTotalElementsCount()
   12188             :  */
   12189           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   12190             : {
   12191           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12192           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   12193             : }
   12194             : 
   12195             : /************************************************************************/
   12196             : /*                        GDALMDArrayGetDimensionCount()                */
   12197             : /************************************************************************/
   12198             : 
   12199             : /** Return the number of dimensions.
   12200             :  *
   12201             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   12202             :  */
   12203       10368 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   12204             : {
   12205       10368 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12206       10368 :     return hArray->m_poImpl->GetDimensionCount();
   12207             : }
   12208             : 
   12209             : /************************************************************************/
   12210             : /*                        GDALMDArrayGetDimensions()                    */
   12211             : /************************************************************************/
   12212             : 
   12213             : /** Return the dimensions of the array
   12214             :  *
   12215             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   12216             :  * array itself needs to be freed, CPLFree() should be called (and
   12217             :  * GDALDimensionRelease() on individual array members).
   12218             :  *
   12219             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   12220             :  *
   12221             :  * @param hArray Array.
   12222             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12223             :  *
   12224             :  * @return an array of *pnCount dimensions.
   12225             :  */
   12226        2299 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   12227             : {
   12228        2299 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12229        2299 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12230        2299 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   12231             :     auto ret = static_cast<GDALDimensionH *>(
   12232        2299 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   12233        6465 :     for (size_t i = 0; i < dims.size(); i++)
   12234             :     {
   12235        4166 :         ret[i] = new GDALDimensionHS(dims[i]);
   12236             :     }
   12237        2299 :     *pnCount = dims.size();
   12238        2299 :     return ret;
   12239             : }
   12240             : 
   12241             : /************************************************************************/
   12242             : /*                        GDALReleaseDimensions()                       */
   12243             : /************************************************************************/
   12244             : 
   12245             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   12246             :  *
   12247             :  * @param dims return pointer of above methods
   12248             :  * @param nCount *pnCount value returned by above methods
   12249             :  */
   12250        2372 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   12251             : {
   12252        6695 :     for (size_t i = 0; i < nCount; i++)
   12253             :     {
   12254        4323 :         delete dims[i];
   12255             :     }
   12256        2372 :     CPLFree(dims);
   12257        2372 : }
   12258             : 
   12259             : /************************************************************************/
   12260             : /*                        GDALMDArrayGetDataType()                     */
   12261             : /************************************************************************/
   12262             : 
   12263             : /** Return the data type
   12264             :  *
   12265             :  * The return must be freed with GDALExtendedDataTypeRelease().
   12266             :  */
   12267        3909 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   12268             : {
   12269        3909 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12270             :     return new GDALExtendedDataTypeHS(
   12271        3909 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   12272             : }
   12273             : 
   12274             : /************************************************************************/
   12275             : /*                          GDALMDArrayRead()                           */
   12276             : /************************************************************************/
   12277             : 
   12278             : /** Read part or totality of a multidimensional array.
   12279             :  *
   12280             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   12281             :  *
   12282             :  * @return TRUE in case of success.
   12283             :  */
   12284        1956 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12285             :                     const size_t *count, const GInt64 *arrayStep,
   12286             :                     const GPtrDiff_t *bufferStride,
   12287             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   12288             :                     const void *pDstBufferAllocStart,
   12289             :                     size_t nDstBufferAllocSize)
   12290             : {
   12291        1956 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12292        1956 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12293           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12294             :     {
   12295           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12296           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12297             :     }
   12298        1956 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12299        1956 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   12300        3912 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   12301        1956 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   12302        1956 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   12303             : }
   12304             : 
   12305             : /************************************************************************/
   12306             : /*                          GDALMDArrayWrite()                           */
   12307             : /************************************************************************/
   12308             : 
   12309             : /** Write part or totality of a multidimensional array.
   12310             :  *
   12311             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   12312             :  *
   12313             :  * @return TRUE in case of success.
   12314             :  */
   12315         558 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12316             :                      const size_t *count, const GInt64 *arrayStep,
   12317             :                      const GPtrDiff_t *bufferStride,
   12318             :                      GDALExtendedDataTypeH bufferDataType,
   12319             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   12320             :                      size_t nSrcBufferAllocSize)
   12321             : {
   12322         558 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12323         558 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12324           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12325             :     {
   12326           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12327           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12328             :     }
   12329         558 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12330         558 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   12331        1116 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   12332         558 :                                    bufferStride, *(bufferDataType->m_poImpl),
   12333             :                                    pSrcBuffer, pSrcBufferAllocStart,
   12334         558 :                                    nSrcBufferAllocSize);
   12335             : }
   12336             : 
   12337             : /************************************************************************/
   12338             : /*                       GDALMDArrayAdviseRead()                        */
   12339             : /************************************************************************/
   12340             : 
   12341             : /** Advise driver of upcoming read requests.
   12342             :  *
   12343             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12344             :  *
   12345             :  * @return TRUE in case of success.
   12346             :  *
   12347             :  * @since GDAL 3.2
   12348             :  */
   12349           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12350             :                           const size_t *count)
   12351             : {
   12352           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   12353             : }
   12354             : 
   12355             : /************************************************************************/
   12356             : /*                      GDALMDArrayAdviseReadEx()                       */
   12357             : /************************************************************************/
   12358             : 
   12359             : /** Advise driver of upcoming read requests.
   12360             :  *
   12361             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12362             :  *
   12363             :  * @return TRUE in case of success.
   12364             :  *
   12365             :  * @since GDAL 3.4
   12366             :  */
   12367          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12368             :                             const size_t *count, CSLConstList papszOptions)
   12369             : {
   12370          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12371          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   12372             : }
   12373             : 
   12374             : /************************************************************************/
   12375             : /*                         GDALMDArrayGetAttribute()                    */
   12376             : /************************************************************************/
   12377             : 
   12378             : /** Return an attribute by its name.
   12379             :  *
   12380             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   12381             :  *
   12382             :  * The returned attribute must be freed with GDALAttributeRelease().
   12383             :  */
   12384         119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   12385             : {
   12386         119 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12387         119 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12388         357 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   12389         119 :     if (attr)
   12390         110 :         return new GDALAttributeHS(attr);
   12391           9 :     return nullptr;
   12392             : }
   12393             : 
   12394             : /************************************************************************/
   12395             : /*                        GDALMDArrayGetAttributes()                    */
   12396             : /************************************************************************/
   12397             : 
   12398             : /** Return the list of attributes contained in this array.
   12399             :  *
   12400             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   12401             :  * array itself needs to be freed, CPLFree() should be called (and
   12402             :  * GDALAttributeRelease() on individual array members).
   12403             :  *
   12404             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   12405             :  *
   12406             :  * @param hArray Array.
   12407             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12408             :  * @param papszOptions Driver specific options determining how attributes
   12409             :  * should be retrieved. Pass nullptr for default behavior.
   12410             :  *
   12411             :  * @return an array of *pnCount attributes.
   12412             :  */
   12413          58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   12414             :                                          CSLConstList papszOptions)
   12415             : {
   12416          58 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12417          58 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12418          58 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   12419             :     auto ret = static_cast<GDALAttributeH *>(
   12420          58 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   12421         187 :     for (size_t i = 0; i < attrs.size(); i++)
   12422             :     {
   12423         129 :         ret[i] = new GDALAttributeHS(attrs[i]);
   12424             :     }
   12425          58 :     *pnCount = attrs.size();
   12426          58 :     return ret;
   12427             : }
   12428             : 
   12429             : /************************************************************************/
   12430             : /*                       GDALMDArrayCreateAttribute()                   */
   12431             : /************************************************************************/
   12432             : 
   12433             : /** Create a attribute within an array.
   12434             :  *
   12435             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   12436             :  *
   12437             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12438             :  */
   12439         160 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   12440             :                                           const char *pszName,
   12441             :                                           size_t nDimensions,
   12442             :                                           const GUInt64 *panDimensions,
   12443             :                                           GDALExtendedDataTypeH hEDT,
   12444             :                                           CSLConstList papszOptions)
   12445             : {
   12446         160 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12447         160 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12448         160 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12449         320 :     std::vector<GUInt64> dims;
   12450         160 :     dims.reserve(nDimensions);
   12451         195 :     for (size_t i = 0; i < nDimensions; i++)
   12452          35 :         dims.push_back(panDimensions[i]);
   12453         160 :     auto ret = hArray->m_poImpl->CreateAttribute(
   12454         480 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12455         160 :     if (!ret)
   12456           9 :         return nullptr;
   12457         151 :     return new GDALAttributeHS(ret);
   12458             : }
   12459             : 
   12460             : /************************************************************************/
   12461             : /*                       GDALMDArrayDeleteAttribute()                   */
   12462             : /************************************************************************/
   12463             : 
   12464             : /** Delete an attribute from an array.
   12465             :  *
   12466             :  * After this call, if a previously obtained instance of the deleted object
   12467             :  * is still alive, no method other than for freeing it should be invoked.
   12468             :  *
   12469             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   12470             :  *
   12471             :  * @return true in case of success.
   12472             :  * @since GDAL 3.8
   12473             :  */
   12474          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   12475             :                                 CSLConstList papszOptions)
   12476             : {
   12477          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   12478          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   12479          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   12480          24 :                                              papszOptions);
   12481             : }
   12482             : 
   12483             : /************************************************************************/
   12484             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   12485             : /************************************************************************/
   12486             : 
   12487             : /** Return the nodata value as a "raw" value.
   12488             :  *
   12489             :  * The value returned might be nullptr in case of no nodata value. When
   12490             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12491             :  * bytes is GetDataType().GetSize().
   12492             :  *
   12493             :  * The returned value should not be modified or freed.
   12494             :  *
   12495             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   12496             :  *
   12497             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   12498             :  */
   12499          76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   12500             : {
   12501          76 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12502          76 :     return hArray->m_poImpl->GetRawNoDataValue();
   12503             : }
   12504             : 
   12505             : /************************************************************************/
   12506             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   12507             : /************************************************************************/
   12508             : 
   12509             : /** Return the nodata value as a double.
   12510             :  *
   12511             :  * The value returned might be nullptr in case of no nodata value. When
   12512             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12513             :  * bytes is GetDataType().GetSize().
   12514             :  *
   12515             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   12516             :  *
   12517             :  * @param hArray Array handle.
   12518             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12519             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   12520             :  *
   12521             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   12522             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   12523             :  * will be set to false then).
   12524             :  */
   12525         120 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   12526             :                                          int *pbHasNoDataValue)
   12527             : {
   12528         120 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12529         120 :     bool bHasNodataValue = false;
   12530         120 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   12531         120 :     if (pbHasNoDataValue)
   12532         120 :         *pbHasNoDataValue = bHasNodataValue;
   12533         120 :     return ret;
   12534             : }
   12535             : 
   12536             : /************************************************************************/
   12537             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   12538             : /************************************************************************/
   12539             : 
   12540             : /** Return the nodata value as a Int64.
   12541             :  *
   12542             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12543             :  *
   12544             :  * @param hArray Array handle.
   12545             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12546             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   12547             :  *
   12548             :  * @return the nodata value as a Int64.
   12549             :  * @since GDAL 3.5
   12550             :  */
   12551          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   12552             :                                          int *pbHasNoDataValue)
   12553             : {
   12554          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12555          11 :     bool bHasNodataValue = false;
   12556          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   12557          11 :     if (pbHasNoDataValue)
   12558          11 :         *pbHasNoDataValue = bHasNodataValue;
   12559          11 :     return ret;
   12560             : }
   12561             : 
   12562             : /************************************************************************/
   12563             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   12564             : /************************************************************************/
   12565             : 
   12566             : /** Return the nodata value as a UInt64.
   12567             :  *
   12568             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12569             :  *
   12570             :  * @param hArray Array handle.
   12571             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12572             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   12573             :  *
   12574             :  * @return the nodata value as a UInt64.
   12575             :  * @since GDAL 3.5
   12576             :  */
   12577           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12578             :                                            int *pbHasNoDataValue)
   12579             : {
   12580           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12581           7 :     bool bHasNodataValue = false;
   12582           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   12583           7 :     if (pbHasNoDataValue)
   12584           7 :         *pbHasNoDataValue = bHasNodataValue;
   12585           7 :     return ret;
   12586             : }
   12587             : 
   12588             : /************************************************************************/
   12589             : /*                     GDALMDArraySetRawNoDataValue()                   */
   12590             : /************************************************************************/
   12591             : 
   12592             : /** Set the nodata value as a "raw" value.
   12593             :  *
   12594             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   12595             :  * void*).
   12596             :  *
   12597             :  * @return TRUE in case of success.
   12598             :  */
   12599          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   12600             : {
   12601          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12602          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   12603             : }
   12604             : 
   12605             : /************************************************************************/
   12606             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   12607             : /************************************************************************/
   12608             : 
   12609             : /** Set the nodata value as a double.
   12610             :  *
   12611             :  * If the natural data type of the attribute/array is not double, type
   12612             :  * conversion will occur to the type returned by GetDataType().
   12613             :  *
   12614             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   12615             :  *
   12616             :  * @return TRUE in case of success.
   12617             :  */
   12618          51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   12619             : {
   12620          51 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12621          51 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   12622             : }
   12623             : 
   12624             : /************************************************************************/
   12625             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   12626             : /************************************************************************/
   12627             : 
   12628             : /** Set the nodata value as a Int64.
   12629             :  *
   12630             :  * If the natural data type of the attribute/array is not Int64, type conversion
   12631             :  * will occur to the type returned by GetDataType().
   12632             :  *
   12633             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   12634             :  *
   12635             :  * @return TRUE in case of success.
   12636             :  * @since GDAL 3.5
   12637             :  */
   12638           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   12639             : {
   12640           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12641           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12642             : }
   12643             : 
   12644             : /************************************************************************/
   12645             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   12646             : /************************************************************************/
   12647             : 
   12648             : /** Set the nodata value as a UInt64.
   12649             :  *
   12650             :  * If the natural data type of the attribute/array is not UInt64, type
   12651             :  * conversion will occur to the type returned by GetDataType().
   12652             :  *
   12653             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   12654             :  *
   12655             :  * @return TRUE in case of success.
   12656             :  * @since GDAL 3.5
   12657             :  */
   12658           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12659             :                                       uint64_t nNoDataValue)
   12660             : {
   12661           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12662           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12663             : }
   12664             : 
   12665             : /************************************************************************/
   12666             : /*                        GDALMDArrayResize()                           */
   12667             : /************************************************************************/
   12668             : 
   12669             : /** Resize an array to new dimensions.
   12670             :  *
   12671             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   12672             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   12673             :  *
   12674             :  * Resizing a dimension used in other arrays will cause those other arrays
   12675             :  * to be resized.
   12676             :  *
   12677             :  * This is the same as the C++ method GDALMDArray::Resize().
   12678             :  *
   12679             :  * @param hArray Array.
   12680             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   12681             :  *                       new size of each indexing dimension.
   12682             :  * @param papszOptions Options. (Driver specific)
   12683             :  * @return true in case of success.
   12684             :  * @since GDAL 3.7
   12685             :  */
   12686          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12687             :                        CSLConstList papszOptions)
   12688             : {
   12689          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12690          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12691          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12692         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12693             :     {
   12694          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12695             :     }
   12696          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12697             : }
   12698             : 
   12699             : /************************************************************************/
   12700             : /*                          GDALMDArraySetScale()                       */
   12701             : /************************************************************************/
   12702             : 
   12703             : /** Set the scale value to apply to raw values.
   12704             :  *
   12705             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12706             :  *
   12707             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12708             :  *
   12709             :  * @return TRUE in case of success.
   12710             :  */
   12711           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   12712             : {
   12713           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12714           0 :     return hArray->m_poImpl->SetScale(dfScale);
   12715             : }
   12716             : 
   12717             : /************************************************************************/
   12718             : /*                        GDALMDArraySetScaleEx()                       */
   12719             : /************************************************************************/
   12720             : 
   12721             : /** Set the scale value to apply to raw values.
   12722             :  *
   12723             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12724             :  *
   12725             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12726             :  *
   12727             :  * @return TRUE in case of success.
   12728             :  * @since GDAL 3.3
   12729             :  */
   12730          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   12731             :                           GDALDataType eStorageType)
   12732             : {
   12733          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12734          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   12735             : }
   12736             : 
   12737             : /************************************************************************/
   12738             : /*                          GDALMDArraySetOffset()                       */
   12739             : /************************************************************************/
   12740             : 
   12741             : /** Set the scale value to apply to raw values.
   12742             :  *
   12743             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12744             :  *
   12745             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12746             :  *
   12747             :  * @return TRUE in case of success.
   12748             :  */
   12749           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   12750             : {
   12751           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12752           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   12753             : }
   12754             : 
   12755             : /************************************************************************/
   12756             : /*                       GDALMDArraySetOffsetEx()                       */
   12757             : /************************************************************************/
   12758             : 
   12759             : /** Set the scale value to apply to raw values.
   12760             :  *
   12761             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   12762             :  *
   12763             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12764             :  *
   12765             :  * @return TRUE in case of success.
   12766             :  * @since GDAL 3.3
   12767             :  */
   12768          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   12769             :                            GDALDataType eStorageType)
   12770             : {
   12771          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12772          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   12773             : }
   12774             : 
   12775             : /************************************************************************/
   12776             : /*                          GDALMDArrayGetScale()                       */
   12777             : /************************************************************************/
   12778             : 
   12779             : /** Get the scale value to apply to raw values.
   12780             :  *
   12781             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12782             :  *
   12783             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12784             :  *
   12785             :  * @return the scale value
   12786             :  */
   12787         103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   12788             : {
   12789         103 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12790         103 :     bool bHasValue = false;
   12791         103 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   12792         103 :     if (pbHasValue)
   12793         103 :         *pbHasValue = bHasValue;
   12794         103 :     return dfRet;
   12795             : }
   12796             : 
   12797             : /************************************************************************/
   12798             : /*                        GDALMDArrayGetScaleEx()                       */
   12799             : /************************************************************************/
   12800             : 
   12801             : /** Get the scale value to apply to raw values.
   12802             :  *
   12803             :  * unscaled_value = raw_value * GetScale() + GetScale()
   12804             :  *
   12805             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12806             :  *
   12807             :  * @return the scale value
   12808             :  * @since GDAL 3.3
   12809             :  */
   12810           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   12811             :                              GDALDataType *peStorageType)
   12812             : {
   12813           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12814           5 :     bool bHasValue = false;
   12815           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   12816           5 :     if (pbHasValue)
   12817           5 :         *pbHasValue = bHasValue;
   12818           5 :     return dfRet;
   12819             : }
   12820             : 
   12821             : /************************************************************************/
   12822             : /*                          GDALMDArrayGetOffset()                      */
   12823             : /************************************************************************/
   12824             : 
   12825             : /** Get the scale value to apply to raw values.
   12826             :  *
   12827             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12828             :  *
   12829             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12830             :  *
   12831             :  * @return the scale value
   12832             :  */
   12833         100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   12834             : {
   12835         100 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12836         100 :     bool bHasValue = false;
   12837         100 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   12838         100 :     if (pbHasValue)
   12839         100 :         *pbHasValue = bHasValue;
   12840         100 :     return dfRet;
   12841             : }
   12842             : 
   12843             : /************************************************************************/
   12844             : /*                        GDALMDArrayGetOffsetEx()                      */
   12845             : /************************************************************************/
   12846             : 
   12847             : /** Get the scale value to apply to raw values.
   12848             :  *
   12849             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12850             :  *
   12851             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12852             :  *
   12853             :  * @return the scale value
   12854             :  * @since GDAL 3.3
   12855             :  */
   12856           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12857             :                               GDALDataType *peStorageType)
   12858             : {
   12859           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12860           5 :     bool bHasValue = false;
   12861           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12862           5 :     if (pbHasValue)
   12863           5 :         *pbHasValue = bHasValue;
   12864           5 :     return dfRet;
   12865             : }
   12866             : 
   12867             : /************************************************************************/
   12868             : /*                      GDALMDArrayGetBlockSize()                       */
   12869             : /************************************************************************/
   12870             : 
   12871             : /** Return the "natural" block size of the array along all dimensions.
   12872             :  *
   12873             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12874             :  * aligned on those tile/block boundaries will be more efficient.
   12875             :  *
   12876             :  * The returned number of elements in the vector is the same as
   12877             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12878             :  * the natural block size along the considered dimension.
   12879             :  * "Flat" arrays will typically return a vector of values set to 0.
   12880             :  *
   12881             :  * The default implementation will return a vector of values set to 0.
   12882             :  *
   12883             :  * This method is used by GetProcessingChunkSize().
   12884             :  *
   12885             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
   12886             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12887             :  * allocation capabilities.
   12888             :  *
   12889             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12890             :  *
   12891             :  * @return the block size, in number of elements along each dimension.
   12892             :  */
   12893          93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12894             : {
   12895          93 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12896          93 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12897          93 :     auto res = hArray->m_poImpl->GetBlockSize();
   12898          93 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12899         285 :     for (size_t i = 0; i < res.size(); i++)
   12900             :     {
   12901         192 :         ret[i] = res[i];
   12902             :     }
   12903          93 :     *pnCount = res.size();
   12904          93 :     return ret;
   12905             : }
   12906             : 
   12907             : /***********************************************************************/
   12908             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12909             : /************************************************************************/
   12910             : 
   12911             : /** \brief Return an optimal chunk size for read/write operations, given the
   12912             :  * natural block size and memory constraints specified.
   12913             :  *
   12914             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12915             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12916             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12917             :  * returned by this method).
   12918             :  *
   12919             :  * This is the same as the C++ method
   12920             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12921             :  *
   12922             :  * @param hArray Array.
   12923             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12924             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12925             :  * chunk.
   12926             :  *
   12927             :  * @return the chunk size, in number of elements along each dimension.
   12928             :  */
   12929             : 
   12930           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12931             :                                           size_t nMaxChunkMemory)
   12932             : {
   12933           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12934           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12935           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12936           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12937           3 :     for (size_t i = 0; i < res.size(); i++)
   12938             :     {
   12939           2 :         ret[i] = res[i];
   12940             :     }
   12941           1 :     *pnCount = res.size();
   12942           1 :     return ret;
   12943             : }
   12944             : 
   12945             : /************************************************************************/
   12946             : /*                     GDALMDArrayGetStructuralInfo()                   */
   12947             : /************************************************************************/
   12948             : 
   12949             : /** Return structural information on the array.
   12950             :  *
   12951             :  * This may be the compression, etc..
   12952             :  *
   12953             :  * The return value should not be freed and is valid until GDALMDArray is
   12954             :  * released or this function called again.
   12955             :  *
   12956             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   12957             :  */
   12958          15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   12959             : {
   12960          15 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12961          15 :     return hArray->m_poImpl->GetStructuralInfo();
   12962             : }
   12963             : 
   12964             : /************************************************************************/
   12965             : /*                        GDALMDArrayGetView()                          */
   12966             : /************************************************************************/
   12967             : 
   12968             : /** Return a view of the array using slicing or field access.
   12969             :  *
   12970             :  * The returned object should be released with GDALMDArrayRelease().
   12971             :  *
   12972             :  * This is the same as the C++ method GDALMDArray::GetView().
   12973             :  */
   12974         430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   12975             : {
   12976         430 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12977         430 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   12978        1290 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   12979         430 :     if (!sliced)
   12980          22 :         return nullptr;
   12981         408 :     return new GDALMDArrayHS(sliced);
   12982             : }
   12983             : 
   12984             : /************************************************************************/
   12985             : /*                       GDALMDArrayTranspose()                         */
   12986             : /************************************************************************/
   12987             : 
   12988             : /** Return a view of the array whose axis have been reordered.
   12989             :  *
   12990             :  * The returned object should be released with GDALMDArrayRelease().
   12991             :  *
   12992             :  * This is the same as the C++ method GDALMDArray::Transpose().
   12993             :  */
   12994          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   12995             :                                   const int *panMapNewAxisToOldAxis)
   12996             : {
   12997          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12998          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   12999          44 :     if (nNewAxisCount)
   13000             :     {
   13001          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   13002             :                nNewAxisCount * sizeof(int));
   13003             :     }
   13004          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   13005          44 :     if (!reordered)
   13006           7 :         return nullptr;
   13007          37 :     return new GDALMDArrayHS(reordered);
   13008             : }
   13009             : 
   13010             : /************************************************************************/
   13011             : /*                      GDALMDArrayGetUnscaled()                        */
   13012             : /************************************************************************/
   13013             : 
   13014             : /** Return an array that is the unscaled version of the current one.
   13015             :  *
   13016             :  * That is each value of the unscaled array will be
   13017             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13018             :  *
   13019             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   13020             :  * from unscaled values to raw values.
   13021             :  *
   13022             :  * The returned object should be released with GDALMDArrayRelease().
   13023             :  *
   13024             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   13025             :  */
   13026          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   13027             : {
   13028          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13029          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   13030          13 :     if (!unscaled)
   13031           0 :         return nullptr;
   13032          13 :     return new GDALMDArrayHS(unscaled);
   13033             : }
   13034             : 
   13035             : /************************************************************************/
   13036             : /*                          GDALMDArrayGetMask()                         */
   13037             : /************************************************************************/
   13038             : 
   13039             : /** Return an array that is a mask for the current array
   13040             :  *
   13041             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   13042             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   13043             :  *
   13044             :  * The returned object should be released with GDALMDArrayRelease().
   13045             :  *
   13046             :  * This is the same as the C++ method GDALMDArray::GetMask().
   13047             :  */
   13048          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   13049             : {
   13050          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13051          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   13052          35 :     if (!unscaled)
   13053           7 :         return nullptr;
   13054          28 :     return new GDALMDArrayHS(unscaled);
   13055             : }
   13056             : 
   13057             : /************************************************************************/
   13058             : /*                   GDALMDArrayGetResampled()                          */
   13059             : /************************************************************************/
   13060             : 
   13061             : /** Return an array that is a resampled / reprojected view of the current array
   13062             :  *
   13063             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   13064             :  *
   13065             :  * Currently this method can only resample along the last 2 dimensions, unless
   13066             :  * orthorectifying a NASA EMIT dataset.
   13067             :  *
   13068             :  * The returned object should be released with GDALMDArrayRelease().
   13069             :  *
   13070             :  * @since 3.4
   13071             :  */
   13072          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   13073             :                                      const GDALDimensionH *pahNewDims,
   13074             :                                      GDALRIOResampleAlg resampleAlg,
   13075             :                                      OGRSpatialReferenceH hTargetSRS,
   13076             :                                      CSLConstList papszOptions)
   13077             : {
   13078          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13079          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   13080          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   13081         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   13082             :     {
   13083          78 :         if (pahNewDims[i])
   13084           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   13085             :     }
   13086          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   13087          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   13088          68 :         papszOptions);
   13089          34 :     if (!poNewArray)
   13090           8 :         return nullptr;
   13091          26 :     return new GDALMDArrayHS(poNewArray);
   13092             : }
   13093             : 
   13094             : /************************************************************************/
   13095             : /*                      GDALMDArraySetUnit()                            */
   13096             : /************************************************************************/
   13097             : 
   13098             : /** Set the variable unit.
   13099             :  *
   13100             :  * Values should conform as much as possible with those allowed by
   13101             :  * the NetCDF CF conventions:
   13102             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13103             :  * but others might be returned.
   13104             :  *
   13105             :  * Few examples are "meter", "degrees", "second", ...
   13106             :  * Empty value means unknown.
   13107             :  *
   13108             :  * This is the same as the C function GDALMDArraySetUnit()
   13109             :  *
   13110             :  * @param hArray array.
   13111             :  * @param pszUnit unit name.
   13112             :  * @return TRUE in case of success.
   13113             :  */
   13114          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   13115             : {
   13116          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13117          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   13118             : }
   13119             : 
   13120             : /************************************************************************/
   13121             : /*                      GDALMDArrayGetUnit()                            */
   13122             : /************************************************************************/
   13123             : 
   13124             : /** Return the array unit.
   13125             :  *
   13126             :  * Values should conform as much as possible with those allowed by
   13127             :  * the NetCDF CF conventions:
   13128             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13129             :  * but others might be returned.
   13130             :  *
   13131             :  * Few examples are "meter", "degrees", "second", ...
   13132             :  * Empty value means unknown.
   13133             :  *
   13134             :  * The return value should not be freed and is valid until GDALMDArray is
   13135             :  * released or this function called again.
   13136             :  *
   13137             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   13138             :  */
   13139         111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   13140             : {
   13141         111 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13142         111 :     return hArray->m_poImpl->GetUnit().c_str();
   13143             : }
   13144             : 
   13145             : /************************************************************************/
   13146             : /*                      GDALMDArrayGetSpatialRef()                      */
   13147             : /************************************************************************/
   13148             : 
   13149             : /** Assign a spatial reference system object to the array.
   13150             :  *
   13151             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   13152             :  * @return TRUE in case of success.
   13153             :  */
   13154          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   13155             : {
   13156          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13157          60 :     return hArray->m_poImpl->SetSpatialRef(
   13158          60 :         OGRSpatialReference::FromHandle(hSRS));
   13159             : }
   13160             : 
   13161             : /************************************************************************/
   13162             : /*                      GDALMDArrayGetSpatialRef()                      */
   13163             : /************************************************************************/
   13164             : 
   13165             : /** Return the spatial reference system object associated with the array.
   13166             :  *
   13167             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   13168             :  *
   13169             :  * The returned object must be freed with OSRDestroySpatialReference().
   13170             :  */
   13171          77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   13172             : {
   13173          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13174          77 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   13175          77 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   13176             : }
   13177             : 
   13178             : /************************************************************************/
   13179             : /*                      GDALMDArrayGetStatistics()                      */
   13180             : /************************************************************************/
   13181             : 
   13182             : /**
   13183             :  * \brief Fetch statistics.
   13184             :  *
   13185             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   13186             :  *
   13187             :  * @since GDAL 3.2
   13188             :  */
   13189             : 
   13190          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   13191             :                                 int bApproxOK, int bForce, double *pdfMin,
   13192             :                                 double *pdfMax, double *pdfMean,
   13193             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   13194             :                                 GDALProgressFunc pfnProgress,
   13195             :                                 void *pProgressData)
   13196             : {
   13197          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   13198          30 :     return hArray->m_poImpl->GetStatistics(
   13199          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   13200          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   13201             : }
   13202             : 
   13203             : /************************************************************************/
   13204             : /*                      GDALMDArrayComputeStatistics()                  */
   13205             : /************************************************************************/
   13206             : 
   13207             : /**
   13208             :  * \brief Compute statistics.
   13209             :  *
   13210             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13211             :  *
   13212             :  * @since GDAL 3.2
   13213             :  * @see GDALMDArrayComputeStatisticsEx()
   13214             :  */
   13215             : 
   13216           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13217             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   13218             :                                  double *pdfMean, double *pdfStdDev,
   13219             :                                  GUInt64 *pnValidCount,
   13220             :                                  GDALProgressFunc pfnProgress,
   13221             :                                  void *pProgressData)
   13222             : {
   13223           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13224           0 :     return hArray->m_poImpl->ComputeStatistics(
   13225           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13226           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   13227             : }
   13228             : 
   13229             : /************************************************************************/
   13230             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   13231             : /************************************************************************/
   13232             : 
   13233             : /**
   13234             :  * \brief Compute statistics.
   13235             :  *
   13236             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   13237             :  *
   13238             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13239             :  *
   13240             :  * @since GDAL 3.8
   13241             :  */
   13242             : 
   13243           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13244             :                                    int bApproxOK, double *pdfMin,
   13245             :                                    double *pdfMax, double *pdfMean,
   13246             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   13247             :                                    GDALProgressFunc pfnProgress,
   13248             :                                    void *pProgressData,
   13249             :                                    CSLConstList papszOptions)
   13250             : {
   13251           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13252           8 :     return hArray->m_poImpl->ComputeStatistics(
   13253           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13254           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   13255             : }
   13256             : 
   13257             : /************************************************************************/
   13258             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   13259             : /************************************************************************/
   13260             : 
   13261             : /** Return coordinate variables.
   13262             :  *
   13263             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   13264             :  * itself needs to be freed, CPLFree() should be called (and
   13265             :  * GDALMDArrayRelease() on individual array members).
   13266             :  *
   13267             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   13268             :  *
   13269             :  * @param hArray Array.
   13270             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13271             :  *
   13272             :  * @return an array of *pnCount arrays.
   13273             :  * @since 3.4
   13274             :  */
   13275          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   13276             :                                                 size_t *pnCount)
   13277             : {
   13278          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13279          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13280          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   13281             :     auto ret = static_cast<GDALMDArrayH *>(
   13282          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   13283          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   13284             :     {
   13285          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   13286             :     }
   13287          13 :     *pnCount = coordinates.size();
   13288          13 :     return ret;
   13289             : }
   13290             : 
   13291             : /************************************************************************/
   13292             : /*                     GDALMDArrayGetGridded()                          */
   13293             : /************************************************************************/
   13294             : 
   13295             : /** Return a gridded array from scattered point data, that is from an array
   13296             :  * whose last dimension is the indexing variable of X and Y arrays.
   13297             :  *
   13298             :  * The returned object should be released with GDALMDArrayRelease().
   13299             :  *
   13300             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   13301             :  *
   13302             :  * @since GDAL 3.7
   13303             :  */
   13304          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   13305             :                                    const char *pszGridOptions,
   13306             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   13307             :                                    CSLConstList papszOptions)
   13308             : {
   13309          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13310          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   13311          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   13312          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   13313          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   13314          22 :     if (!gridded)
   13315          19 :         return nullptr;
   13316           3 :     return new GDALMDArrayHS(gridded);
   13317             : }
   13318             : 
   13319             : /************************************************************************/
   13320             : /*                      GDALMDArrayGetMeshGrid()                        */
   13321             : /************************************************************************/
   13322             : 
   13323             : /** Return a list of multidimensional arrays from a list of one-dimensional
   13324             :  * arrays.
   13325             :  *
   13326             :  * This is typically used to transform one-dimensional longitude, latitude
   13327             :  * arrays into 2D ones.
   13328             :  *
   13329             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   13330             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   13331             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   13332             :  * repeated to fill the matrix along the first dimension for x1, the second
   13333             :  * for x2 and so on.
   13334             :  *
   13335             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   13336             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   13337             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   13338             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   13339             :  *
   13340             :  * and
   13341             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   13342             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   13343             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   13344             :  *
   13345             :  * The currently supported options are:
   13346             :  * <ul>
   13347             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   13348             :  * output.
   13349             :  * </li>
   13350             :  * </ul>
   13351             :  *
   13352             :  * This is the same as
   13353             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   13354             :  * function.
   13355             :  *
   13356             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   13357             :  * If only the array itself needs to be freed, CPLFree() should be called
   13358             :  * (and GDALMDArrayRelease() on individual array members).
   13359             :  *
   13360             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   13361             :  *
   13362             :  * @param pahInputArrays Input arrays
   13363             :  * @param nCountInputArrays Number of input arrays
   13364             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   13365             :  * @param papszOptions NULL, or NULL terminated list of options.
   13366             :  *
   13367             :  * @return an array of *pnCountOutputArrays arrays.
   13368             :  * @since 3.10
   13369             :  */
   13370           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   13371             :                                      size_t nCountInputArrays,
   13372             :                                      size_t *pnCountOutputArrays,
   13373             :                                      CSLConstList papszOptions)
   13374             : {
   13375           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   13376           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   13377             : 
   13378          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   13379          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   13380          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   13381             : 
   13382             :     const auto apoOutputArrays =
   13383           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   13384             :     auto ret = static_cast<GDALMDArrayH *>(
   13385           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   13386          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   13387             :     {
   13388          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   13389             :     }
   13390           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   13391           7 :     return ret;
   13392             : }
   13393             : 
   13394             : /************************************************************************/
   13395             : /*                        GDALReleaseArrays()                           */
   13396             : /************************************************************************/
   13397             : 
   13398             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   13399             :  *
   13400             :  * @param arrays return pointer of above methods
   13401             :  * @param nCount *pnCount value returned by above methods
   13402             :  */
   13403          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   13404             : {
   13405          46 :     for (size_t i = 0; i < nCount; i++)
   13406             :     {
   13407          26 :         delete arrays[i];
   13408             :     }
   13409          20 :     CPLFree(arrays);
   13410          20 : }
   13411             : 
   13412             : /************************************************************************/
   13413             : /*                           GDALMDArrayCache()                         */
   13414             : /************************************************************************/
   13415             : 
   13416             : /**
   13417             :  * \brief Cache the content of the array into an auxiliary filename.
   13418             :  *
   13419             :  * This is the same as the C++ method GDALMDArray::Cache().
   13420             :  *
   13421             :  * @since GDAL 3.4
   13422             :  */
   13423             : 
   13424           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   13425             : {
   13426           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13427           7 :     return hArray->m_poImpl->Cache(papszOptions);
   13428             : }
   13429             : 
   13430             : /************************************************************************/
   13431             : /*                       GDALMDArrayRename()                           */
   13432             : /************************************************************************/
   13433             : 
   13434             : /** Rename the array.
   13435             :  *
   13436             :  * This is not implemented by all drivers.
   13437             :  *
   13438             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   13439             :  *
   13440             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13441             :  *
   13442             :  * @return true in case of success
   13443             :  * @since GDAL 3.8
   13444             :  */
   13445          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   13446             : {
   13447          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   13448          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13449          28 :     return hArray->m_poImpl->Rename(pszNewName);
   13450             : }
   13451             : 
   13452             : /************************************************************************/
   13453             : /*                        GDALAttributeRelease()                        */
   13454             : /************************************************************************/
   13455             : 
   13456             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   13457             :  *
   13458             :  * Note: when applied on a object coming from a driver, this does not
   13459             :  * destroy the object in the file, database, etc...
   13460             :  */
   13461         732 : void GDALAttributeRelease(GDALAttributeH hAttr)
   13462             : {
   13463         732 :     delete hAttr;
   13464         732 : }
   13465             : 
   13466             : /************************************************************************/
   13467             : /*                        GDALAttributeGetName()                        */
   13468             : /************************************************************************/
   13469             : 
   13470             : /** Return the name of the attribute.
   13471             :  *
   13472             :  * The returned pointer is valid until hAttr is released.
   13473             :  *
   13474             :  * This is the same as the C++ method GDALAttribute::GetName().
   13475             :  */
   13476         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   13477             : {
   13478         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13479         361 :     return hAttr->m_poImpl->GetName().c_str();
   13480             : }
   13481             : 
   13482             : /************************************************************************/
   13483             : /*                      GDALAttributeGetFullName()                      */
   13484             : /************************************************************************/
   13485             : 
   13486             : /** Return the full name of the attribute.
   13487             :  *
   13488             :  * The returned pointer is valid until hAttr is released.
   13489             :  *
   13490             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   13491             :  */
   13492          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   13493             : {
   13494          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13495          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   13496             : }
   13497             : 
   13498             : /************************************************************************/
   13499             : /*                   GDALAttributeGetTotalElementsCount()               */
   13500             : /************************************************************************/
   13501             : 
   13502             : /** Return the total number of values in the attribute.
   13503             :  *
   13504             :  * This is the same as the C++ method
   13505             :  * GDALAbstractMDArray::GetTotalElementsCount()
   13506             :  */
   13507         176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   13508             : {
   13509         176 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13510         176 :     return hAttr->m_poImpl->GetTotalElementsCount();
   13511             : }
   13512             : 
   13513             : /************************************************************************/
   13514             : /*                    GDALAttributeGetDimensionCount()                */
   13515             : /************************************************************************/
   13516             : 
   13517             : /** Return the number of dimensions.
   13518             :  *
   13519             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   13520             :  */
   13521          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   13522             : {
   13523          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13524          12 :     return hAttr->m_poImpl->GetDimensionCount();
   13525             : }
   13526             : 
   13527             : /************************************************************************/
   13528             : /*                       GDALAttributeGetDimensionsSize()                */
   13529             : /************************************************************************/
   13530             : 
   13531             : /** Return the dimension sizes of the attribute.
   13532             :  *
   13533             :  * The returned array must be freed with CPLFree()
   13534             :  *
   13535             :  * @param hAttr Attribute.
   13536             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13537             :  *
   13538             :  * @return an array of *pnCount values.
   13539             :  */
   13540          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   13541             : {
   13542          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13543          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13544          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   13545          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   13546          22 :     for (size_t i = 0; i < dims.size(); i++)
   13547             :     {
   13548          11 :         ret[i] = dims[i]->GetSize();
   13549             :     }
   13550          11 :     *pnCount = dims.size();
   13551          11 :     return ret;
   13552             : }
   13553             : 
   13554             : /************************************************************************/
   13555             : /*                       GDALAttributeGetDataType()                     */
   13556             : /************************************************************************/
   13557             : 
   13558             : /** Return the data type
   13559             :  *
   13560             :  * The return must be freed with GDALExtendedDataTypeRelease().
   13561             :  */
   13562         429 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   13563             : {
   13564         429 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13565             :     return new GDALExtendedDataTypeHS(
   13566         429 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   13567             : }
   13568             : 
   13569             : /************************************************************************/
   13570             : /*                       GDALAttributeReadAsRaw()                       */
   13571             : /************************************************************************/
   13572             : 
   13573             : /** Return the raw value of an attribute.
   13574             :  *
   13575             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   13576             :  *
   13577             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   13578             :  *
   13579             :  * @param hAttr Attribute.
   13580             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   13581             :  *
   13582             :  * @return a buffer of *pnSize bytes.
   13583             :  */
   13584           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   13585             : {
   13586           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13587           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   13588          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   13589           6 :     *pnSize = res.size();
   13590           6 :     auto ret = res.StealData();
   13591           6 :     if (!ret)
   13592             :     {
   13593           0 :         *pnSize = 0;
   13594           0 :         return nullptr;
   13595             :     }
   13596           6 :     return ret;
   13597             : }
   13598             : 
   13599             : /************************************************************************/
   13600             : /*                       GDALAttributeFreeRawResult()                   */
   13601             : /************************************************************************/
   13602             : 
   13603             : /** Free the return of GDALAttributeAsRaw()
   13604             :  */
   13605           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   13606             :                                 CPL_UNUSED size_t nSize)
   13607             : {
   13608           6 :     VALIDATE_POINTER0(hAttr, __func__);
   13609           6 :     if (raw)
   13610             :     {
   13611           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   13612           6 :         const auto nDTSize(dt.GetSize());
   13613           6 :         GByte *pabyPtr = raw;
   13614           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   13615           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   13616          12 :         for (size_t i = 0; i < nEltCount; ++i)
   13617             :         {
   13618           6 :             dt.FreeDynamicMemory(pabyPtr);
   13619           6 :             pabyPtr += nDTSize;
   13620             :         }
   13621           6 :         CPLFree(raw);
   13622             :     }
   13623             : }
   13624             : 
   13625             : /************************************************************************/
   13626             : /*                       GDALAttributeReadAsString()                    */
   13627             : /************************************************************************/
   13628             : 
   13629             : /** Return the value of an attribute as a string.
   13630             :  *
   13631             :  * The returned string should not be freed, and its lifetime does not
   13632             :  * excess a next call to ReadAsString() on the same object, or the deletion
   13633             :  * of the object itself.
   13634             :  *
   13635             :  * This function will only return the first element if there are several.
   13636             :  *
   13637             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   13638             :  *
   13639             :  * @return a string, or nullptr.
   13640             :  */
   13641         107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   13642             : {
   13643         107 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13644         107 :     return hAttr->m_poImpl->ReadAsString();
   13645             : }
   13646             : 
   13647             : /************************************************************************/
   13648             : /*                      GDALAttributeReadAsInt()                        */
   13649             : /************************************************************************/
   13650             : 
   13651             : /** Return the value of an attribute as a integer.
   13652             :  *
   13653             :  * This function will only return the first element if there are several.
   13654             :  *
   13655             :  * It can fail if its value can not be converted to integer.
   13656             :  *
   13657             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   13658             :  *
   13659             :  * @return a integer, or INT_MIN in case of error.
   13660             :  */
   13661          22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   13662             : {
   13663          22 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13664          22 :     return hAttr->m_poImpl->ReadAsInt();
   13665             : }
   13666             : 
   13667             : /************************************************************************/
   13668             : /*                      GDALAttributeReadAsInt64()                      */
   13669             : /************************************************************************/
   13670             : 
   13671             : /** Return the value of an attribute as a int64_t.
   13672             :  *
   13673             :  * This function will only return the first element if there are several.
   13674             :  *
   13675             :  * It can fail if its value can not be converted to integer.
   13676             :  *
   13677             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   13678             :  *
   13679             :  * @return an int64_t, or INT64_MIN in case of error.
   13680             :  */
   13681          15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   13682             : {
   13683          15 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13684          15 :     return hAttr->m_poImpl->ReadAsInt64();
   13685             : }
   13686             : 
   13687             : /************************************************************************/
   13688             : /*                       GDALAttributeReadAsDouble()                    */
   13689             : /************************************************************************/
   13690             : 
   13691             : /** Return the value of an attribute as a double.
   13692             :  *
   13693             :  * This function will only return the first element if there are several.
   13694             :  *
   13695             :  * It can fail if its value can not be converted to double.
   13696             :  *
   13697             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13698             :  *
   13699             :  * @return a double value.
   13700             :  */
   13701          40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   13702             : {
   13703          40 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13704          40 :     return hAttr->m_poImpl->ReadAsDouble();
   13705             : }
   13706             : 
   13707             : /************************************************************************/
   13708             : /*                     GDALAttributeReadAsStringArray()                 */
   13709             : /************************************************************************/
   13710             : 
   13711             : /** Return the value of an attribute as an array of strings.
   13712             :  *
   13713             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   13714             :  *
   13715             :  * The return value must be freed with CSLDestroy().
   13716             :  */
   13717          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   13718             : {
   13719          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13720          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   13721             : }
   13722             : 
   13723             : /************************************************************************/
   13724             : /*                     GDALAttributeReadAsIntArray()                    */
   13725             : /************************************************************************/
   13726             : 
   13727             : /** Return the value of an attribute as an array of integers.
   13728             :  *
   13729             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   13730             :  *
   13731             :  * @param hAttr Attribute
   13732             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13733             :  * @return array to be freed with CPLFree(), or nullptr.
   13734             :  */
   13735          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   13736             : {
   13737          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13738          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13739          15 :     *pnCount = 0;
   13740          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   13741          15 :     if (tmp.empty())
   13742           0 :         return nullptr;
   13743          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   13744          15 :     if (!ret)
   13745           0 :         return nullptr;
   13746          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   13747          15 :     *pnCount = tmp.size();
   13748          15 :     return ret;
   13749             : }
   13750             : 
   13751             : /************************************************************************/
   13752             : /*                     GDALAttributeReadAsInt64Array()                  */
   13753             : /************************************************************************/
   13754             : 
   13755             : /** Return the value of an attribute as an array of int64_t.
   13756             :  *
   13757             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   13758             :  *
   13759             :  * @param hAttr Attribute
   13760             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13761             :  * @return array to be freed with CPLFree(), or nullptr.
   13762             :  */
   13763          14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   13764             : {
   13765          14 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13766          14 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13767          14 :     *pnCount = 0;
   13768          28 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   13769          14 :     if (tmp.empty())
   13770           0 :         return nullptr;
   13771             :     auto ret = static_cast<int64_t *>(
   13772          14 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   13773          14 :     if (!ret)
   13774           0 :         return nullptr;
   13775          14 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   13776          14 :     *pnCount = tmp.size();
   13777          14 :     return ret;
   13778             : }
   13779             : 
   13780             : /************************************************************************/
   13781             : /*                     GDALAttributeReadAsDoubleArray()                 */
   13782             : /************************************************************************/
   13783             : 
   13784             : /** Return the value of an attribute as an array of doubles.
   13785             :  *
   13786             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   13787             :  *
   13788             :  * @param hAttr Attribute
   13789             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13790             :  * @return array to be freed with CPLFree(), or nullptr.
   13791             :  */
   13792          29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   13793             : {
   13794          29 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13795          29 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13796          29 :     *pnCount = 0;
   13797          58 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   13798          29 :     if (tmp.empty())
   13799           0 :         return nullptr;
   13800             :     auto ret =
   13801          29 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   13802          29 :     if (!ret)
   13803           0 :         return nullptr;
   13804          29 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   13805          29 :     *pnCount = tmp.size();
   13806          29 :     return ret;
   13807             : }
   13808             : 
   13809             : /************************************************************************/
   13810             : /*                     GDALAttributeWriteRaw()                          */
   13811             : /************************************************************************/
   13812             : 
   13813             : /** Write an attribute from raw values expressed in GetDataType()
   13814             :  *
   13815             :  * The values should be provided in the type of GetDataType() and there should
   13816             :  * be exactly GetTotalElementsCount() of them.
   13817             :  * If GetDataType() is a string, each value should be a char* pointer.
   13818             :  *
   13819             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   13820             :  *
   13821             :  * @param hAttr Attribute
   13822             :  * @param pabyValue Buffer of nLen bytes.
   13823             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   13824             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   13825             :  * @return TRUE in case of success.
   13826             :  */
   13827           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   13828             :                           size_t nLength)
   13829             : {
   13830           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13831           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   13832             : }
   13833             : 
   13834             : /************************************************************************/
   13835             : /*                     GDALAttributeWriteString()                       */
   13836             : /************************************************************************/
   13837             : 
   13838             : /** Write an attribute from a string value.
   13839             :  *
   13840             :  * Type conversion will be performed if needed. If the attribute contains
   13841             :  * multiple values, only the first one will be updated.
   13842             :  *
   13843             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   13844             :  *
   13845             :  * @param hAttr Attribute
   13846             :  * @param pszVal Pointer to a string.
   13847             :  * @return TRUE in case of success.
   13848             :  */
   13849         177 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   13850             : {
   13851         177 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13852         177 :     return hAttr->m_poImpl->Write(pszVal);
   13853             : }
   13854             : 
   13855             : /************************************************************************/
   13856             : /*                        GDALAttributeWriteInt()                       */
   13857             : /************************************************************************/
   13858             : 
   13859             : /** Write an attribute from a integer value.
   13860             :  *
   13861             :  * Type conversion will be performed if needed. If the attribute contains
   13862             :  * multiple values, only the first one will be updated.
   13863             :  *
   13864             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   13865             :  *
   13866             :  * @param hAttr Attribute
   13867             :  * @param nVal Value.
   13868             :  * @return TRUE in case of success.
   13869             :  */
   13870          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   13871             : {
   13872          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13873          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   13874             : }
   13875             : 
   13876             : /************************************************************************/
   13877             : /*                        GDALAttributeWriteInt64()                     */
   13878             : /************************************************************************/
   13879             : 
   13880             : /** Write an attribute from an int64_t value.
   13881             :  *
   13882             :  * Type conversion will be performed if needed. If the attribute contains
   13883             :  * multiple values, only the first one will be updated.
   13884             :  *
   13885             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   13886             :  *
   13887             :  * @param hAttr Attribute
   13888             :  * @param nVal Value.
   13889             :  * @return TRUE in case of success.
   13890             :  */
   13891          11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   13892             : {
   13893          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13894          11 :     return hAttr->m_poImpl->WriteInt64(nVal);
   13895             : }
   13896             : 
   13897             : /************************************************************************/
   13898             : /*                        GDALAttributeWriteDouble()                    */
   13899             : /************************************************************************/
   13900             : 
   13901             : /** Write an attribute from a double value.
   13902             :  *
   13903             :  * Type conversion will be performed if needed. If the attribute contains
   13904             :  * multiple values, only the first one will be updated.
   13905             :  *
   13906             :  * This is the same as the C++ method GDALAttribute::Write(double);
   13907             :  *
   13908             :  * @param hAttr Attribute
   13909             :  * @param dfVal Value.
   13910             :  *
   13911             :  * @return TRUE in case of success.
   13912             :  */
   13913          11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   13914             : {
   13915          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13916          11 :     return hAttr->m_poImpl->Write(dfVal);
   13917             : }
   13918             : 
   13919             : /************************************************************************/
   13920             : /*                       GDALAttributeWriteStringArray()                */
   13921             : /************************************************************************/
   13922             : 
   13923             : /** Write an attribute from an array of strings.
   13924             :  *
   13925             :  * Type conversion will be performed if needed.
   13926             :  *
   13927             :  * Exactly GetTotalElementsCount() strings must be provided
   13928             :  *
   13929             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   13930             :  *
   13931             :  * @param hAttr Attribute
   13932             :  * @param papszValues Array of strings.
   13933             :  * @return TRUE in case of success.
   13934             :  */
   13935           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   13936             :                                   CSLConstList papszValues)
   13937             : {
   13938           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13939           8 :     return hAttr->m_poImpl->Write(papszValues);
   13940             : }
   13941             : 
   13942             : /************************************************************************/
   13943             : /*                       GDALAttributeWriteIntArray()                */
   13944             : /************************************************************************/
   13945             : 
   13946             : /** Write an attribute from an array of int.
   13947             :  *
   13948             :  * Type conversion will be performed if needed.
   13949             :  *
   13950             :  * Exactly GetTotalElementsCount() strings must be provided
   13951             :  *
   13952             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   13953             :  * size_t)
   13954             :  *
   13955             :  * @param hAttr Attribute
   13956             :  * @param panValues Array of int.
   13957             :  * @param nCount Should be equal to GetTotalElementsCount().
   13958             :  * @return TRUE in case of success.
   13959             :  */
   13960          11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   13961             :                                size_t nCount)
   13962             : {
   13963          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13964          11 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13965             : }
   13966             : 
   13967             : /************************************************************************/
   13968             : /*                       GDALAttributeWriteInt64Array()                 */
   13969             : /************************************************************************/
   13970             : 
   13971             : /** Write an attribute from an array of int64_t.
   13972             :  *
   13973             :  * Type conversion will be performed if needed.
   13974             :  *
   13975             :  * Exactly GetTotalElementsCount() strings must be provided
   13976             :  *
   13977             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   13978             :  * size_t)
   13979             :  *
   13980             :  * @param hAttr Attribute
   13981             :  * @param panValues Array of int64_t.
   13982             :  * @param nCount Should be equal to GetTotalElementsCount().
   13983             :  * @return TRUE in case of success.
   13984             :  */
   13985          10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   13986             :                                  size_t nCount)
   13987             : {
   13988          10 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13989          10 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13990             : }
   13991             : 
   13992             : /************************************************************************/
   13993             : /*                       GDALAttributeWriteDoubleArray()                */
   13994             : /************************************************************************/
   13995             : 
   13996             : /** Write an attribute from an array of double.
   13997             :  *
   13998             :  * Type conversion will be performed if needed.
   13999             :  *
   14000             :  * Exactly GetTotalElementsCount() strings must be provided
   14001             :  *
   14002             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   14003             :  * size_t)
   14004             :  *
   14005             :  * @param hAttr Attribute
   14006             :  * @param padfValues Array of double.
   14007             :  * @param nCount Should be equal to GetTotalElementsCount().
   14008             :  * @return TRUE in case of success.
   14009             :  */
   14010           7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   14011             :                                   const double *padfValues, size_t nCount)
   14012             : {
   14013           7 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14014           7 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   14015             : }
   14016             : 
   14017             : /************************************************************************/
   14018             : /*                      GDALAttributeRename()                           */
   14019             : /************************************************************************/
   14020             : 
   14021             : /** Rename the attribute.
   14022             :  *
   14023             :  * This is not implemented by all drivers.
   14024             :  *
   14025             :  * Drivers known to implement it: MEM, netCDF.
   14026             :  *
   14027             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   14028             :  *
   14029             :  * @return true in case of success
   14030             :  * @since GDAL 3.8
   14031             :  */
   14032          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   14033             : {
   14034          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   14035          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14036          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   14037             : }
   14038             : 
   14039             : /************************************************************************/
   14040             : /*                        GDALDimensionRelease()                        */
   14041             : /************************************************************************/
   14042             : 
   14043             : /** Release the GDAL in-memory object associated with a GDALDimension.
   14044             :  *
   14045             :  * Note: when applied on a object coming from a driver, this does not
   14046             :  * destroy the object in the file, database, etc...
   14047             :  */
   14048        4910 : void GDALDimensionRelease(GDALDimensionH hDim)
   14049             : {
   14050        4910 :     delete hDim;
   14051        4910 : }
   14052             : 
   14053             : /************************************************************************/
   14054             : /*                        GDALDimensionGetName()                        */
   14055             : /************************************************************************/
   14056             : 
   14057             : /** Return dimension name.
   14058             :  *
   14059             :  * This is the same as the C++ method GDALDimension::GetName()
   14060             :  */
   14061         284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   14062             : {
   14063         284 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14064         284 :     return hDim->m_poImpl->GetName().c_str();
   14065             : }
   14066             : 
   14067             : /************************************************************************/
   14068             : /*                      GDALDimensionGetFullName()                      */
   14069             : /************************************************************************/
   14070             : 
   14071             : /** Return dimension full name.
   14072             :  *
   14073             :  * This is the same as the C++ method GDALDimension::GetFullName()
   14074             :  */
   14075          80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   14076             : {
   14077          80 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14078          80 :     return hDim->m_poImpl->GetFullName().c_str();
   14079             : }
   14080             : 
   14081             : /************************************************************************/
   14082             : /*                        GDALDimensionGetType()                        */
   14083             : /************************************************************************/
   14084             : 
   14085             : /** Return dimension type.
   14086             :  *
   14087             :  * This is the same as the C++ method GDALDimension::GetType()
   14088             :  */
   14089          62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   14090             : {
   14091          62 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14092          62 :     return hDim->m_poImpl->GetType().c_str();
   14093             : }
   14094             : 
   14095             : /************************************************************************/
   14096             : /*                     GDALDimensionGetDirection()                      */
   14097             : /************************************************************************/
   14098             : 
   14099             : /** Return dimension direction.
   14100             :  *
   14101             :  * This is the same as the C++ method GDALDimension::GetDirection()
   14102             :  */
   14103          32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   14104             : {
   14105          32 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14106          32 :     return hDim->m_poImpl->GetDirection().c_str();
   14107             : }
   14108             : 
   14109             : /************************************************************************/
   14110             : /*                        GDALDimensionGetSize()                        */
   14111             : /************************************************************************/
   14112             : 
   14113             : /** Return the size, that is the number of values along the dimension.
   14114             :  *
   14115             :  * This is the same as the C++ method GDALDimension::GetSize()
   14116             :  */
   14117        3685 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   14118             : {
   14119        3685 :     VALIDATE_POINTER1(hDim, __func__, 0);
   14120        3685 :     return hDim->m_poImpl->GetSize();
   14121             : }
   14122             : 
   14123             : /************************************************************************/
   14124             : /*                     GDALDimensionGetIndexingVariable()               */
   14125             : /************************************************************************/
   14126             : 
   14127             : /** Return the variable that is used to index the dimension (if there is one).
   14128             :  *
   14129             :  * This is the array, typically one-dimensional, describing the values taken
   14130             :  * by the dimension.
   14131             :  *
   14132             :  * The returned value should be freed with GDALMDArrayRelease().
   14133             :  *
   14134             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   14135             :  */
   14136         118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   14137             : {
   14138         118 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14139         236 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   14140         118 :     if (!var)
   14141          10 :         return nullptr;
   14142         108 :     return new GDALMDArrayHS(var);
   14143             : }
   14144             : 
   14145             : /************************************************************************/
   14146             : /*                      GDALDimensionSetIndexingVariable()              */
   14147             : /************************************************************************/
   14148             : 
   14149             : /** Set the variable that is used to index the dimension.
   14150             :  *
   14151             :  * This is the array, typically one-dimensional, describing the values taken
   14152             :  * by the dimension.
   14153             :  *
   14154             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   14155             :  *
   14156             :  * @return TRUE in case of success.
   14157             :  */
   14158          23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   14159             : {
   14160          23 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   14161          69 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   14162          46 :                                                       : nullptr);
   14163             : }
   14164             : 
   14165             : /************************************************************************/
   14166             : /*                      GDALDimensionRename()                           */
   14167             : /************************************************************************/
   14168             : 
   14169             : /** Rename the dimension.
   14170             :  *
   14171             :  * This is not implemented by all drivers.
   14172             :  *
   14173             :  * Drivers known to implement it: MEM, netCDF.
   14174             :  *
   14175             :  * This is the same as the C++ method GDALDimension::Rename()
   14176             :  *
   14177             :  * @return true in case of success
   14178             :  * @since GDAL 3.8
   14179             :  */
   14180          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   14181             : {
   14182          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   14183          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14184          31 :     return hDim->m_poImpl->Rename(pszNewName);
   14185             : }
   14186             : 
   14187             : /************************************************************************/
   14188             : /*                       GDALDatasetGetRootGroup()                      */
   14189             : /************************************************************************/
   14190             : 
   14191             : /** Return the root GDALGroup of this dataset.
   14192             :  *
   14193             :  * Only valid for multidimensional datasets.
   14194             :  *
   14195             :  * The returned value must be freed with GDALGroupRelease().
   14196             :  *
   14197             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   14198             :  *
   14199             :  * @since GDAL 3.1
   14200             :  */
   14201        1176 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   14202             : {
   14203        1176 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   14204        1176 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   14205        1176 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   14206             : }
   14207             : 
   14208             : /************************************************************************/
   14209             : /*                      GDALRasterBandAsMDArray()                        */
   14210             : /************************************************************************/
   14211             : 
   14212             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   14213             :  *
   14214             :  * The band must be linked to a GDALDataset. If this dataset is not already
   14215             :  * marked as shared, it will be, so that the returned array holds a reference
   14216             :  * to it.
   14217             :  *
   14218             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   14219             :  * returned array will have an associated indexing variable.
   14220             :  *
   14221             :  * The returned pointer must be released with GDALMDArrayRelease().
   14222             :  *
   14223             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   14224             :  *
   14225             :  * @return a new array, or NULL.
   14226             :  *
   14227             :  * @since GDAL 3.1
   14228             :  */
   14229          21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   14230             : {
   14231          21 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   14232          42 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   14233          21 :     if (!poArray)
   14234           0 :         return nullptr;
   14235          21 :     return new GDALMDArrayHS(poArray);
   14236             : }
   14237             : 
   14238             : /************************************************************************/
   14239             : /*                       GDALMDArrayAsClassicDataset()                  */
   14240             : /************************************************************************/
   14241             : 
   14242             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14243             :  *
   14244             :  * Only 2D or more arrays are supported.
   14245             :  *
   14246             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14247             :  * raster bands.
   14248             :  *
   14249             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   14250             :  *
   14251             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14252             :  *
   14253             :  * @param hArray Array.
   14254             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14255             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14256             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14257             :  */
   14258           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   14259             :                                          size_t iYDim)
   14260             : {
   14261           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14262           0 :     return GDALDataset::ToHandle(
   14263           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   14264             : }
   14265             : 
   14266             : /************************************************************************/
   14267             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   14268             : /************************************************************************/
   14269             : 
   14270             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14271             :  *
   14272             :  * Only 2D or more arrays are supported.
   14273             :  *
   14274             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14275             :  * raster bands.
   14276             :  *
   14277             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   14278             :  *
   14279             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14280             :  * @param hArray Array.
   14281             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14282             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14283             :  *              Ignored if the dimension count is 1.
   14284             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
   14285             :  *                   BAND_IMAGERY_METADATA option.
   14286             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   14287             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14288             :  * @since GDAL 3.8
   14289             :  */
   14290          90 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   14291             :                                            size_t iYDim, GDALGroupH hRootGroup,
   14292             :                                            CSLConstList papszOptions)
   14293             : {
   14294          90 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14295         180 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   14296         180 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   14297         180 :         papszOptions));
   14298             : }
   14299             : 
   14300             : //! @cond Doxygen_Suppress
   14301             : 
   14302         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   14303             :                                          const std::string &osName,
   14304             :                                          const std::string &osValue,
   14305         180 :                                          GDALExtendedDataTypeSubType eSubType)
   14306             :     : GDALAbstractMDArray(osParentName, osName),
   14307             :       GDALAttribute(osParentName, osName),
   14308         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   14309             : {
   14310         180 : }
   14311             : 
   14312             : const std::vector<std::shared_ptr<GDALDimension>> &
   14313          30 : GDALAttributeString::GetDimensions() const
   14314             : {
   14315          30 :     return m_dims;
   14316             : }
   14317             : 
   14318          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   14319             : {
   14320          21 :     return m_dt;
   14321             : }
   14322             : 
   14323          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   14324             :                                 const GPtrDiff_t *,
   14325             :                                 const GDALExtendedDataType &bufferDataType,
   14326             :                                 void *pDstBuffer) const
   14327             : {
   14328          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   14329           0 :         return false;
   14330          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   14331          10 :     if (!pszStr)
   14332           0 :         return false;
   14333          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   14334          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   14335          10 :     return true;
   14336             : }
   14337             : 
   14338          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14339             :                                            const std::string &osName,
   14340          66 :                                            double dfValue)
   14341             :     : GDALAbstractMDArray(osParentName, osName),
   14342             :       GDALAttribute(osParentName, osName),
   14343          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   14344             : {
   14345          66 : }
   14346             : 
   14347          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14348             :                                            const std::string &osName,
   14349          27 :                                            int nValue)
   14350             :     : GDALAbstractMDArray(osParentName, osName),
   14351             :       GDALAttribute(osParentName, osName),
   14352          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   14353             : {
   14354          27 : }
   14355             : 
   14356           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14357             :                                            const std::string &osName,
   14358           7 :                                            const std::vector<GUInt32> &anValues)
   14359             :     : GDALAbstractMDArray(osParentName, osName),
   14360             :       GDALAttribute(osParentName, osName),
   14361           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   14362             : {
   14363           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   14364          14 :         std::string(), "dim0", std::string(), std::string(),
   14365           7 :         m_anValuesUInt32.size()));
   14366           7 : }
   14367             : 
   14368             : const std::vector<std::shared_ptr<GDALDimension>> &
   14369          14 : GDALAttributeNumeric::GetDimensions() const
   14370             : {
   14371          14 :     return m_dims;
   14372             : }
   14373             : 
   14374           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   14375             : {
   14376           8 :     return m_dt;
   14377             : }
   14378             : 
   14379           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   14380             :                                  const size_t *count, const GInt64 *arrayStep,
   14381             :                                  const GPtrDiff_t *bufferStride,
   14382             :                                  const GDALExtendedDataType &bufferDataType,
   14383             :                                  void *pDstBuffer) const
   14384             : {
   14385           4 :     if (m_dims.empty())
   14386             :     {
   14387           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   14388           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   14389             :                                             bufferDataType);
   14390             :         else
   14391             :         {
   14392           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   14393           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   14394             :                                             bufferDataType);
   14395             :         }
   14396             :     }
   14397             :     else
   14398             :     {
   14399           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   14400           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14401          30 :         for (size_t i = 0; i < count[0]; ++i)
   14402             :         {
   14403          29 :             GDALExtendedDataType::CopyValue(
   14404          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   14405          29 :                                                       i * arrayStep[0])],
   14406          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   14407          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   14408             :         }
   14409             :     }
   14410           4 :     return true;
   14411             : }
   14412             : 
   14413         194 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   14414             :     const std::string &osParentName, const std::string &osName,
   14415             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14416         194 :     double dfIncrement, double dfOffsetInIncrement)
   14417             :     : GDALAbstractMDArray(osParentName, osName),
   14418             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   14419             :       m_dfIncrement(dfIncrement),
   14420         388 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   14421             : {
   14422         194 : }
   14423             : 
   14424         194 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   14425             :     const std::string &osParentName, const std::string &osName,
   14426             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14427             :     double dfIncrement, double dfOffsetInIncrement)
   14428             : {
   14429             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   14430         194 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   14431         194 :     poArray->SetSelf(poArray);
   14432         194 :     return poArray;
   14433             : }
   14434             : 
   14435             : const std::vector<std::shared_ptr<GDALDimension>> &
   14436         788 : GDALMDArrayRegularlySpaced::GetDimensions() const
   14437             : {
   14438         788 :     return m_dims;
   14439             : }
   14440             : 
   14441         316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   14442             : {
   14443         316 :     return m_dt;
   14444             : }
   14445             : 
   14446             : std::vector<std::shared_ptr<GDALAttribute>>
   14447           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   14448             : {
   14449           4 :     return m_attributes;
   14450             : }
   14451             : 
   14452           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   14453             :     const std::shared_ptr<GDALAttribute> &poAttr)
   14454             : {
   14455           0 :     m_attributes.emplace_back(poAttr);
   14456           0 : }
   14457             : 
   14458         188 : bool GDALMDArrayRegularlySpaced::IRead(
   14459             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   14460             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   14461             :     void *pDstBuffer) const
   14462             : {
   14463         188 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14464       14719 :     for (size_t i = 0; i < count[0]; i++)
   14465             :     {
   14466       14531 :         const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
   14467       14531 :                                           m_dfOffsetInIncrement) *
   14468       14531 :                                              m_dfIncrement;
   14469       14531 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   14470             :                                         bufferDataType);
   14471       14531 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   14472             :     }
   14473         188 :     return true;
   14474             : }
   14475             : 
   14476        3025 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   14477             :     const std::string &osParentName, const std::string &osName,
   14478        3025 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   14479        3025 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   14480             : {
   14481        3025 : }
   14482             : 
   14483             : std::shared_ptr<GDALMDArray>
   14484         731 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   14485             : {
   14486         731 :     return m_poIndexingVariable.lock();
   14487             : }
   14488             : 
   14489             : // cppcheck-suppress passedByValue
   14490         500 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   14491             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   14492             : {
   14493         500 :     m_poIndexingVariable = poIndexingVariable;
   14494         500 :     return true;
   14495             : }
   14496             : 
   14497          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   14498             : {
   14499          33 :     m_nSize = nNewSize;
   14500          33 : }
   14501             : 
   14502             : /************************************************************************/
   14503             : /*                       GDALPamMultiDim::Private                       */
   14504             : /************************************************************************/
   14505             : 
   14506             : struct GDALPamMultiDim::Private
   14507             : {
   14508             :     std::string m_osFilename{};
   14509             :     std::string m_osPamFilename{};
   14510             : 
   14511             :     struct Statistics
   14512             :     {
   14513             :         bool bHasStats = false;
   14514             :         bool bApproxStats = false;
   14515             :         double dfMin = 0;
   14516             :         double dfMax = 0;
   14517             :         double dfMean = 0;
   14518             :         double dfStdDev = 0;
   14519             :         GUInt64 nValidCount = 0;
   14520             :     };
   14521             : 
   14522             :     struct ArrayInfo
   14523             :     {
   14524             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   14525             :         // cppcheck-suppress unusedStructMember
   14526             :         Statistics stats{};
   14527             :     };
   14528             : 
   14529             :     typedef std::pair<std::string, std::string> NameContext;
   14530             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   14531             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   14532             :     bool m_bDirty = false;
   14533             :     bool m_bLoaded = false;
   14534             : };
   14535             : 
   14536             : /************************************************************************/
   14537             : /*                          GDALPamMultiDim                             */
   14538             : /************************************************************************/
   14539             : 
   14540        1449 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   14541        1449 :     : d(new Private())
   14542             : {
   14543        1449 :     d->m_osFilename = osFilename;
   14544        1449 : }
   14545             : 
   14546             : /************************************************************************/
   14547             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   14548             : /************************************************************************/
   14549             : 
   14550        1449 : GDALPamMultiDim::~GDALPamMultiDim()
   14551             : {
   14552        1449 :     if (d->m_bDirty)
   14553          30 :         Save();
   14554        1449 : }
   14555             : 
   14556             : /************************************************************************/
   14557             : /*                          GDALPamMultiDim::Load()                     */
   14558             : /************************************************************************/
   14559             : 
   14560         107 : void GDALPamMultiDim::Load()
   14561             : {
   14562         107 :     if (d->m_bLoaded)
   14563          96 :         return;
   14564          45 :     d->m_bLoaded = true;
   14565             : 
   14566          45 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   14567          45 :     d->m_osPamFilename =
   14568          90 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   14569          45 :     CPLXMLTreeCloser oTree(nullptr);
   14570             :     {
   14571          90 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14572          45 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   14573             :     }
   14574          45 :     if (!oTree)
   14575             :     {
   14576          34 :         return;
   14577             :     }
   14578          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   14579          11 :     if (!poPAMMultiDim)
   14580           0 :         return;
   14581          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   14582          24 :          psIter = psIter->psNext)
   14583             :     {
   14584          24 :         if (psIter->eType == CXT_Element &&
   14585          24 :             strcmp(psIter->pszValue, "Array") == 0)
   14586             :         {
   14587          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   14588          13 :             if (!pszName)
   14589           0 :                 continue;
   14590          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   14591             :             const auto oKey =
   14592          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   14593             : 
   14594             :             /* --------------------------------------------------------------------
   14595             :              */
   14596             :             /*      Check for an SRS node. */
   14597             :             /* --------------------------------------------------------------------
   14598             :              */
   14599          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   14600          13 :             if (psSRSNode)
   14601             :             {
   14602             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   14603           6 :                     std::make_shared<OGRSpatialReference>();
   14604           3 :                 poSRS->SetFromUserInput(
   14605             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   14606             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   14607           3 :                 const char *pszMapping = CPLGetXMLValue(
   14608             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   14609           3 :                 if (pszMapping)
   14610             :                 {
   14611             :                     char **papszTokens =
   14612           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   14613           6 :                     std::vector<int> anMapping;
   14614           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   14615             :                     {
   14616           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   14617             :                     }
   14618           3 :                     CSLDestroy(papszTokens);
   14619           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   14620             :                 }
   14621             :                 else
   14622             :                 {
   14623           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   14624             :                 }
   14625             : 
   14626             :                 const char *pszCoordinateEpoch =
   14627           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   14628           3 :                 if (pszCoordinateEpoch)
   14629           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   14630             : 
   14631           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   14632             :             }
   14633             : 
   14634             :             const CPLXMLNode *psStatistics =
   14635          13 :                 CPLGetXMLNode(psIter, "Statistics");
   14636          13 :             if (psStatistics)
   14637             :             {
   14638           7 :                 Private::Statistics sStats;
   14639           7 :                 sStats.bHasStats = true;
   14640           7 :                 sStats.bApproxStats = CPLTestBool(
   14641             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   14642           7 :                 sStats.dfMin =
   14643           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   14644           7 :                 sStats.dfMax =
   14645           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   14646           7 :                 sStats.dfMean =
   14647           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   14648           7 :                 sStats.dfStdDev =
   14649           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   14650           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   14651             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   14652           7 :                 d->m_oMapArray[oKey].stats = sStats;
   14653          13 :             }
   14654             :         }
   14655             :         else
   14656             :         {
   14657          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   14658          11 :             psIter->psNext = nullptr;
   14659          11 :             d->m_apoOtherNodes.emplace_back(
   14660          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   14661          11 :             psIter->psNext = psNextBackup;
   14662             :         }
   14663             :     }
   14664             : }
   14665             : 
   14666             : /************************************************************************/
   14667             : /*                          GDALPamMultiDim::Save()                     */
   14668             : /************************************************************************/
   14669             : 
   14670          30 : void GDALPamMultiDim::Save()
   14671             : {
   14672             :     CPLXMLTreeCloser oTree(
   14673          60 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   14674          34 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   14675             :     {
   14676           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   14677             :     }
   14678         112 :     for (const auto &kv : d->m_oMapArray)
   14679             :     {
   14680             :         CPLXMLNode *psArrayNode =
   14681          82 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   14682          82 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   14683          82 :         if (!kv.first.second.empty())
   14684             :         {
   14685           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   14686             :                                        kv.first.second.c_str());
   14687             :         }
   14688          82 :         if (kv.second.poSRS)
   14689             :         {
   14690          71 :             char *pszWKT = nullptr;
   14691             :             {
   14692         142 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14693          71 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   14694          71 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   14695             :             }
   14696             :             CPLXMLNode *psSRSNode =
   14697          71 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   14698          71 :             CPLFree(pszWKT);
   14699             :             const auto &mapping =
   14700          71 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   14701         142 :             CPLString osMapping;
   14702         213 :             for (size_t i = 0; i < mapping.size(); ++i)
   14703             :             {
   14704         142 :                 if (!osMapping.empty())
   14705          71 :                     osMapping += ",";
   14706         142 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   14707             :             }
   14708          71 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   14709             :                                        osMapping.c_str());
   14710             : 
   14711             :             const double dfCoordinateEpoch =
   14712          71 :                 kv.second.poSRS->GetCoordinateEpoch();
   14713          71 :             if (dfCoordinateEpoch > 0)
   14714             :             {
   14715             :                 std::string osCoordinateEpoch =
   14716           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   14717           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   14718             :                 {
   14719           6 :                     while (osCoordinateEpoch.back() == '0')
   14720           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   14721             :                 }
   14722           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   14723             :                                            osCoordinateEpoch.c_str());
   14724             :             }
   14725             :         }
   14726             : 
   14727          82 :         if (kv.second.stats.bHasStats)
   14728             :         {
   14729             :             CPLXMLNode *psMDArray =
   14730           8 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   14731           8 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   14732           8 :                                         kv.second.stats.bApproxStats ? "1"
   14733             :                                                                      : "0");
   14734           8 :             CPLCreateXMLElementAndValue(
   14735             :                 psMDArray, "Minimum",
   14736           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   14737           8 :             CPLCreateXMLElementAndValue(
   14738             :                 psMDArray, "Maximum",
   14739           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   14740           8 :             CPLCreateXMLElementAndValue(
   14741           8 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   14742           8 :             CPLCreateXMLElementAndValue(
   14743             :                 psMDArray, "StdDev",
   14744           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   14745           8 :             CPLCreateXMLElementAndValue(
   14746             :                 psMDArray, "ValidSampleCount",
   14747           8 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   14748             :         }
   14749             :     }
   14750             : 
   14751             :     int bSaved;
   14752          60 :     CPLErrorAccumulator oErrorAccumulator;
   14753             :     {
   14754          30 :         auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
   14755          30 :         CPL_IGNORE_RET_VAL(oAccumulator);
   14756             :         bSaved =
   14757          30 :             CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   14758             :     }
   14759             : 
   14760          30 :     const char *pszNewPam = nullptr;
   14761          30 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   14762           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   14763             :     {
   14764           0 :         CPLErrorReset();
   14765           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   14766             :     }
   14767             :     else
   14768             :     {
   14769          30 :         oErrorAccumulator.ReplayErrors();
   14770             :     }
   14771          30 : }
   14772             : 
   14773             : /************************************************************************/
   14774             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   14775             : /************************************************************************/
   14776             : 
   14777             : std::shared_ptr<OGRSpatialReference>
   14778          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   14779             :                                const std::string &osContext)
   14780             : {
   14781          10 :     Load();
   14782             :     auto oIter =
   14783          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14784          10 :     if (oIter != d->m_oMapArray.end())
   14785           2 :         return oIter->second.poSRS;
   14786           8 :     return nullptr;
   14787             : }
   14788             : 
   14789             : /************************************************************************/
   14790             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   14791             : /************************************************************************/
   14792             : 
   14793          72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   14794             :                                     const std::string &osContext,
   14795             :                                     const OGRSpatialReference *poSRS)
   14796             : {
   14797          72 :     Load();
   14798          72 :     d->m_bDirty = true;
   14799          72 :     if (poSRS && !poSRS->IsEmpty())
   14800          71 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   14801             :             poSRS->Clone());
   14802             :     else
   14803           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   14804           1 :             .poSRS.reset();
   14805          72 : }
   14806             : 
   14807             : /************************************************************************/
   14808             : /*                           GetStatistics()                            */
   14809             : /************************************************************************/
   14810             : 
   14811          16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   14812             :                                       const std::string &osContext,
   14813             :                                       bool bApproxOK, double *pdfMin,
   14814             :                                       double *pdfMax, double *pdfMean,
   14815             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   14816             : {
   14817          16 :     Load();
   14818             :     auto oIter =
   14819          16 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14820          16 :     if (oIter == d->m_oMapArray.end())
   14821           9 :         return CE_Failure;
   14822           7 :     const auto &stats = oIter->second.stats;
   14823           7 :     if (!stats.bHasStats)
   14824           1 :         return CE_Failure;
   14825           6 :     if (!bApproxOK && stats.bApproxStats)
   14826           0 :         return CE_Failure;
   14827           6 :     if (pdfMin)
   14828           6 :         *pdfMin = stats.dfMin;
   14829           6 :     if (pdfMax)
   14830           6 :         *pdfMax = stats.dfMax;
   14831           6 :     if (pdfMean)
   14832           6 :         *pdfMean = stats.dfMean;
   14833           6 :     if (pdfStdDev)
   14834           6 :         *pdfStdDev = stats.dfStdDev;
   14835           6 :     if (pnValidCount)
   14836           6 :         *pnValidCount = stats.nValidCount;
   14837           6 :     return CE_None;
   14838             : }
   14839             : 
   14840             : /************************************************************************/
   14841             : /*                           SetStatistics()                            */
   14842             : /************************************************************************/
   14843             : 
   14844           8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   14845             :                                     const std::string &osContext,
   14846             :                                     bool bApproxStats, double dfMin,
   14847             :                                     double dfMax, double dfMean,
   14848             :                                     double dfStdDev, GUInt64 nValidCount)
   14849             : {
   14850           8 :     Load();
   14851           8 :     d->m_bDirty = true;
   14852             :     auto &stats =
   14853           8 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   14854           8 :     stats.bHasStats = true;
   14855           8 :     stats.bApproxStats = bApproxStats;
   14856           8 :     stats.dfMin = dfMin;
   14857           8 :     stats.dfMax = dfMax;
   14858           8 :     stats.dfMean = dfMean;
   14859           8 :     stats.dfStdDev = dfStdDev;
   14860           8 :     stats.nValidCount = nValidCount;
   14861           8 : }
   14862             : 
   14863             : /************************************************************************/
   14864             : /*                           ClearStatistics()                          */
   14865             : /************************************************************************/
   14866             : 
   14867           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   14868             :                                       const std::string &osContext)
   14869             : {
   14870           0 :     Load();
   14871           0 :     d->m_bDirty = true;
   14872           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   14873             :         false;
   14874           0 : }
   14875             : 
   14876             : /************************************************************************/
   14877             : /*                           ClearStatistics()                          */
   14878             : /************************************************************************/
   14879             : 
   14880           1 : void GDALPamMultiDim::ClearStatistics()
   14881             : {
   14882           1 :     Load();
   14883           1 :     d->m_bDirty = true;
   14884           3 :     for (auto &kv : d->m_oMapArray)
   14885           2 :         kv.second.stats.bHasStats = false;
   14886           1 : }
   14887             : 
   14888             : /************************************************************************/
   14889             : /*                             GetPAM()                                 */
   14890             : /************************************************************************/
   14891             : 
   14892             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   14893         815 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   14894             : {
   14895         815 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   14896         815 :     if (poPamArray)
   14897         567 :         return poPamArray->GetPAM();
   14898         248 :     return nullptr;
   14899             : }
   14900             : 
   14901             : /************************************************************************/
   14902             : /*                           GDALPamMDArray                             */
   14903             : /************************************************************************/
   14904             : 
   14905        3755 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   14906             :                                const std::string &osName,
   14907             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   14908           0 :                                const std::string &osContext)
   14909             :     :
   14910             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   14911             :       GDALAbstractMDArray(osParentName, osName),
   14912             : #endif
   14913        3755 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   14914             : {
   14915        3755 : }
   14916             : 
   14917             : /************************************************************************/
   14918             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   14919             : /************************************************************************/
   14920             : 
   14921          72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   14922             : {
   14923          72 :     if (!m_poPam)
   14924           0 :         return false;
   14925          72 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   14926          72 :     return true;
   14927             : }
   14928             : 
   14929             : /************************************************************************/
   14930             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   14931             : /************************************************************************/
   14932             : 
   14933          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   14934             : {
   14935          10 :     if (!m_poPam)
   14936           0 :         return nullptr;
   14937          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   14938             : }
   14939             : 
   14940             : /************************************************************************/
   14941             : /*                           GetStatistics()                            */
   14942             : /************************************************************************/
   14943             : 
   14944          16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   14945             :                                      double *pdfMin, double *pdfMax,
   14946             :                                      double *pdfMean, double *pdfStdDev,
   14947             :                                      GUInt64 *pnValidCount,
   14948             :                                      GDALProgressFunc pfnProgress,
   14949             :                                      void *pProgressData)
   14950             : {
   14951          16 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   14952             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   14953          16 :                                           pdfStdDev, pnValidCount) == CE_None)
   14954             :     {
   14955           6 :         return CE_None;
   14956             :     }
   14957          10 :     if (!bForce)
   14958           4 :         return CE_Warning;
   14959             : 
   14960           6 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   14961             :                                       pdfMean, pdfStdDev, pnValidCount,
   14962           6 :                                       pfnProgress, pProgressData);
   14963             : }
   14964             : 
   14965             : /************************************************************************/
   14966             : /*                           SetStatistics()                            */
   14967             : /************************************************************************/
   14968             : 
   14969           8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   14970             :                                    double dfMax, double dfMean, double dfStdDev,
   14971             :                                    GUInt64 nValidCount,
   14972             :                                    CSLConstList /* papszOptions */)
   14973             : {
   14974           8 :     if (!m_poPam)
   14975           0 :         return false;
   14976           8 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   14977             :                            dfMax, dfMean, dfStdDev, nValidCount);
   14978           8 :     return true;
   14979             : }
   14980             : 
   14981             : /************************************************************************/
   14982             : /*                           ClearStatistics()                          */
   14983             : /************************************************************************/
   14984             : 
   14985           0 : void GDALPamMDArray::ClearStatistics()
   14986             : {
   14987           0 :     if (!m_poPam)
   14988           0 :         return;
   14989           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   14990             : }
   14991             : 
   14992             : //! @endcond

Generated by: LCOV version 1.14